[osmium] 04/16: Imported Upstream version 0.0~20140410-d1d56bedb3

Bas Couwenberg sebastic at xs4all.nl
Mon Jun 9 01:03:07 UTC 2014


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

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

commit 552666424c10b92f6e611cb68e74c7055163f0e2
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Mon Jun 9 02:35:23 2014 +0200

    Imported Upstream version 0.0~20140410-d1d56bedb3
---
 Doxyfile                                           | 2586 +++++++++++++-------
 Makefile                                           |   62 +-
 NOTES_FOR_DEVELOPERS                               |   30 +-
 README                                             |   77 +-
 examples/.gitignore                                |    4 +-
 examples/Makefile                                  |  103 +-
 examples/README                                    |   20 +-
 examples/nodedensity.cpp                           |   39 +-
 examples/osmium_convert.cpp                        |   97 +-
 examples/osmium_debug.cpp                          |   34 +-
 examples/osmium_find_bbox.cpp                      |   44 +-
 examples/osmium_mpdump.cpp                         |   90 +
 examples/osmium_progress.cpp                       |   34 +-
 examples/osmium_range_from_history.cpp             |   40 +-
 examples/osmium_relation_members.cpp               |  109 +
 examples/osmium_sizeof.cpp                         |   45 +-
 examples/osmium_store_and_debug.cpp                |   42 +-
 examples/osmium_time.cpp                           |   65 +-
 examples/osmium_to_postgis.cpp                     |  183 ++
 examples/osmium_toogr.cpp                          |  163 +-
 examples/osmium_toogr2.cpp                         |  154 +-
 examples/osmium_toshape.cpp                        |   67 +-
 get_options.sh                                     |   20 +
 include/osmium.hpp                                 |  111 +-
 include/osmium/CGAlgorithms.h                      |  217 --
 include/osmium/debug.hpp                           |   73 +
 include/osmium/export.hpp                          |    7 +-
 include/osmium/export/csv.hpp                      |   42 +-
 include/osmium/export/shapefile.hpp                |  317 ++-
 include/osmium/geometry.hpp                        |  107 +-
 include/osmium/geometry/from_way.hpp               |   81 +-
 include/osmium/geometry/geos.hpp                   |  119 +
 include/osmium/geometry/haversine.hpp              |   83 +
 include/osmium/geometry/linestring.hpp             |  146 +-
 include/osmium/geometry/multipolygon.hpp           |  254 +-
 include/osmium/geometry/null.hpp                   |   15 +-
 include/osmium/geometry/ogr.hpp                    |   93 +
 include/osmium/geometry/ogr_multipolygon.hpp       |   97 +
 include/osmium/geometry/point.hpp                  |   96 +-
 include/osmium/geometry/polygon.hpp                |  123 +-
 include/osmium/geometry/shplib.hpp                 |  222 ++
 include/osmium/handler.hpp                         |  161 +-
 include/osmium/handler/coordinates_for_ways.hpp    |   20 +-
 include/osmium/handler/debug.hpp                   |   59 +-
 include/osmium/handler/endtime.hpp                 |   41 +-
 include/osmium/handler/find_bbox.hpp               |    8 +-
 include/osmium/handler/multipolygon.hpp            |  181 --
 include/osmium/handler/progress.hpp                |  138 +-
 include/osmium/handler/range_from_history.hpp      |   51 +-
 include/osmium/handler/statistics.hpp              |  240 --
 include/osmium/input.hpp                           |   59 +-
 include/osmium/input/pbf.hpp                       |   64 +-
 include/osmium/input/xml.hpp                       |  250 +-
 .../osmium/javascript.hpp                          |   30 +-
 .../handler.hpp}                                   |  171 +-
 include/osmium/javascript/template.hpp             |   48 +-
 include/osmium/{utils => javascript}/unicode.hpp   |   41 +-
 include/osmium/javascript/wrapper/export_csv.hpp   |   80 +
 .../osmium/javascript/wrapper/export_shapefile.hpp |  196 ++
 include/osmium/javascript/wrapper/geometry.hpp     |  224 ++
 include/osmium/javascript/wrapper/osm.hpp          |  323 +++
 include/osmium/javascript/wrapper/position.hpp     |   55 +
 include/osmium/multipolygon/assembler.hpp          |  128 +
 include/osmium/multipolygon/builder.hpp            |  980 ++++++++
 include/osmium/osm.hpp                             |    2 +-
 include/osmium/osm/area.hpp                        | 1140 +--------
 include/osmium/osm/bounds.hpp                      |   53 +-
 include/osmium/osm/meta.hpp                        |   24 +-
 include/osmium/osm/node.hpp                        |   79 +-
 include/osmium/osm/object.hpp                      |  136 +-
 include/osmium/osm/position.hpp                    |  160 +-
 include/osmium/osm/relation.hpp                    |   79 +-
 include/osmium/osm/relation_member.hpp             |   57 +-
 include/osmium/osm/relation_member_list.hpp        |   49 +-
 include/osmium/osm/segment.hpp                     |   75 +
 include/osmium/osm/tag.hpp                         |   15 +-
 include/osmium/osm/tag_list.hpp                    |   65 +-
 .../osmium/{exceptions.hpp => osm/tag_ostream.hpp} |   29 +-
 include/osmium/osm/types.hpp                       |   37 +-
 include/osmium/osm/undirected_segment.hpp          |   66 +
 include/osmium/osm/way.hpp                         |  170 +-
 include/osmium/osm/way_node.hpp                    |   38 +-
 include/osmium/osm/way_node_list.hpp               |   74 +-
 include/osmium/osmfile.hpp                         |  172 +-
 include/osmium/osmfile_impl.hpp                    |   55 -
 include/osmium/output.hpp                          |   92 +-
 include/osmium/output/pbf.hpp                      |  159 +-
 include/osmium/output/xml.hpp                      |  173 +-
 include/osmium/relations/assembler.hpp             |  527 ++++
 include/osmium/relations/relation_info.hpp         |  140 ++
 include/osmium/{export.hpp => smart_ptr.hpp}       |   32 +-
 include/osmium/storage/byid.hpp                    |  381 +--
 include/osmium/storage/byid/fixed_array.hpp        |  113 +
 include/osmium/storage/byid/mmap_anon.hpp          |  126 +
 include/osmium/storage/byid/mmap_file.hpp          |  172 ++
 include/osmium/storage/byid/sparse_table.hpp       |  104 +
 include/osmium/storage/byid/stl_map.hpp            |   82 +
 include/osmium/storage/byid/vector.hpp             |  119 +
 include/osmium/storage/objectstore.hpp             |   54 +-
 include/osmium/tags/key_filter.hpp                 |   82 +
 include/osmium/tags/key_value_filter.hpp           |   84 +
 include/osmium/tags/regex_filter.hpp               |   90 +
 include/osmium/tags/to_string.hpp                  |  116 +
 include/osmium/utils/delta.hpp                     |    7 +-
 include/osmium/utils/filter_and_accumulate.hpp     |   48 +
 include/osmium/utils/sqlite.hpp                    |  195 --
 include/osmium/utils/stringtable.hpp               |   52 +-
 include/osmium/utils/timer.h                       |  129 -
 include/osmium/utils/timestamp.hpp                 |   95 +-
 osmjs/Makefile                                     |   50 +-
 osmjs/README                                       |   20 +-
 osmjs/js/callback_test.js                          |   42 +
 osmjs/js/config.js                                 |    2 +-
 osmjs/js/osm2csv.js                                |   20 +
 osmjs/js/shape_export.js                           |    2 +-
 osmjs/js/testgeom.js                               |    2 +-
 osmjs/osmjs.cpp                                    |  301 +--
 test/.gitignore                                    |    9 +
 test/Makefile                                      |   82 +
 test/run_tests.sh                                  |  131 +-
 test/t/geometry/test_haversine.cpp                 |   23 +
 .../geometry/test_linestring_geometry.cpp          |    3 +-
 .../geometry/test_point_geometry.cpp               |    3 +-
 .../geometry/test_polygon_geometry.cpp             |    5 +-
 .../geometry_geos}/test_point_geometry.cpp         |    7 +-
 .../geometry => t/geometry_ogr}/test_geometry.cpp  |    8 +-
 test/t/handler/test_handler.cpp                    |  199 ++
 test/t/handler/test_handler_debug.cpp              |  268 ++
 test/{testgroup_plain => t}/osm/test_bounds.cpp    |   13 +-
 test/t/osm/test_node.cpp                           |  104 +
 test/t/osm/test_object.cpp                         |  253 ++
 test/{testgroup_plain => t}/osm/test_position.cpp  |   41 +-
 test/t/osm/test_relation.cpp                       |  100 +
 test/t/osm/test_relation_member.cpp                |   82 +
 test/t/osm/test_relation_member_list.cpp           |  100 +
 test/t/osm/test_tag_list.cpp                       |  121 +
 test/t/osm/test_tag_ostream.cpp                    |   19 +
 test/t/osm/test_way.cpp                            |  118 +
 test/{testgroup_plain => t}/osm/test_way_node.cpp  |   34 +-
 test/t/osm/test_way_node_list.cpp                  |  199 ++
 test/t/osmfile/test_filename.cpp                   |  203 ++
 test/t/osmfile/test_read_and_write.cpp             |  256 ++
 test/t/osmfile/test_url.cpp                        |   17 +
 test/t/tags/test_filter.cpp                        |  110 +
 test/t/tags/test_regex_filter.cpp                  |   38 +
 test/t/tags/test_tag.cpp                           |   25 +
 test/t/tags/test_to_string.cpp                     |   73 +
 .../utils/test_timestamp.cpp                       |    9 +-
 test/temp_file_fixture.hpp                         |   72 +
 test/test_main.cpp                                 |    1 -
 test/test_utils.cpp                                |    2 +-
 test/testgroup_geos/setup.sh                       |    1 -
 test/testgroup_ogr/setup.sh                        |    1 -
 test/testgroup_plain/osm/test_node.cpp             |   41 -
 test/testgroup_plain/osm/test_way_node_list.cpp    |   38 -
 test/testgroup_plain/osmfile/test_filename.cpp     |   88 -
 test/testgroup_plain/setup.sh                      |    1 -
 valgrind.supp                                      |   38 +
 158 files changed, 12143 insertions(+), 6838 deletions(-)

diff --git a/Doxyfile b/Doxyfile
index 95ec9bb..46ead01 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,1510 +1,2282 @@
-# Doxyfile 1.5.8
+# Doxyfile 1.8.5
 
 # This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
 #
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
 # The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
 
 #---------------------------------------------------------------------------
 # Project related configuration options
 #---------------------------------------------------------------------------
 
-# This tag specifies the encoding used for all characters in the config file 
-# that follow. The default is UTF-8 which is also the encoding used for all 
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
-# iconv built into libc) for the transcoding. See 
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
 
 DOXYFILE_ENCODING      = UTF-8
 
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
-# by quotes) that should identify the project.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
 
 PROJECT_NAME           = "Osmium"
 
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
-# This could be handy for archiving the generated documentation or 
-# if some version control system is used.
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
 
 PROJECT_NUMBER         = 0.1
 
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
-# base path where the generated documentation will be put. 
-# If a relative path is entered, it will be relative to the location 
-# where doxygen was started. If left blank the current directory will be used.
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
 
 OUTPUT_DIRECTORY       = "doc"
 
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
-# 4096 sub-directories (in 2 levels) under the output directory of each output 
-# format and will distribute the generated files over these directories. 
-# Enabling this option can be useful when feeding doxygen a huge amount of 
-# source files, where putting all generated files in the same directory would 
-# otherwise cause performance problems for the file system.
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
 
 CREATE_SUBDIRS         = NO
 
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
-# documentation generated by doxygen is written. Doxygen will use this 
-# information to generate all constant output in the proper language. 
-# The default language is English, other supported languages are: 
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
-# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
-# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
-# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
-# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, 
-# Spanish, Swedish, and Ukrainian.
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-
+# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en,
+# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
+# Turkish, Ukrainian and Vietnamese.
+# The default value is: English.
 
 OUTPUT_LANGUAGE        = English
 
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
-# include brief member descriptions after the members that are listed in 
-# the file and class documentation (similar to JavaDoc). 
-# Set to NO to disable this.
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
 
 BRIEF_MEMBER_DESC      = YES
 
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
-# the brief description of a member or function before the detailed description. 
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
 # brief descriptions will be completely suppressed.
+# The default value is: YES.
 
 REPEAT_BRIEF           = YES
 
-# This tag implements a quasi-intelligent brief description abbreviator 
-# that is used to form the text in various listings. Each string 
-# in this list, if found as the leading text of the brief description, will be 
-# stripped from the text and the result after processing the whole list, is 
-# used as the annotated text. Otherwise, the brief description is used as-is. 
-# If left blank, the following values are used ("$name" is automatically 
-# replaced with the name of the entity): "The $name class" "The $name widget" 
-# "The $name file" "is" "provides" "specifies" "contains" 
-# "represents" "a" "an" "the"
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
 
-ABBREVIATE_BRIEF       = 
+ABBREVIATE_BRIEF       =
 
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
-# Doxygen will generate a detailed section even if there is only a brief 
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
 # description.
+# The default value is: NO.
 
 ALWAYS_DETAILED_SEC    = NO
 
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
-# inherited members of a class in the documentation of that class as if those 
-# members were ordinary class members. Constructors, destructors and assignment 
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
 # operators of the base classes will not be shown.
+# The default value is: NO.
 
 INLINE_INHERITED_MEMB  = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
-# path before files name in the file list and in the header files. If set 
-# to NO the shortest path that makes the file name unique will be used.
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
 
 FULL_PATH_NAMES        = YES
 
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
-# can be used to strip a user-defined part of the path. Stripping is 
-# only done if one of the specified strings matches the left-hand part of 
-# the path. The tag can be used to show relative paths in the file list. 
-# If left blank the directory from which doxygen is run is used as the 
-# path to strip.
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
 
-STRIP_FROM_PATH        = 
+STRIP_FROM_PATH        =
 
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
-# the path mentioned in the documentation of a class, which tells 
-# the reader which header file to include in order to use a class. 
-# If left blank only the name of the header file containing the class 
-# definition is used. Otherwise one should specify the include paths that 
-# are normally passed to the compiler using the -I flag.
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
 
-STRIP_FROM_INC_PATH    = 
+STRIP_FROM_INC_PATH    =
 
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
-# (but less readable) file names. This can be useful is your file systems 
-# doesn't support long names like on DOS, Mac, or CD-ROM.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
 
 SHORT_NAMES            = NO
 
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
-# will interpret the first line (until the first dot) of a JavaDoc-style 
-# comment as the brief description. If set to NO, the JavaDoc 
-# comments will behave just like regular Qt-style comments 
-# (thus requiring an explicit @brief command for a brief description.)
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
 
 JAVADOC_AUTOBRIEF      = NO
 
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
-# interpret the first line (until the first dot) of a Qt-style 
-# comment as the brief description. If set to NO, the comments 
-# will behave just like regular Qt-style comments (thus requiring 
-# an explicit \brief command for a brief description.)
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
 
 QT_AUTOBRIEF           = NO
 
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
-# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
-# comments) as a brief description. This used to be the default behaviour. 
-# The new default is to treat a multi-line C++ comment block as a detailed 
-# description. Set this tag to YES if you prefer the old behaviour instead.
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
 
 MULTILINE_CPP_IS_BRIEF = NO
 
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
-# member inherits the documentation from any documented member that it 
-# re-implements.
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
 
 INHERIT_DOCS           = YES
 
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
-# a new page for each member. If set to NO, the documentation of a member will 
-# be part of the file/class/namespace that contains it.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
 
 SEPARATE_MEMBER_PAGES  = NO
 
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
-# Doxygen uses this value to replace tabs by spaces in code fragments.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
 
 TAB_SIZE               = 8
 
-# This tag can be used to specify a number of aliases that acts 
-# as commands in the documentation. An alias has the form "name=value". 
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
-# put the command \sideeffect (or @sideeffect) in the documentation, which 
-# will result in a user-defined paragraph with heading "Side Effects:". 
-# You can put \n's in the value part of an alias to insert newlines.
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
 
-ALIASES                = 
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
 
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
-# sources only. Doxygen will then generate output that is more tailored for C. 
-# For instance, some of the names that are used will be different. The list 
-# of all members will be omitted, etc.
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_FOR_C  = NO
 
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
-# sources only. Doxygen will then generate output that is more tailored for 
-# Java. For instance, namespaces will be presented as packages, qualified 
-# scopes will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_JAVA   = NO
 
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
-# sources only. Doxygen will then generate output that is more tailored for 
-# Fortran.
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
 
 OPTIMIZE_FOR_FORTRAN   = NO
 
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
-# sources. Doxygen will then generate output that is tailored for 
-# VHDL.
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
-# Doxygen selects the parser to use depending on the extension of the files it parses. 
-# With this tag you can assign which parser to use for a given extension. 
-# Doxygen has a built-in mapping, but you can override or extend it using this tag. 
-# The format is ext=language, where ext is a file extension, and language is one of 
-# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, 
-# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat 
-# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), 
-# use: inc=Fortran f=C
-
-EXTENSION_MAPPING      = 
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
-# to include (a tag file for) the STL sources as input, then you should 
-# set this tag to YES in order to let doxygen match functions declarations and 
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
-# func(std::string) {}). This also make the inheritance and collaboration 
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
 
 BUILTIN_STL_SUPPORT    = YES
 
-# If you use Microsoft's C++/CLI language, you should set this option to YES to 
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
+# The default value is: NO.
 
 CPP_CLI_SUPPORT        = NO
 
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
-# Doxygen will parse them like normal C++ but will assume all classes use public 
-# instead of private inheritance when no explicit protection keyword is present.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate getter 
-# and setter methods for a property. Setting this option to YES (the default) 
-# will make doxygen to replace the get and set methods by a property in the 
-# documentation. This will only work if the methods are indeed getting or 
-# setting a simple type. If this is not the case, or you want to show the 
-# methods anyway, you should set this option to NO.
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
 
 IDL_PROPERTY_SUPPORT   = YES
 
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
-# tag is set to YES, then doxygen will reuse the documentation of the first 
-# member in the group (if any) for the other members of the group. By default 
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
 # all members of a group must be documented explicitly.
+# The default value is: NO.
 
 DISTRIBUTE_GROUP_DOC   = NO
 
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
-# the same type (for instance a group of public functions) to be put as a 
-# subgroup of that type (e.g. under the Public Functions section). Set it to 
-# NO to prevent subgrouping. Alternatively, this can be done per class using 
-# the \nosubgrouping command.
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
 
 SUBGROUPING            = YES
 
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
-# is documented as struct, union, or enum with the name of the typedef. So 
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
-# with name TypeT. When disabled the typedef will appear as a member of a file, 
-# namespace, or class. And the struct will be named TypeS. This can typically 
-# be useful for C code in case the coding convention dictates that all compound 
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
 # types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
 
 TYPEDEF_HIDES_STRUCT   = NO
 
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
-# determine which symbols to keep in memory and which to flush to disk. 
-# When the cache is full, less often used symbols will be written to disk. 
-# For small to medium size projects (<1000 input files) the default value is 
-# probably good enough. For larger projects a too small cache size can cause 
-# doxygen to be busy swapping symbols to and from disk most of the time 
-# causing a significant performance penality. 
-# If the system has enough physical memory increasing the cache will improve the 
-# performance by keeping more symbols in memory. Note that the value works on 
-# a logarithmic scale so increasing the size by one will rougly double the 
-# memory usage. The cache size is given by this formula: 
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE      = 0
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
 
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
 
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
-# documentation are documented, even if no documentation was available. 
-# Private class members and static file members will be hidden unless 
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
 
 EXTRACT_ALL            = YES
 
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
 
 EXTRACT_PRIVATE        = YES
 
-# If the EXTRACT_STATIC tag is set to YES all static members of a file 
-# will be included in the documentation.
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
 
 EXTRACT_STATIC         = YES
 
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
-# defined locally in source files will be included in the documentation. 
-# If set to NO only classes defined in header files are included.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
 
 EXTRACT_LOCAL_CLASSES  = YES
 
-# This flag is only useful for Objective-C code. When set to YES local 
-# methods, which are defined in the implementation section but not in 
-# the interface are included in the documentation. 
-# If set to NO (the default) only methods in the interface are included.
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
 
 EXTRACT_LOCAL_METHODS  = NO
 
-# If this flag is set to YES, the members of anonymous namespaces will be 
-# extracted and appear in the documentation as a namespace called 
-# 'anonymous_namespace{file}', where file will be replaced with the base 
-# name of the file that contains the anonymous namespace. By default 
-# anonymous namespace are hidden.
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
 
 EXTRACT_ANON_NSPACES   = NO
 
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
-# undocumented members of documented classes, files or namespaces. 
-# If set to NO (the default) these members will be included in the 
-# various overviews, but no documentation section is generated. 
-# This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
 
 HIDE_UNDOC_MEMBERS     = NO
 
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
-# undocumented classes that are normally visible in the class hierarchy. 
-# If set to NO (the default) these classes will be included in the various 
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
 
 HIDE_UNDOC_CLASSES     = NO
 
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
-# friend (class|struct|union) declarations. 
-# If set to NO (the default) these declarations will be included in the 
-# documentation.
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
 
 HIDE_FRIEND_COMPOUNDS  = NO
 
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
-# documentation blocks found inside the body of a function. 
-# If set to NO (the default) these blocks will be appended to the 
-# function's detailed documentation block.
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
 
 HIDE_IN_BODY_DOCS      = NO
 
-# The INTERNAL_DOCS tag determines if documentation 
-# that is typed after a \internal command is included. If the tag is set 
-# to NO (the default) then the documentation will be excluded. 
-# Set it to YES to include the internal documentation.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
 
 INTERNAL_DOCS          = NO
 
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
-# file names in lower-case letters. If set to YES upper-case letters are also 
-# allowed. This is useful if you have classes or files whose names only differ 
-# in case and if your file system supports case sensitive file names. Windows 
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
 # and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
 
 CASE_SENSE_NAMES       = YES
 
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
-# will show members with their full class and namespace scopes in the 
-# documentation. If set to YES the scope will be hidden.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
 
 HIDE_SCOPE_NAMES       = NO
 
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
-# will put a list of the files that are included by a file in the documentation 
-# of that file.
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
 
 SHOW_INCLUDE_FILES     = YES
 
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
-# is inserted in the documentation for inline members.
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
 
 INLINE_INFO            = YES
 
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
-# will sort the (detailed) documentation of file and class members 
-# alphabetically by member name. If set to NO the members will appear in 
-# declaration order.
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
 
 SORT_MEMBER_DOCS       = YES
 
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
-# brief documentation of file, namespace and class members alphabetically 
-# by member name. If set to NO (the default) the members will appear in 
-# declaration order.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: NO.
 
 SORT_BRIEF_DOCS        = NO
 
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
-# hierarchy of group names into alphabetical order. If set to NO (the default) 
-# the group names will appear in their defined order.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
 
 SORT_GROUP_NAMES       = NO
 
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
-# sorted by fully-qualified names, including namespaces. If set to 
-# NO (the default), the class list will be sorted only by class name, 
-# not including the namespace part. 
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 
-# Note: This option applies only to the class list, not to the 
-# alphabetical list.
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
 
 SORT_BY_SCOPE_NAME     = NO
 
-# The GENERATE_TODOLIST tag can be used to enable (YES) or 
-# disable (NO) the todo list. This list is created by putting \todo 
-# commands in the documentation.
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
 
 GENERATE_TODOLIST      = YES
 
-# The GENERATE_TESTLIST tag can be used to enable (YES) or 
-# disable (NO) the test list. This list is created by putting \test 
-# commands in the documentation.
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
 
 GENERATE_TESTLIST      = YES
 
-# The GENERATE_BUGLIST tag can be used to enable (YES) or 
-# disable (NO) the bug list. This list is created by putting \bug 
-# commands in the documentation.
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
 
 GENERATE_BUGLIST       = YES
 
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
-# disable (NO) the deprecated list. This list is created by putting 
-# \deprecated commands in the documentation.
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
 
 GENERATE_DEPRECATEDLIST= YES
 
-# The ENABLED_SECTIONS tag can be used to enable conditional 
-# documentation sections, marked by \if sectionname ... \endif.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
 
-ENABLED_SECTIONS       = 
+ENABLED_SECTIONS       =
 
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
-# the initial value of a variable or define consists of for it to appear in 
-# the documentation. If the initializer consists of more lines than specified 
-# here it will be hidden. Use a value of 0 to hide initializers completely. 
-# The appearance of the initializer of individual variables and defines in the 
-# documentation can be controlled using \showinitializer or \hideinitializer 
-# command in the documentation regardless of this setting.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
 
 MAX_INITIALIZER_LINES  = 30
 
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
-# at the bottom of the documentation of classes and structs. If set to YES the 
-# list will mention the files that were used to generate the documentation.
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
 
 SHOW_USED_FILES        = YES
 
-# If the sources in your project are distributed over multiple directories 
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. 
-# This will remove the Files entry from the Quick Index and from the 
-# Folder Tree View (if specified). The default is YES.
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
 
 SHOW_FILES             = YES
 
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
-# Namespaces page. 
-# This will remove the Namespaces entry from the Quick Index 
-# and from the Folder Tree View (if specified). The default is YES.
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
 
 SHOW_NAMESPACES        = YES
 
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
-# doxygen should invoke to get the current version for each file (typically from 
-# the version control system). Doxygen will invoke the program by executing (via 
-# popen()) the command <command> <input-file>, where <command> is the value of 
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
-# provided by doxygen. Whatever the program writes to standard output 
-# is used as the file version. See the manual for examples.
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
 
-FILE_VERSION_FILTER    = 
+LAYOUT_FILE            =
 
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 
-# doxygen. The layout file controls the global structure of the generated output files 
-# in an output format independent way. The create the layout file that represents 
-# doxygen's defaults, run doxygen with the -l option. You can optionally specify a 
-# file name after the option, if omitted DoxygenLayout.xml will be used as the name 
-# of the layout file.
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
 
-LAYOUT_FILE            = 
+CITE_BIB_FILES         =
 
 #---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 
-# The QUIET tag can be used to turn on/off the messages that are generated 
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
 
 QUIET                  = NO
 
-# The WARNINGS tag can be used to turn on/off the warning messages that are 
-# generated by doxygen. Possible values are YES and NO. If left blank 
-# NO is used.
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
 
 WARNINGS               = YES
 
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
-# automatically be disabled.
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
 
 WARN_IF_UNDOCUMENTED   = YES
 
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
-# potential errors in the documentation, such as not documenting some 
-# parameters in a documented function, or documenting parameters that 
-# don't exist or using markup commands wrongly.
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
 
 WARN_IF_DOC_ERROR      = YES
 
-# This WARN_NO_PARAMDOC option can be abled to get warnings for 
-# functions that are documented, but have no documentation for their parameters 
-# or return value. If set to NO (the default) doxygen will only warn about 
-# wrong or incomplete parameter documentation, but not about the absence of 
-# documentation.
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
 
 WARN_NO_PARAMDOC       = NO
 
-# The WARN_FORMAT tag determines the format of the warning messages that 
-# doxygen can produce. The string should contain the $file, $line, and $text 
-# tags, which will be replaced by the file and line number from which the 
-# warning originated and the warning text. Optionally the format may contain 
-# $version, which will be replaced by the version of the file (if it could 
-# be obtained via FILE_VERSION_FILTER)
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
 
 WARN_FORMAT            = "$file:$line: $text"
 
-# The WARN_LOGFILE tag can be used to specify a file to which warning 
-# and error messages should be written. If left blank the output is written 
-# to stderr.
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
 
-WARN_LOGFILE           = 
+WARN_LOGFILE           =
 
 #---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
 #---------------------------------------------------------------------------
 
-# The INPUT tag can be used to specify the files and/or directories that contain 
-# documented source files. You may enter file names like "myfile.cpp" or 
-# directories like "/usr/src/myproject". Separate the files or directories 
-# with spaces.
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
 
 INPUT                  = include
 
-# This tag can be used to specify the character encoding of the source files 
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
-# also the default input encoding. Doxygen uses libiconv (or the iconv built 
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
-# the list of possible encodings.
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
 
 INPUT_ENCODING         = UTF-8
 
-# If the value of the INPUT tag contains directories, you can use the 
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
-# blank the following patterns are tested: 
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
 
 FILE_PATTERNS          = *.hpp
 
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
-# should be searched for input files as well. Possible values are YES and NO. 
-# If left blank NO is used.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
 
 RECURSIVE              = YES
 
-# The EXCLUDE tag can be used to specify files and/or directories that should 
-# excluded from the INPUT source files. This way you can easily exclude a 
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
 # subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
 
-EXCLUDE                = 
+EXCLUDE                =
 
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
-# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
 # from the input.
+# The default value is: NO.
 
 EXCLUDE_SYMLINKS       = NO
 
-# If the value of the INPUT tag contains directories, you can use the 
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
-# certain files from those directories. Note that the wildcards are matched 
-# against the file with absolute path, so to exclude all test directories 
-# for example use the pattern */test/*
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       = 
+EXCLUDE_PATTERNS       =
 
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
-# (namespaces, classes, functions, etc.) that should be excluded from the 
-# output. The symbol name can be a fully qualified name, a word, or if the 
-# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
 # AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
 
-EXCLUDE_SYMBOLS        = 
+EXCLUDE_SYMBOLS        =
 
-# The EXAMPLE_PATH tag can be used to specify one or more files or 
-# directories that contain example code fragments that are included (see 
-# the \include command).
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
 
-EXAMPLE_PATH           = 
+EXAMPLE_PATH           =
 
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
-# blank all files are included.
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
 
-EXAMPLE_PATTERNS       = 
+EXAMPLE_PATTERNS       =
 
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
-# searched for input files to be used with the \include or \dontinclude 
-# commands irrespective of the value of the RECURSIVE tag. 
-# Possible values are YES and NO. If left blank NO is used.
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
 
 EXAMPLE_RECURSIVE      = NO
 
-# The IMAGE_PATH tag can be used to specify one or more files or 
-# directories that contain image that are included in the documentation (see 
-# the \image command).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
 
-IMAGE_PATH             = 
+IMAGE_PATH             =
 
-# The INPUT_FILTER tag can be used to specify a program that doxygen should 
-# invoke to filter for each input file. Doxygen will invoke the filter program 
-# by executing (via popen()) the command <filter> <input-file>, where <filter> 
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
-# input file. Doxygen will then use the output that the filter program writes 
-# to standard output. 
-# If FILTER_PATTERNS is specified, this tag will be 
-# ignored.
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
 
-INPUT_FILTER           = 
+INPUT_FILTER           =
 
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
-# basis. 
-# Doxygen will compare the file name with each pattern and apply the 
-# filter if there is a match. 
-# The filters are a list of the form: 
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
-# is applied to all files.
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
 
-FILTER_PATTERNS        = 
+FILTER_PATTERNS        =
 
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
-# INPUT_FILTER) will be used to filter the input files when producing source 
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
 
 FILTER_SOURCE_FILES    = NO
 
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
 #---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
 #---------------------------------------------------------------------------
 
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
-# be generated. Documented entities will be cross-referenced with these sources. 
-# Note: To get rid of all source code in the generated output, make sure also 
-# VERBATIM_HEADERS is set to NO.
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
 
 SOURCE_BROWSER         = NO
 
-# Setting the INLINE_SOURCES tag to YES will include the body 
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
 
 INLINE_SOURCES         = NO
 
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
-# doxygen to hide any special comment blocks from generated source code 
-# fragments. Normal C and C++ comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
 
 STRIP_CODE_COMMENTS    = YES
 
-# If the REFERENCED_BY_RELATION tag is set to YES 
-# then for each documented function all documented 
-# functions referencing it will be listed.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
 
 REFERENCED_BY_RELATION = NO
 
-# If the REFERENCES_RELATION tag is set to YES 
-# then for each documented function all documented entities 
-# called/used by that function will be listed.
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
 
 REFERENCES_RELATION    = NO
 
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 
-# link to the source code. 
-# Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
 
 REFERENCES_LINK_SOURCE = YES
 
-# If the USE_HTAGS tag is set to YES then the references to source code 
-# will point to the HTML generated by the htags(1) tool instead of doxygen 
-# built-in source browser. The htags tool is part of GNU's global source 
-# tagging system (see http://www.gnu.org/software/global/global.html). You 
-# will need version 4.8.6 or higher.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
 
 USE_HTAGS              = NO
 
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
-# will generate a verbatim copy of the header file for each class for 
-# which an include is specified. Set to NO to disable this.
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
 
 VERBATIM_HEADERS       = YES
 
 #---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
 
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
-# of all compounds will be generated. Enable this if the project 
-# contains a lot of classes, structs, unions or interfaces.
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
 
 ALPHABETICAL_INDEX     = NO
 
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
-# in which this list will be split (can be a number in the range [1..20])
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
 COLS_IN_ALPHA_INDEX    = 5
 
-# In case all classes in a project start with a common prefix, all 
-# classes will be put under the same header in the alphabetical index. 
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
-# should be ignored while generating the index headers.
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
-IGNORE_PREFIX          = 
+IGNORE_PREFIX          =
 
 #---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
 
 GENERATE_HTML          = YES
 
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `html' will be used as the default path.
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_OUTPUT            = html
 
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
-# doxygen will generate files with .html extension.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_FILE_EXTENSION    = .html
 
-# The HTML_HEADER tag can be used to specify a personal HTML header for 
-# each generated HTML page. If it is left blank doxygen will generate a 
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
 # standard header.
-
-HTML_HEADER            = 
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
-# each generated HTML page. If it is left blank doxygen will generate a 
-# standard footer.
-
-HTML_FOOTER            = 
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
-# style sheet that is used by each HTML page. It can be used to 
-# fine-tune the look of the HTML output. If the tag is left blank doxygen 
-# will generate a default style sheet. Note that doxygen will try to copy 
-# the style sheet file to the HTML output directory, so don't put your own 
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET        = 
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
-# files or namespaces will be aligned in HTML using tables. If set to 
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
-# documentation will contain sections that can be hidden and shown after the 
-# page has loaded. For this to work a browser that supports 
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_DYNAMIC_SECTIONS  = NO
 
-# If the GENERATE_DOCSET tag is set to YES, additional index files 
-# will be generated that can be used as input for Apple's Xcode 3 
-# integrated development environment, introduced with OSX 10.5 (Leopard). 
-# To create a documentation set, doxygen will generate a Makefile in the 
-# HTML output directory. Running make will produce the docset in that 
-# directory and running "make install" will install the docset in 
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
-# it at startup. 
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_DOCSET        = NO
 
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
-# feed. A documentation feed provides an umbrella under which multiple 
-# documentation sets from a single provider (such as a company or product suite) 
-# can be grouped.
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
-# should uniquely identify the documentation set bundle. This should be a 
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
-# will append .docset to the name.
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
-# will be generated that can be used as input for tools like the 
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
-# of the generated HTML documentation.
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_HTMLHELP      = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
-# be used to specify the file name of the resulting .chm file. You 
-# can add a path in front of the file if the result should not be 
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
 # written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
-CHM_FILE               = 
+CHM_FILE               =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
-# be used to specify the location (absolute path including file name) of 
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
-# the HTML help compiler on the generated index.hhp.
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
-HHC_LOCATION           = 
+HHC_LOCATION           =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
-# controls if a separate .chi index file is generated (YES) or that 
-# it should be included in the master .chm file (NO).
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 GENERATE_CHI           = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file 
-# content.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
-CHM_INDEX_ENCODING     = 
+CHM_INDEX_ENCODING     =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
-# controls whether a binary table of contents is generated (YES) or a 
-# normal table of contents (NO) in the .chm file.
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 BINARY_TOC             = NO
 
-# The TOC_EXPAND flag can be set to YES to add extra items for group members 
-# to the contents of the HTML help documentation and to the tree view.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 TOC_EXPAND             = NO
 
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 
-# are set, an additional index file will be generated that can be used as input for 
-# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 
-# HTML documentation.
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_QHP           = NO
 
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
-# be used to specify the file name of the resulting .qch file. 
-# The path specified is relative to the HTML output folder.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
-QCH_FILE               = 
+QCH_FILE               =
 
-# The QHP_NAMESPACE tag specifies the namespace to use when generating 
-# Qt Help Project output. For more information please see 
-# http://doc.trolltech.com/qthelpproject.html#namespace
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
-QHP_NAMESPACE          = 
+QHP_NAMESPACE          =
 
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
-# Qt Help Project output. For more information please see 
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_VIRTUAL_FOLDER     = doc
 
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. 
-# For more information please see 
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-QHP_CUST_FILTER_NAME   = 
+DISABLE_INDEX          = NO
 
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see 
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-QHP_CUST_FILTER_ATTRS  = 
+GENERATE_TREEVIEW      = NONE
 
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's 
-# filter section matches. 
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-QHP_SECT_FILTER_ATTRS  = 
+ENUM_VALUES_PER_LINE   = 4
 
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
-# be used to specify the location of Qt's qhelpgenerator. 
-# If non-empty doxygen will try to run qhelpgenerator on the generated 
-# .qhp file.
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-QHG_LOCATION           = 
+TREEVIEW_WIDTH         = 250
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
-# top of each HTML page. The value NO (the default) enables the index and 
-# the value YES disables it.
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-DISABLE_INDEX          = NO
+EXT_LINKS_IN_WINDOW    = NO
 
-# This tag can be used to set the number of enum values (range [1..20]) 
-# that doxygen will group on one line in the generated HTML documentation.
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-ENUM_VALUES_PER_LINE   = 4
+FORMULA_FONTSIZE       = 10
 
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
-# structure should be generated to display hierarchical information. 
-# If the tag value is set to FRAME, a side panel will be generated 
-# containing a tree-like index structure (just like the one that 
-# is generated for HTML Help). For this to work a browser that supports 
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
-# probably better off using the HTML help feature. Other possible values 
-# for this tag are: HIERARCHIES, which will generate the Groups, Directories, 
-# and Class Hierarchy pages using a tree view instead of an ordered list; 
-# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which 
-# disables this behavior completely. For backwards compatibility with previous 
-# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE 
-# respectively.
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-GENERATE_TREEVIEW      = NONE
+SEARCHENGINE           = YES
 
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
-# used to set the initial width (in pixels) of the frame in which the tree 
-# is shown.
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
 
-TREEVIEW_WIDTH         = 250
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
 
-# Use this tag to change the font size of Latex formulas included 
-# as images in the HTML documentation. The default is 10. Note that 
-# when you change the font size after a successful doxygen run you need 
-# to manually remove any form_*.png images from the HTML output directory 
-# to force them to be regenerated.
+SEARCHENGINE_URL       =
 
-FORMULA_FONTSIZE       = 10
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
 
 #---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
 
 GENERATE_LATEX         = NO
 
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `latex' will be used as the default path.
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_OUTPUT           = latex
 
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
-# invoked. If left blank `latex' will be used as the default command name.
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_CMD_NAME         = latex
 
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
-# generate index for LaTeX. If left blank `makeindex' will be used as the 
-# default command name.
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
-# LaTeX documents. This may be useful for small projects and may help to 
-# save some trees in general.
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 COMPACT_LATEX          = NO
 
-# The PAPER_TYPE tag can be used to set the paper type that is used 
-# by the printer. Possible values are: a4, a4wide, letter, legal and 
-# executive. If left blank a4wide will be used.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 PAPER_TYPE             = a4wide
 
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
-# packages that should be included in the LaTeX output.
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
-EXTRA_PACKAGES         = 
+EXTRA_PACKAGES         =
 
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
-# the generated latex document. The header should contain everything until 
-# the first chapter. If it is left blank doxygen will generate a 
-# standard header. Notice: only use this tag if you know what you are doing!
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
-LATEX_HEADER           = 
+LATEX_FOOTER           =
 
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
-# contain links (just like the HTML output) instead of page references 
-# This makes the output suitable for online browsing using a pdf viewer.
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 PDF_HYPERLINKS         = YES
 
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
-# plain latex in the generated Makefile. Set this option to YES to get a 
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
 # higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 USE_PDFLATEX           = YES
 
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
-# command to the generated LaTeX files. This will instruct LaTeX to keep 
-# running if errors occur, instead of asking the user for help. 
-# This option is also used when generating formulas in HTML.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_BATCHMODE        = NO
 
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
-# include the index chapters (such as File Index, Compound Index, etc.) 
-# in the output.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_HIDE_INDICES     = NO
 
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
 #---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
-# The RTF output is optimized for Word 97 and may not look very pretty with 
-# other RTF readers or editors.
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
 
 GENERATE_RTF           = NO
 
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `rtf' will be used as the default path.
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_OUTPUT             = rtf
 
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
-# RTF documents. This may be useful for small projects and may help to 
-# save some trees in general.
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 COMPACT_RTF            = NO
 
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
-# will contain hyperlink fields. The RTF file will 
-# contain links (just like the HTML output) instead of page references. 
-# This makes the output suitable for online browsing using WORD or other 
-# programs which support those fields. 
-# Note: wordpad (write) and others do not support links.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_HYPERLINKS         = NO
 
-# Load stylesheet definitions from file. Syntax is similar to doxygen's 
-# config file, i.e. a series of assignments. You only have to provide 
-# replacements, missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
-RTF_STYLESHEET_FILE    = 
+RTF_STYLESHEET_FILE    =
 
-# Set optional variables used in the generation of an rtf document. 
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
-RTF_EXTENSIONS_FILE    = 
+RTF_EXTENSIONS_FILE    =
 
 #---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
-# generate man pages
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
 
 GENERATE_MAN           = NO
 
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `man' will be used as the default path.
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_OUTPUT             = man
 
-# The MAN_EXTENSION tag determines the extension that is added to 
-# the generated man pages (default is the subroutine's section .3)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_EXTENSION          = .3
 
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
-# then it will generate one additional man file for each entity 
-# documented in the real man page(s). These additional files 
-# only source the real man page, but without them the man command 
-# would be unable to find the correct page. The default is NO.
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_LINKS              = NO
 
 #---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_XML tag is set to YES Doxygen will 
-# generate an XML file that captures the structure of 
-# the code including all documentation.
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
 
 GENERATE_XML           = NO
 
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `xml' will be used as the default path.
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
 XML_OUTPUT             = xml
 
-# The XML_SCHEMA tag can be used to specify an XML schema, 
-# which can be used by a validating XML parser to check the 
-# syntax of the XML files.
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
-XML_SCHEMA             = 
+XML_SCHEMA             =
 
-# The XML_DTD tag can be used to specify an XML DTD, 
-# which can be used by a validating XML parser to check the 
-# syntax of the XML files.
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
-XML_DTD                = 
+XML_DTD                =
 
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
-# dump the program listings (including syntax highlighting 
-# and cross-referencing information) to the XML output. Note that 
-# enabling this will significantly increase the size of the XML output.
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
 XML_PROGRAMLISTING     = YES
 
 #---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
-# generate an AutoGen Definitions (see autogen.sf.net) file 
-# that captures the structure of the code including all 
-# documentation. Note that this feature is still experimental 
-# and incomplete at the moment.
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
 
 GENERATE_AUTOGEN_DEF   = NO
 
 #---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
-# generate a Perl module file that captures the structure of 
-# the code including all documentation. Note that this 
-# feature is still experimental and incomplete at the 
-# moment.
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
 
 GENERATE_PERLMOD       = NO
 
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
-# to generate PDF and DVI output from the Perl module output.
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_LATEX          = NO
 
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
-# nicely formatted so it can be parsed by a human reader. 
-# This is useful 
-# if you want to understand what is going on. 
-# On the other hand, if this 
-# tag is set to NO the size of the Perl module output will be much smaller 
-# and Perl will parse it just the same.
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_PRETTY         = YES
 
-# The names of the make variables in the generated doxyrules.make file 
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
-# This is useful so different doxyrules.make files included by the same 
-# Makefile don't overwrite each other's variables.
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
-PERLMOD_MAKEVAR_PREFIX = 
+PERLMOD_MAKEVAR_PREFIX =
 
 #---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
+# Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
-# evaluate all C-preprocessor directives found in the sources and include 
-# files.
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
 
 ENABLE_PREPROCESSING   = YES
 
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
-# names in the source code. If set to NO (the default) only conditional 
-# compilation will be performed. Macro expansion can be done in a controlled 
-# way by setting EXPAND_ONLY_PREDEF to YES.
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 MACRO_EXPANSION        = NO
 
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
-# then the macro expansion is limited to the macros specified with the 
-# PREDEFINED and EXPAND_AS_DEFINED tags.
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 EXPAND_ONLY_PREDEF     = NO
 
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 SEARCH_INCLUDES        = YES
 
-# The INCLUDE_PATH tag can be used to specify one or more directories that 
-# contain include files that are not input files but should be processed by 
-# the preprocessor.
-
-INCLUDE_PATH           = 
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
-# patterns (like *.h and *.hpp) to filter out the header-files in the 
-# directories. If left blank, the patterns specified with FILE_PATTERNS will 
-# be used.
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = __linux__ \
+                         OSMIUM_WITH_PBF_INPUT \
+                         OSMIUM_WITH_XML_INPUT
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-INCLUDE_FILE_PATTERNS  = 
+SKIP_FUNCTION_MACROS   = YES
 
-# The PREDEFINED tag can be used to specify one or more macro names that 
-# are defined before the preprocessor is started (similar to the -D option of 
-# gcc). The argument of the tag is a list of macros of the form: name 
-# or name=definition (no spaces). If the definition and the = are 
-# omitted =1 is assumed. To prevent a macro definition from being 
-# undefined via #undef or recursively expanded use the := operator 
-# instead of the = operator.
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
 
-PREDEFINED             = OSMIUM_WITH_GEOS OSMIUM_WITH_SHPLIB OSMIUM_WITH_JAVASCRIPT OSMIUM_WITH_OUTPUT_OSM_XML
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
 
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
-# this tag can be used to specify a list of macro names that should be expanded. 
-# The macro definition that is found in the sources will be used. 
-# Use the PREDEFINED tag if you want to use a different macro definition.
+TAGFILES               =
 
-EXPAND_AS_DEFINED      = 
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
 
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
-# doxygen's preprocessor will remove all function-like macros that are alone 
-# on a line, have an all uppercase name, and do not end with a semicolon. Such 
-# function macros are typically used for boiler-plate code, and will confuse 
-# the parser if not removed.
+GENERATE_TAGFILE       =
 
-SKIP_FUNCTION_MACROS   = YES
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
 
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references   
-#---------------------------------------------------------------------------
+ALLEXTERNALS           = NO
 
-# The TAGFILES option can be used to specify one or more tagfiles. 
-# Optionally an initial location of the external documentation 
-# can be added for each tagfile. The format of a tag file without 
-# this location is as follows: 
-#  
-# TAGFILES = file1 file2 ... 
-# Adding location for the tag files is done as follows: 
-#  
-# TAGFILES = file1=loc1 "file2 = loc2" ... 
-# where "loc1" and "loc2" can be relative or absolute paths or 
-# URLs. If a location is present for each tag, the installdox tool 
-# does not have to be run to correct the links. 
-# Note that each tag file must have a unique name 
-# (where the name does NOT include the path) 
-# If a tag file is not located in the directory in which doxygen 
-# is run, you must also specify the path to the tagfile here.
-
-#TAGFILES               = doc/libstdc++.tag=http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
-# a tag file that is based on the input files it reads.
-
-#GENERATE_TAGFILE       = doc/osmium.tag
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
-# in the class index. If set to NO only the inherited external classes 
-# will be listed.
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
 
-ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
 
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
-# in the modules index. If set to NO, only the current project's groups will 
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
 # be listed.
+# The default value is: YES.
 
-EXTERNAL_GROUPS        = YES
+EXTERNAL_PAGES         = YES
 
-# The PERL_PATH should be the absolute path and name of the perl script 
-# interpreter (i.e. the result of `which perl').
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
 
 PERL_PATH              = /usr/bin/perl
 
 #---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
+# Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
-# or super classes. Setting the tag to NO turns the diagrams off. Note that 
-# this option is superseded by the HAVE_DOT option below. This is only a 
-# fallback. It is recommended to install and use dot, since it yields more 
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
 # powerful graphs.
+# The default value is: YES.
 
 CLASS_DIAGRAMS         = YES
 
-# You can define message sequence charts within doxygen comments using the \msc 
-# command. Doxygen will then run the mscgen tool (see 
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
-# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
 # default search path.
 
-MSCGEN_PATH            = 
+MSCGEN_PATH            =
 
-# If set to YES, the inheritance and collaboration graphs will hide 
-# inheritance and usage relations if the target is undocumented 
-# or is not a class.
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
 
 HIDE_UNDOC_RELATIONS   = YES
 
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
-# available from the path. This tool is part of Graphviz, a graph visualization 
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
-# have no effect if this option is set to NO (the default)
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
 
 HAVE_DOT               = YES
 
-# By default doxygen will write a font called FreeSans.ttf to the output 
-# directory and reference it in all dot files that doxygen generates. This 
-# font does not include all possible unicode characters however, so when you need 
-# these (or just want a differently looking font) you can specify the font name 
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
-# which can be done by putting it in a standard location or by setting the 
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
-# containing the font.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_FONTNAME           = FreeSans
+DOT_FONTNAME           =
 
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
-# The default size is 10pt.
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_FONTSIZE           = 10
 
-# By default doxygen will tell dot to use the output directory to look for the 
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
-# different font using DOT_FONTNAME you can set the path where dot 
-# can find it using this tag.
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_FONTPATH           = 
+DOT_FONTPATH           =
 
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect inheritance relations. Setting this tag to YES will force the 
-# the CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CLASS_GRAPH            = YES
 
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect implementation dependencies (inheritance, containment, and 
-# class references variables) of the class with other documented classes.
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 COLLABORATION_GRAPH    = YES
 
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for groups, showing the direct groups dependencies
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GROUP_GRAPHS           = YES
 
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
-# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
 # Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 UML_LOOK               = NO
 
-# If set to YES, the inheritance and collaboration graphs will show the 
-# relations between templates and their instances.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 TEMPLATE_RELATIONS     = NO
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
-# tags are set to YES then doxygen will generate a graph for each documented 
-# file showing the direct and indirect include dependencies of the file with 
-# other documented files.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INCLUDE_GRAPH          = YES
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
-# documented header file showing the documented files that directly or 
-# indirectly include this file.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INCLUDED_BY_GRAPH      = YES
 
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
-# doxygen will generate a call dependency graph for every global function 
-# or class method. Note that enabling this option will significantly increase 
-# the time of a run. So in most cases it will be better to enable call graphs 
-# for selected functions only using the \callgraph command.
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CALL_GRAPH             = NO
 
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
-# doxygen will generate a caller dependency graph for every global function 
-# or class method. Note that enabling this option will significantly increase 
-# the time of a run. So in most cases it will be better to enable caller 
-# graphs for selected functions only using the \callergraph command.
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CALLER_GRAPH           = NO
 
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
-# will graphical hierarchy of all classes instead of a textual one.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
-# then doxygen will show the dependencies a directory has on other directories 
-# in a graphical way. The dependency relations are determined by the #include 
-# relations between the files in the directories.
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DIRECTORY_GRAPH        = YES
 
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
-# generated by dot. Possible values are png, jpg, or gif 
-# If left blank png will be used.
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_IMAGE_FORMAT       = png
 
-# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
 
-DOT_PATH               = 
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-# The DOTFILE_DIRS tag can be used to specify one or more directories that 
-# contain dot files that are included in the documentation (see the 
-# \dotfile command).
+DOTFILE_DIRS           =
 
-DOTFILE_DIRS           = 
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
 
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
-# nodes that will be shown in the graph. If the number of nodes in a graph 
-# becomes larger than this value, doxygen will truncate the graph, which is 
-# visualized by representing a node as a red box. Note that doxygen if the 
-# number of direct children of the root node in a graph is already larger than 
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_GRAPH_MAX_NODES    = 50
 
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
-# graphs generated by dot. A depth value of 3 means that only nodes reachable 
-# from the root by following a path via at most 3 edges will be shown. Nodes 
-# that lay further from the root node will be omitted. Note that setting this 
-# option to 1 or 2 may greatly reduce the computation time needed for large 
-# code bases. Also note that the size of a graph can be further restricted by 
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 MAX_DOT_GRAPH_DEPTH    = 0
 
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
-# background. This is disabled by default, because dot on Windows does not 
-# seem to support this out of the box. Warning: Depending on the platform used, 
-# enabling this option may lead to badly anti-aliased labels on the edges of 
-# a graph (i.e. they become hard to read).
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_TRANSPARENT        = NO
 
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
-# files in one run (i.e. multiple -o and -T options on the command line). This 
-# makes dot run faster, but since only newer versions of dot (>1.8.10) 
-# support this, this feature is disabled by default.
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_MULTI_TARGETS      = NO
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
-# generate a legend page explaining the meaning of the various boxes and 
-# arrows in the dot generated graphs.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GENERATE_LEGEND        = YES
 
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
-# remove the intermediate dot files that are used to generate 
-# the various graphs.
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_CLEANUP            = YES
-
-#---------------------------------------------------------------------------
-# Options related to the search engine
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be 
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE           = YES
diff --git a/Makefile b/Makefile
index 36e756f..8f2d797 100644
--- a/Makefile
+++ b/Makefile
@@ -4,43 +4,67 @@
 #
 #------------------------------------------------------------------------------
 
+PREFIX ?= /usr
+CXX=g++
+
+INSTALL_USER := root
+
+# We use the numeric id 0 here because different systems (Linux vs. BSD)
+# use different names for the "root group".
+INSTALL_GROUP := 0
+
 all:
 
+.PHONY: clean install check check-includes test indent
+
 clean:
-	rm -fr doc/html
+	rm -fr check-includes-report doc/html
+	$(MAKE) -C test clean
 
 install: doc
-	install -m 755 -g root -o root -d $(DESTDIR)/usr/include
-	install -m 755 -g root -o root -d $(DESTDIR)/usr/share/doc/libosmium-dev
-	install -m 644 -g root -o root README $(DESTDIR)/usr/share/doc/libosmium-dev/README
-	install -m 644 -g root -o root include/osmium.hpp $(DESTDIR)/usr/include
-	cp --recursive include/osmium $(DESTDIR)/usr/include
-	cp --recursive doc/html $(DESTDIR)/usr/share/doc/libosmium-dev
+	install -m 755 -g $(INSTALL_GROUP) -o $(INSTALL_USER) -d $(DESTDIR)$(PREFIX)/include
+	install -m 755 -g $(INSTALL_GROUP) -o $(INSTALL_USER) -d $(DESTDIR)$(PREFIX)/share/doc/libosmium-dev
+	install -m 644 -g $(INSTALL_GROUP) -o $(INSTALL_USER) README $(DESTDIR)$(PREFIX)/share/doc/libosmium-dev/README
+	install -m 644 -g $(INSTALL_GROUP) -o $(INSTALL_USER) include/osmium.hpp $(DESTDIR)$(PREFIX)/include
+	cp -r include/osmium $(DESTDIR)$(PREFIX)/include
+	cp -r doc/html $(DESTDIR)$(PREFIX)/share/doc/libosmium-dev
 
 check:
-	cppcheck --enable=all -I include */*.cpp test/*/test_*.cpp
+	cppcheck --enable=all -I include */*.cpp test/t/*/test_*.cpp
+
+WARNINGFLAGS = -Wall -Wextra -Wredundant-decls -Wdisabled-optimization -pedantic -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wno-long-long -Winline -Weffc++ -Wold-style-cast
 
 # This will try to compile each include file on its own to detect missing
 # #include directives. Note that if this reports [OK], it is not enough
-# to be sure it will compile in production code. But if it reports [FAILED]
+# to be sure it will compile in production code. But if it reports an error
 # we know we are missing something.
 check-includes:
-	echo "check includes report:" >check-includes-report; \
-	for FILE in include/*.hpp include/*/*.hpp include/*/*/*.hpp; do \
-        echo "$${FILE}:" >>check-includes-report; \
-        echo -n "$${FILE} "; \
-        if `g++ -I include $${FILE} 2>>check-includes-report`; then \
-            echo "[OK]"; \
+	echo "CHECK INCLUDES REPORT:" >check-includes-report; \
+    allok=yes; \
+	for FILE in include/*.hpp include/*/*.hpp include/*/*/*.hpp include/*/*/*/*.hpp; do \
+        flags=`./get_options.sh --cflags $${FILE}`; \
+        eval eflags=$${flags}; \
+        compile="$(CXX) $(WARNINGFLAGS) -I include $${eflags} $${FILE}"; \
+        echo "\n======== $${FILE}\n$${compile}" >>check-includes-report; \
+        if `$${compile} 2>>check-includes-report`; then \
+            echo "[OK] $${FILE}"; \
         else \
-            echo "[FAILED]"; \
+            echo "[  ] $${FILE}"; \
+            allok=no; \
         fi; \
         rm -f $${FILE}.gch; \
-	done
+	done; \
+    if test $${allok} = "yes"; then echo "All files OK"; else echo "There were errors"; fi; \
+    echo "\nDONE" >>check-includes-report
+
+test:
+	(cd test && ./run_tests.sh)
 
 indent:
-	astyle --style=java --indent-namespaces --indent-switches --pad-header --suffix=none --recursive include/\*.hpp examples/\*.cpp examples/\*.hpp osmjs/\*.cpp test/\*.cpp
+	astyle --style=java --indent-namespaces --indent-switches --pad-header --lineend=linux --suffix=none --recursive include/\*.hpp examples/\*.cpp examples/\*.hpp osmjs/\*.cpp test/\*.cpp
+#	astyle --style=java --indent-namespaces --indent-switches --pad-header --unpad-paren --align-pointer=type --lineend=linux --suffix=none --recursive include/\*.hpp examples/\*.cpp examples/\*.hpp osmjs/\*.cpp test/\*.cpp
 
-doc: doc/html/files.html
+doc: doc/html/files.html Doxyfile
 
 doc/html/files.html: include/*.hpp include/*/*.hpp include/*/*/*.hpp
 	doxygen >/dev/null
diff --git a/NOTES_FOR_DEVELOPERS b/NOTES_FOR_DEVELOPERS
index 4026ee0..79f12b8 100644
--- a/NOTES_FOR_DEVELOPERS
+++ b/NOTES_FOR_DEVELOPERS
@@ -16,8 +16,6 @@ Namespace
 =========
 
 All Osmium code must be in the "Osmium" namespace or one of its sub-namespaces.
-There are currently some utility classes not in the Osmium namespace, but this
-should change.
 
 
 Include-Only
@@ -41,27 +39,42 @@ different.
 * Variables, attributes, and function names are lowercase with
   underscores_between_words.
 * Class attribute names start with "m_" (member).
-* Template parameters start with uppercase "T" and use CamelCase.
+* Template parameters are single uppercase letters or start with uppercase "T"
+  and use CamelCase.
 * Typedefs have names_like_this_t which end in "_t".
 * Macros should only be used for controlling which parts of the code should be
   included when compiling.
 * Use descriptive_variable_names, exceptions are well-established conventions
-  like "i" for a loop variable.
+  like "i" for a loop variable. Iterators are usually called "it".
 * Declare variables where they are first used (C++ style), not at the beginning
   of a function (old C style).
 * Names from external namespaces (even "std") are always mentioned explicitly.
-  Do not use "using".
+  Do not use "using". The one exception are smart pointers and related functions.
+  Include <osmium/smart_ptr.hpp> and use the functions without namespace.
 
 Use "make indent" in the toplevel directory to fix indentation. It calls
 "astyle" with the right parameters. This program is in the "astyle" Debian
 package.
 
 
+Debugging code
+==============
+
+Classes that need debugging should derive from Osmium::WithDebug. Debugging
+code in those classes should be wrapped in
+
+  if (debug && has_debug_level(LEVEL)) {
+  }
+
+Classes not derived from Osmium::WithDebug do not support debugging.
+
+Debugging output (if it was requested) should go to std::cout.
+
+
 Checking your code
 ==================
 
 The Osmium makefiles use pretty draconian warning options for gcc.
-(-Wall -Wextra -Wredundant-decls -Wdisabled-optimization -pedantic).
 This is good. Code should never produce any warnings, even with those
 settings.
 
@@ -69,6 +82,10 @@ Call "make check" in the toplevel directory to check your code. It uses
 cppcheck which finds some bugs that gcc doesn't. But take the result with
 a grain of salt, it also sometimes produces wrong warnings.
 
+Use "make check-includes" to compile all include files on their own to
+check whether dependencies are all okay. This compiles with extra
+draconian warnings, some of them you can ignore.
+
 
 Testing
 =======
@@ -87,4 +104,3 @@ Osmium uses the Doxygen (www.doxygen.org) source code documentation system.
 
 Call "make doc" in the toplevel directory to generate the documentation.
 
-
diff --git a/README b/README
index d2a2103..acb2359 100644
--- a/README
+++ b/README
@@ -12,8 +12,7 @@ Available handlers include:
 * Javascript handler (calls Javascript callbacks you provide)
 * Multipolygon handler (assembles areas(multipolygons) from relations and ways)
 * NodeLocationStore handler (stores node locations and builds way geometries from them)
-* Statistics handler (does some general statistics)
-* (others...)
+* ...
 
 Of course, you can also write your own handlers.
 
@@ -31,56 +30,64 @@ option to do this.
 PREREQUISITES
 -------------
 
-Different parts of Osmium and the applications build on top of it need
+Different parts of Osmium (and the applications built on top of it) need
 different libraries:
 
 boost (several libraries)
     http://www.boost.org/
     Debian/Ubuntu: libboost-dev
+    openSUSE: boost-devel
 
 zlib (for PBF support)
     http://www.zlib.net/
     Debian/Ubuntu: zlib1g-dev
+    openSUSE: zlib-devel
 
 shapelib (for shapefile support in osmjs)
     http://shapelib.maptools.org/
     Debian/Ubuntu: libshp-dev
+    openSUSE: libshp-devel
 
-libsqlite3 (for some applications only)
-    http://sqlite.org/
-    Debian/Ubuntu: libsqlite3-dev
-
-libgd (for tagstats and nodedensity applications only)
+libgd (for nodedensity example only)
     http://www.libgd.org/
     Debian/Ubuntu: libgd2-xpm-dev
+    openSUSE: gd-devel
 
 GDAL (for OGR support)
     http://gdal.org/
     Debian/Ubuntu: libgdal1-dev
+    openSUSE: libgdal-devel
 
 Expat (for parsing XML files)
     http://expat.sourceforge.net/
     Debian/Ubuntu: libexpat1-dev
+    openSUSE: libexpat-devel
 
 GEOS (for assembling multipolygons etc.)
     http://trac.osgeo.org/geos/
-    Debian/Ubuntu: libgeos-dev
+    Debian/Ubuntu: libgeos++-dev
+    openSUSE: libgeos-devel
 
 Google sparsehash
     http://code.google.com/p/google-sparsehash/
     Debian/Ubuntu: libsparsehash-dev
+    openSUSE: sparsehash
 
 Google V8 Javascript engine (for Javascript support)
     http://code.google.com/apis/v8/
+    Osmium works with V8 version 3.14 and 3.15, but not with 3.21.
     Debian/Ubuntu: libv8-dev
+    openSUSE: v8-devel
 
 LibICU (for UTF-8/UTF-16 conversion, only for Javascript support)
     http://site.icu-project.org/
     Debian/Ubuntu: libicu-dev
+    openSUSE: libicu-devel
 
 Google protocol buffers (for PBF support)
-    http://code.google.com/p/protobuf/ (at least Version 2.3.0 needed)
+    http://code.google.com/p/protobuf/ (at least version 2.3.0 needed)
     Debian/Ubuntu: libprotobuf-dev protobuf-compiler
+    openSUSE: protobuf-devel
     Also see http://wiki.openstreetmap.org/wiki/PBF_Format
 
 Doxygen (to build API documentation)
@@ -93,12 +100,10 @@ libboost-test (for tests)
 
 OSMPBF (for PBF support)
     https://github.com/scrosby/OSM-binary
-    You need to build this first.
+    Debian/Ubuntu: libosmpbf-dev
 
-You need to either install the Debian/Ubuntu packages (or equivalent for other
-distributions) or install those libraries from source. Most libraries should be
-available in all distributions. The Google libs (V8, protobuf, sparsehash)
-are only available in newer distributions (Ubuntu since Maverick).
+You need to either install the packages for your distribution or install those
+libraries from source. Most libraries should be available in all distributions.
 
 
 FILES
@@ -110,6 +115,8 @@ include     - C/C++ include files. All of Osmium is in those header files which
 osmjs       - Osmium application "osmjs".
 osmjs/js    - Example Javascript handlers.
 examples    - Osmium example applications.
+test        - Tests (see below).
+debian      - Needed to build Debian/Ubuntu packages.
 
 
 BUILDING
@@ -131,12 +138,41 @@ See "osmjs/README" for build instructions of osmjs.
 Call "make clean" in any of those places to clean up.
 
 
+USING OSMIUM IN YOUR C++ CODE
+-----------------------------
+
+To use Osmium in your code you just have to include the headers you are
+interested in. So, if you need the Way class for instance you include
+<osmium/osm/way.hpp>. If you need the Debug handler, you include
+<osmium/handler/debug.hpp>.
+
+If you need any OSM file input, then first define one or both of these macros
+and then include <osmium.hpp>:
+
+  #define OSMIUM_WITH_PBF_INPUT
+  #define OSMIUM_WITH_XML_INPUT
+  #include <osmium.hpp>
+
+There are some parts of Osmium that are a bit more difficult to use.
+You'll find some examples in the 'example' and 'osmjs' directories.
+
+
 TESTING
 -------
 
-There are a few tests using the Boost Unit Test Framework in the 'test'
-directory. Go there and type "./run_tests.sh" to compile and run the tests.
-Many more tests are needed, any help appreciated.
+There are a few tests using the Boost Unit Test Framework in the "test"
+directory. Many more tests are needed, any help appreciated.
+
+Run "make test" from the main directory or go to the "test" directory and type
+"./run_tests.sh" to compile and run the tests. You can run a single test by
+calling "./run_test.sh TESTFILE", for instance:
+  ./run_test.sh t/osm/test_node.cpp
+
+There are some other tests that are not neatly integrated. Go to the "test"
+directory and run
+  broken_way_geometries/test.sh
+or
+  utf8_clipping/test.sh
 
 
 LICENSE
@@ -145,8 +181,8 @@ LICENSE
 Osmium is available under the GNU LGPL version 3 or later, or - at your option -
 the GNU GPL version 3 or later.
 
-See http://eigen.tuxfamily.org/index.php?title=Licensing_FAQ for a good
-description of what that means.
+See http://eigen.tuxfamily.org/index.php?title=Licensing_FAQ&oldid=1116 for a
+good description of what that means.
 
 
 AUTHORS
@@ -159,4 +195,5 @@ Other authors:
 * Christian Vetter <veaac.fdirct at gmail.com> (PBF parser)
 * Scott A. Crosby <scott at sacrosby.com> (PBF format)
 * Peter Körner <github at mazdermind.de> (XML and PBF writer, ...)
+* Johannes Kolb <johannes.kolb at gmx.net> (Tests, ...)
 
diff --git a/examples/.gitignore b/examples/.gitignore
index ac4c74f..691dd59 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -2,12 +2,14 @@ nodedensity
 osmium_convert
 osmium_debug
 osmium_find_bbox
+osmium_mpdump
 osmium_progress
 osmium_range_from_history
+osmium_relation_members
 osmium_sizeof
-osmium_stats
 osmium_store_and_debug
 osmium_time
 osmium_toogr
 osmium_toogr2
+osmium_to_postgis
 osmium_toshape
diff --git a/examples/Makefile b/examples/Makefile
index 9542d55..391ec80 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -1,53 +1,54 @@
 #------------------------------------------------------------------------------
 #
-#  Makefile for Osmium examples.
+#  Makefile for Osmium examples
 #
 #------------------------------------------------------------------------------
+#
+#  You can set several environment variables before running make if you don't
+#  like the defaults:
+#
+#  CXX                - Your C++ compiler.
+#  CPLUS_INCLUDE_PATH - Include file search path.
+#  CXXFLAGS           - Extra compiler flags.
+#  LDFLAGS            - Extra linker flags.
+#  
+#------------------------------------------------------------------------------
 
-CXX = g++
-#CXX = clang
-
-CXXFLAGS = -g
-#CXXFLAGS = -O3
-
-CXXFLAGS += -Wall -Wextra -Wredundant-decls -Wdisabled-optimization -pedantic -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo
-#CXXFLAGS += -Wpadded -Winline -Weffc++ -Wold-style-cast
-
-# uncomment this if you want information on how long it took to build the multipolygons
-#CXXFLAGS += -DOSMIUM_WITH_MULTIPOLYGON_PROFILING
-
-CXXFLAGS_GEOS    = -DOSMIUM_WITH_GEOS $(shell geos-config --cflags)
-CXXFLAGS_SHAPE   = -DOSMIUM_WITH_SHPLIB $(CXXFLAGS_GEOS)
-CXXFLAGS_LIBXML2 = -DOSMIUM_WITH_OUTPUT_OSM_XML $(shell xml2-config --cflags)
-CXXFLAGS_OGR     = -DOSMIUM_WITH_OGR $(shell gdal-config --cflags)
-
+CXXFLAGS += -O3
+#CXXFLAGS += -g
 CXXFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+CXXFLAGS += -I../include -I/usr/include/libshp
 
-CXXFLAGS += -I../include
+# remove this if you do not want debugging to be compiled in
+CXXFLAGS += -DOSMIUM_WITH_DEBUG
 
-LDFLAGS = -L/usr/local/lib -lexpat -lpthread
+CXXFLAGS_GEOS     := $(shell geos-config --cflags)
+CXXFLAGS_LIBXML2  := $(shell xml2-config --cflags)
+CXXFLAGS_OGR      := $(shell gdal-config --cflags)
+CXXFLAGS_WARNINGS := -Wall -Wextra -Wdisabled-optimization -pedantic -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wno-long-long
 
-LIB_GD       = -lgd -lz -lm
-LIB_SQLITE   = -lsqlite3
-LIB_V8       = -lv8
-LIB_GEOS     = $(shell geos-config --libs)
-LIB_SHAPE    = -lshp $(LIB_GEOS)
-LIB_PROTOBUF = -lz -lprotobuf-lite -losmpbf
-LIB_XML2     = $(shell xml2-config --libs)
-LIB_OGR      = $(shell gdal-config --libs)
+LIB_EXPAT  := -lexpat
+LIB_PBF    := -lz -lpthread -lprotobuf-lite -losmpbf
+LIB_GD     := -lgd -lz -lm
+LIB_GEOS   := $(shell geos-config --libs)
+LIB_OGR    := $(shell gdal-config --libs)
+LIB_SHAPE  := -lshp $(LIB_GEOS)
+LIB_XML2   := $(shell xml2-config --libs)
 
-PROGRAMS = \
+PROGRAMS := \
     osmium_convert \
     osmium_debug \
-    osmium_store_and_debug \
     osmium_find_bbox \
+    osmium_mpdump \
     osmium_progress \
     osmium_range_from_history \
+    osmium_relation_members \
     osmium_sizeof \
-    osmium_stats \
+    osmium_store_and_debug \
     osmium_time \
     osmium_toogr \
     osmium_toogr2 \
+    osmium_to_postgis \
     osmium_toshape \
     nodedensity
 
@@ -56,44 +57,50 @@ PROGRAMS = \
 all: $(PROGRAMS)
 
 osmium_convert: osmium_convert.cpp
-	$(CXX) $(CXXFLAGS) $(CXXFLAGS_LIBXML2) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF) $(LIB_XML2)
-
-osmium_store_and_debug: osmium_store_and_debug.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS_LIBXML2) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_XML2)
 
 osmium_debug: osmium_debug.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF)
-#	$(CXX) $(CXXFLAGS) -DOSMIUM_DEBUG_WITH_ENDTIME -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF)
+#	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -DOSMIUM_DEBUG_WITH_ENDTIME -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF)
 
 osmium_find_bbox: osmium_find_bbox.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF)
+
+osmium_mpdump: osmium_mpdump.cpp
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS_GEOS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_GEOS)
 
 osmium_progress: osmium_progress.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF)
 
 osmium_range_from_history: osmium_range_from_history.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS_LIBXML2) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_XML2)
+
+osmium_relation_members: osmium_relation_members.cpp
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF)
 
 osmium_sizeof: osmium_sizeof.cpp
-	$(CXX) $(CXXFLAGS) $(CXXFLAGS_LIBXML2) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF) $(LIB_XML2)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF)
 
-osmium_stats: osmium_stats.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF) $(LIB_SQLITE)
+osmium_store_and_debug: osmium_store_and_debug.cpp
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF)
 
 osmium_time: osmium_time.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF)
 
 osmium_toogr: osmium_toogr.cpp
-	$(CXX) $(CXXFLAGS) $(CXXFLAGS_OGR) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF) $(LIB_OGR)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS_OGR) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_OGR)
 
 osmium_toogr2: osmium_toogr2.cpp
-	$(CXX) $(CXXFLAGS) $(CXXFLAGS_OGR) $(CXXFLAGS_GEOS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF) $(LIB_OGR) $(LIB_GEOS)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS_OGR) $(CXXFLAGS_GEOS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_OGR) $(LIB_GEOS)
+
+osmium_to_postgis: osmium_to_postgis.cpp
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS_OGR) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_OGR)
 
 osmium_toshape: osmium_toshape.cpp
-	$(CXX) $(CXXFLAGS) $(CXXFLAGS_SHAPE) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF) $(LIB_SHAPE)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS_GEOS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_SHAPE)
 
 nodedensity: nodedensity.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF) $(LIB_GD)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_GD)
 
 clean:
 	rm -f *.o core $(PROGRAMS)
diff --git a/examples/README b/examples/README
index f5d0757..5be1f82 100644
--- a/examples/README
+++ b/examples/README
@@ -3,7 +3,7 @@ Osmium Examples
 ===============
 
 This folder contains a number of example applications to demonstrate the
-use of the Osmium framework for other developers.
+use of the Osmium framework.
 
 
 FILES
@@ -23,21 +23,27 @@ FILES
 * osmium_find_bbox  
   This is a small tool to find the bounding box of the input file.
 
+* osmium_mpdump
+  Create multipolygons and dump them to stdout.
+
 * osmium_progress  
   This is a small tool demonstrating the use of the progress handler.
 
 * osmium_range_from_history  
   Program to test the RangeFromHistory handler.
 
+* osmium_relation_members
+  Shows how the Osmium::Relations::Assembler class is used. Collects
+  all members of all relations in the input data and dumps the tags of
+  all relations and all their members to stdout.
+
 * osmium_sizeof  
   This is a small tool to find out the sizes of some basic classes.
   It is only used for Osmium development.
 
-* osmium_stats  
-  This is a small tool to try the Statistics handler.
-
 * osmium_store_and_debug  
-  This is a small tool to dump the contents of the input file.
+  This example program shows how to read an OSM change file and
+  apply it to an OSM file. The results are dumped to stdout.
 
 * osmium_time  
   A small application that counts the various nodes, ways and relations in an
@@ -52,6 +58,10 @@ FILES
   the OGR library. (This version creates multipolygons and reads the input file
   twice to do that.)
 
+* osmium_to_postgis
+  This is an example tool that loads OSM data into a PostGIS database with
+  hstore tags column using the OGR library.
+
 * osmium_toshape  
   An example application that converts any kind of OSM file into a set of
   shapefiles. It is filtering out highways as linestrings and postboxes as points.
diff --git a/examples/nodedensity.cpp b/examples/nodedensity.cpp
index 6eb2121..da42a00 100644
--- a/examples/nodedensity.cpp
+++ b/examples/nodedensity.cpp
@@ -1,29 +1,14 @@
 /*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
-
+The code in this example file is released into the Public Domain.
 */
 
-#include <string>
 #include <limits>
-#include <cstdio>
+#include <iostream>
 #include <gd.h>
 
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
+
 #include <osmium.hpp>
 
 /* ================================================== */
@@ -60,8 +45,8 @@ public:
     }
 
     void node(const shared_ptr<Osmium::OSM::Node const>& node) {
-        int x = int( (180 + node->position().lon()) * m_factor );
-        int y = int( ( 90 - node->position().lat()) * m_factor );
+        int x = int((180 + node->position().lon()) * m_factor);
+        int y = int(( 90 - node->position().lat()) * m_factor);
         if (x <        0) x =         0;
         if (x >= m_xsize) x = m_xsize-1;
         if (y <        0) y =         0;
@@ -95,16 +80,14 @@ public:
 
         gdImagePng(im, stdout);
         gdImageDestroy(im);
-        throw Osmium::Input::StopReading();
+        throw Osmium::Handler::StopReading();
     }
 
 }; // class NodeDensityHandler
 
 /* ================================================== */
 
-int main(int argc, char *argv[]) {
-    Osmium::init();
-
+int main(int argc, char* argv[]) {
     if (argc != 5) {
         std::cerr << "Usage: " << argv[0] << " OSMFILE SIZE MIN MAX\n\n";
         std::cerr << "  OSMFILE - OSM file of any type.\n";
@@ -121,6 +104,8 @@ int main(int argc, char *argv[]) {
 
     Osmium::OSMFile infile(argv[1]);
     NodeDensityHandler handler(size, min, max);
-    infile.read(handler);
+    Osmium::Input::read(infile, handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_convert.cpp b/examples/osmium_convert.cpp
index 9f525a1..6d650a9 100644
--- a/examples/osmium_convert.cpp
+++ b/examples/osmium_convert.cpp
@@ -2,81 +2,20 @@
 
   Convert OSM files from one format into another.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+  The code in this example file is released into the Public Domain.
 
 */
 
-#include <cstdlib>
+#include <iostream>
 #include <getopt.h>
 
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
 #include <osmium.hpp>
+#include <osmium/output/xml.hpp>
+#include <osmium/output/pbf.hpp>
 #include <osmium/handler/progress.hpp>
 
-class ConvertHandler : public Osmium::Handler::Base {
-
-    Osmium::OSMFile& m_outfile;
-    Osmium::Handler::Progress m_progress_handler;
-
-    Osmium::Output::Base* output;
-
-public:
-
-    ConvertHandler(Osmium::OSMFile& osmfile) : m_outfile(osmfile) {
-    }
-
-    ~ConvertHandler() {
-    }
-
-    void init(Osmium::OSM::Meta& meta) {
-        output = m_outfile.create_output_file();
-        output->init(meta);
-        m_progress_handler.init(meta);
-    }
-
-    void node(const shared_ptr<Osmium::OSM::Node const>& node) {
-        output->node(node);
-        m_progress_handler.node(node);
-    }
-
-    void way(const shared_ptr<Osmium::OSM::Way const>& way) {
-        output->way(way);
-        m_progress_handler.way(way);
-    }
-
-    void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
-        output->relation(relation);
-        m_progress_handler.relation(relation);
-    }
-
-    void final() {
-        output->final();
-        m_progress_handler.final();
-        delete output;
-    }
-
-};
-
-/* ================================================== */
-
 void print_help() {
     std::cout << "osmium_convert [OPTIONS] [INFILE [OUTFILE]]\n\n" \
               << "If INFILE or OUTFILE is not given stdin/stdout is assumed.\n" \
@@ -97,7 +36,7 @@ void print_help() {
               << "  -t, --to-format=FORMAT    Output format\n";
 }
 
-int main(int argc, char *argv[]) {
+int main(int argc, char* argv[]) {
     static struct option long_options[] = {
         {"debug",       no_argument, 0, 'd'},
         {"help",        no_argument, 0, 'h'},
@@ -148,8 +87,6 @@ int main(int argc, char *argv[]) {
         input =  argv[optind];
     }
 
-    Osmium::init(debug);
-
     Osmium::OSMFile infile(input);
     if (!input_format.empty()) {
         try {
@@ -170,15 +107,25 @@ int main(int argc, char *argv[]) {
         }
     }
 
-    if (infile.get_type() == Osmium::OSMFile::FileType::OSM() && outfile.get_type() == Osmium::OSMFile::FileType::History()) {
+    if (infile.type() == Osmium::OSMFile::FileType::OSM() && outfile.type() == Osmium::OSMFile::FileType::History()) {
         std::cerr << "Warning! You are converting from an OSM file without history information to one with history information.\nThis will almost certainly not do what you want!" << std::endl;
-    } else if (infile.get_type() == Osmium::OSMFile::FileType::History() && outfile.get_type() == Osmium::OSMFile::FileType::OSM()) {
+    } else if (infile.type() == Osmium::OSMFile::FileType::History() && outfile.type() == Osmium::OSMFile::FileType::OSM()) {
         std::cerr << "Warning! You are converting from an OSM file with history information to one without history information.\nThis will almost certainly not do what you want!" << std::endl;
-    } else if (infile.get_type() != outfile.get_type()) {
+    } else if (infile.type() != outfile.type()) {
         std::cerr << "Warning! Source and destination are not of the same type." << std::endl;
     }
 
-    ConvertHandler handler(outfile);
-    infile.read(handler);
+    Osmium::Output::Handler out(outfile);
+    out.set_debug_level(debug ? 1 : 0);
+    out.set_generator("osmium_convert");
+
+    Osmium::Handler::Progress progress_handler;
+
+    typedef Osmium::Handler::Sequence<Osmium::Output::Handler, Osmium::Handler::Progress> sequence_handler_t;
+    sequence_handler_t sequence_handler(out, progress_handler);
+
+    Osmium::Input::read(infile, sequence_handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_debug.cpp b/examples/osmium_debug.cpp
index 46e668c..2fbeec6 100644
--- a/examples/osmium_debug.cpp
+++ b/examples/osmium_debug.cpp
@@ -5,28 +5,14 @@
   If OSMIUM_DEBUG_WITH_ENDTIME is defined when compiling, the
   Osmium::Handler::EndTime is used.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
+  The code in this example file is released into the Public Domain.
 
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
+*/
 
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+#include <iostream>
 
-*/
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
 
 #include <osmium.hpp>
 #include <osmium/handler/debug.hpp>
@@ -35,11 +21,9 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 # include <osmium/handler/endtime.hpp>
 #endif // OSMIUM_DEBUG_WITH_ENDTIME
 
-int main(int argc, char *argv[]) {
+int main(int argc, char* argv[]) {
     std::ios_base::sync_with_stdio(false);
 
-    Osmium::init(true);
-
     if (argc != 2) {
         std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
         exit(1);
@@ -48,10 +32,12 @@ int main(int argc, char *argv[]) {
     Osmium::OSMFile infile(argv[1]);
 #ifdef OSMIUM_DEBUG_WITH_ENDTIME
     Osmium::Handler::Debug debug_handler(true);
-    Osmium::Handler::EndTime<Osmium::Handler::Debug> handler(&debug_handler);
+    Osmium::Handler::EndTime<Osmium::Handler::Debug> handler(debug_handler);
 #else
     Osmium::Handler::Debug handler;
 #endif // OSMIUM_DEBUG_WITH_ENDTIME
-    infile.read(handler);
+    Osmium::Input::read(infile, handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_find_bbox.cpp b/examples/osmium_find_bbox.cpp
index de54254..d9ac1ce 100644
--- a/examples/osmium_find_bbox.cpp
+++ b/examples/osmium_find_bbox.cpp
@@ -2,39 +2,21 @@
 
   This is a small tool to find the bounding box of the input file.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+  The code in this example file is released into the Public Domain.
 
 */
 
-#include <cstdlib>
+#include <iostream>
+
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
 
 #include <osmium.hpp>
 #include <osmium/handler/find_bbox.hpp>
 
 /* ================================================== */
 
-int main(int argc, char *argv[]) {
-    Osmium::init();
-
+int main(int argc, char* argv[]) {
     if (argc != 2) {
         std::cerr << "Usage: " << argv[0] << " OSMFILE" << std::endl;
         exit(1);
@@ -42,12 +24,14 @@ int main(int argc, char *argv[]) {
 
     Osmium::OSMFile infile(argv[1]);
     Osmium::Handler::FindBbox handler;
-    infile.read(handler);
+    Osmium::Input::read(infile, handler);
+
+    Osmium::OSM::Bounds bounds = handler.bounds();
+    std::cout <<  "minlon=" << bounds.bottom_left().lon()
+              << " minlat=" << bounds.bottom_left().lat()
+              << " maxlon=" << bounds.top_right().lon()
+              << " maxlat=" << bounds.top_right().lat() << std::endl;
 
-    Osmium::OSM::Bounds b = handler.bounds();
-    std::cout <<  "minlon=" << b.bl().lon()
-              << " minlat=" << b.bl().lat()
-              << " maxlon=" << b.tr().lon()
-              << " maxlat=" << b.tr().lat() << std::endl;
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_mpdump.cpp b/examples/osmium_mpdump.cpp
new file mode 100644
index 0000000..1dc9cce
--- /dev/null
+++ b/examples/osmium_mpdump.cpp
@@ -0,0 +1,90 @@
+/*
+
+  Create multipolygons and dump them to stdout.
+
+  The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
+
+#include <osmium.hpp>
+#include <osmium/storage/byid/sparse_table.hpp>
+#include <osmium/storage/byid/mmap_file.hpp>
+#include <osmium/handler/coordinates_for_ways.hpp>
+#include <osmium/multipolygon/assembler.hpp>
+#include <osmium/geometry/multipolygon.hpp>
+
+/* ================================================== */
+
+class DumpHandler : public Osmium::Handler::Base {
+
+public:
+
+    DumpHandler() : Osmium::Handler::Base() {
+    }
+
+    void area(const shared_ptr<Osmium::OSM::Area const>& area) {
+        Osmium::Geometry::MultiPolygon multipolygon(*area);
+
+        std::cout << "Area " << (area->from_way() ? "from way" : "from relation")
+                  << " id=" << area->id() << " (orig_id=" << area->orig_id() << ")"
+                  << " version=" << area->version()
+                  << " timestamp=" << area->timestamp()
+                  << " uid=" << area->uid()
+                  << " user=" << area->user()
+                  << "\n  " << multipolygon.as_WKT() << "\n";
+
+        BOOST_FOREACH(const Osmium::OSM::Tag& tag, area->tags()) {
+            std::cout << "  " << tag.key() << "=" << tag.value() << "\n";
+        }
+
+        std::cout << "\n";
+    }
+
+};
+
+/* ================================================== */
+
+typedef Osmium::Storage::ById::SparseTable<Osmium::OSM::Position> storage_sparsetable_t;
+typedef Osmium::Storage::ById::MmapFile<Osmium::OSM::Position> storage_mmap_t;
+
+int main(int argc, char* argv[]) {
+    std::ios_base::sync_with_stdio(false);
+
+    if (argc != 2) {
+        std::cerr << "Usage: " << argv[0] << " OSMFILE" << std::endl;
+        exit(1);
+    }
+
+    Osmium::OSMFile infile(argv[1]);
+
+    bool attempt_repair = true;
+
+    storage_sparsetable_t store_pos;
+    storage_mmap_t store_neg;
+
+    DumpHandler dump_handler;
+
+    typedef Osmium::MultiPolygon::Assembler<DumpHandler> assembler_t;
+    assembler_t assembler(dump_handler, attempt_repair);
+    assembler.set_debug_level(1);
+
+    typedef Osmium::Handler::CoordinatesForWays<storage_sparsetable_t, storage_mmap_t> cfw_handler_t;
+    cfw_handler_t cfw_handler(store_pos, store_neg);
+
+    typedef Osmium::Handler::Sequence<cfw_handler_t, assembler_t::HandlerPass2> sequence_handler_t;
+    sequence_handler_t sequence_handler(cfw_handler, assembler.handler_pass2());
+
+    std::cerr << "First pass...\n";
+    Osmium::Input::read(infile, assembler.handler_pass1());
+
+    std::cerr << "Second pass...\n";
+    Osmium::Input::read(infile, sequence_handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_progress.cpp b/examples/osmium_progress.cpp
index 5710ff6..41266d0 100644
--- a/examples/osmium_progress.cpp
+++ b/examples/osmium_progress.cpp
@@ -2,39 +2,21 @@
 
   This is a small tool demonstrates the use of the progress handler.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+  The code in this example file is released into the Public Domain.
 
 */
 
-#include <cstdlib>
+#include <iostream>
+
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
 
 #include <osmium.hpp>
 #include <osmium/handler/progress.hpp>
 
 /* ================================================== */
 
-int main(int argc, char *argv[]) {
-    Osmium::init(true);
-
+int main(int argc, char* argv[]) {
     if (argc != 2) {
         std::cerr << "Usage: " << argv[0] << " OSMFILE" << std::endl;
         exit(1);
@@ -42,6 +24,8 @@ int main(int argc, char *argv[]) {
 
     Osmium::OSMFile infile(argv[1]);
     Osmium::Handler::Progress handler;
-    infile.read(handler);
+    Osmium::Input::read(infile, handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_range_from_history.cpp b/examples/osmium_range_from_history.cpp
index d3dc842..3384b15 100644
--- a/examples/osmium_range_from_history.cpp
+++ b/examples/osmium_range_from_history.cpp
@@ -2,37 +2,23 @@
 
   Program to test the RangeFromHistory handler.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
+  The code in this example file is released into the Public Domain.
 
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
+*/
 
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+#include <iostream>
 
-*/
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
 
 #include <osmium.hpp>
+#include <osmium/output/xml.hpp>
+#include <osmium/output/pbf.hpp>
 #include <osmium/handler/debug.hpp>
 #include <osmium/handler/endtime.hpp>
 #include <osmium/handler/range_from_history.hpp>
 
-int main(int argc, char *argv[]) {
-    Osmium::init();
-
+int main(int argc, char* argv[]) {
     if (argc != 3) {
         std::cerr << "Usage: " << argv[0] << " INFILE OUTFILE\n";
         exit(1);
@@ -40,8 +26,12 @@ int main(int argc, char *argv[]) {
 
     Osmium::OSMFile infile(argv[1]);
     Osmium::OSMFile outfile(argv[2]);
-    Osmium::Handler::RangeFromHistory<Osmium::Output::Base> range_handler(outfile.create_output_file(), time(0), time(0));
-    Osmium::Handler::EndTime<Osmium::Handler::RangeFromHistory<Osmium::Output::Base> > handler(&range_handler);
-    infile.read(handler);
+
+    Osmium::Output::Handler out(outfile);
+    Osmium::Handler::RangeFromHistory<Osmium::Output::Handler> range_handler(out, time(0), time(0));
+    Osmium::Handler::EndTime<Osmium::Handler::RangeFromHistory<Osmium::Output::Handler> > handler(range_handler);
+    Osmium::Input::read(infile, handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_relation_members.cpp b/examples/osmium_relation_members.cpp
new file mode 100644
index 0000000..507a548
--- /dev/null
+++ b/examples/osmium_relation_members.cpp
@@ -0,0 +1,109 @@
+/*
+
+  Shows how the Osmium::Relations::Assembler class is used. Collects
+  all members of all relations in the input data and dumps the tags of
+  all relations and all their members to stdout.
+
+  The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
+
+#include <osmium.hpp>
+#include <osmium/relations/assembler.hpp>
+
+class DebugRelationsAssembler : public Osmium::Relations::Assembler<DebugRelationsAssembler, Osmium::Relations::RelationInfo, true, true, true> {
+
+    typedef Osmium::Relations::Assembler<DebugRelationsAssembler, Osmium::Relations::RelationInfo, true, true, true> AssemblerType;
+
+    /**
+     * Dump information about a relation with all its members to stdout.
+     */
+    void dump_relation(const Osmium::Relations::RelationInfo& relation_info, bool complete) const {
+        std::cout << "Relation " << relation_info.relation()->id() << (complete ? "\n" : " (INCOMPLETE)\n");
+        BOOST_FOREACH(const Osmium::OSM::Tag& tag, relation_info.relation()->tags()) {
+            std::cout << "  " << tag.key() << "=" << tag.value() << "\n";
+        }
+
+        int i = 0;
+        const Osmium::OSM::RelationMemberList& rml = relation_info.relation()->members();
+        BOOST_FOREACH(const Osmium::OSM::RelationMember& rm, rml) {
+            std::cout << "  [" << i << "] Member ";
+            switch (rm.type()) {
+                case 'n':
+                    std::cout << "node";
+                    break;
+                case 'w':
+                    std::cout << "way";
+                    break;
+                case 'r':
+                    std::cout << "relation";
+                    break;
+            }
+            std::cout << " " << rm.ref() << " with role '" << rm.role() << "'";
+            if (relation_info.members()[i]) {
+                std::cout << "\n";
+                BOOST_FOREACH(const Osmium::OSM::Tag& tag, relation_info.members()[i]->tags()) {
+                    std::cout << "      " << tag.key() << "=" << tag.value() << "\n";
+                }
+            } else {
+                std::cout << " (NOT IN INPUT FILE)\n";
+            }
+            ++i;
+        }
+        std::cout << "\n";
+    }
+
+public:
+
+    DebugRelationsAssembler() :
+        AssemblerType() {
+    }
+
+    void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
+        add_relation(Osmium::Relations::RelationInfo(relation));
+    }
+
+    bool keep_member(Osmium::Relations::RelationInfo& /*relation_info*/, const Osmium::OSM::RelationMember& /*member*/) {
+        return true;
+    }
+
+    void complete_relation(Osmium::Relations::RelationInfo& relation_info) {
+        dump_relation(relation_info, true);
+    }
+
+    void all_members_available() {
+        AssemblerType::clean_assembled_relations();
+        BOOST_FOREACH(const Osmium::Relations::RelationInfo& relation_info, AssemblerType::relations()) {
+            dump_relation(relation_info, false);
+        }
+    }
+
+};
+
+int main(int argc, char* argv[]) {
+    std::ios_base::sync_with_stdio(false);
+
+    if (argc != 2) {
+        std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
+        exit(1);
+    }
+
+    DebugRelationsAssembler assembler;
+
+    Osmium::OSMFile infile(argv[1]);
+
+    std::cout << "First pass (reading relations)..." << std::endl;
+    Osmium::Input::read(infile, assembler.handler_pass1());
+    std::cout << "Used memory: " << assembler.used_memory() / (1024 * 1024) << " MB" << std::endl;
+
+    std::cout << "Second pass (reading members)..." << std::endl;
+    Osmium::Input::read(infile, assembler.handler_pass2());
+
+    google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_sizeof.cpp b/examples/osmium_sizeof.cpp
index 6cdd0f0..1261324 100644
--- a/examples/osmium_sizeof.cpp
+++ b/examples/osmium_sizeof.cpp
@@ -3,46 +3,30 @@
   This is a small tool to find out the sizes of some basic classes.
   It is only used for Osmium development.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+  The code in this example file is released into the Public Domain.
 
 */
 
-#include <cstdlib>
+#include <iostream>
+#include <osmpbf/osmpbf.h>
 
-#include <osmium.hpp>
-#include <osmium/output/xml.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/area.hpp>
+#include <osmium/relations/relation_info.hpp>
+#include <osmium/relations/assembler.hpp>
 
 int main() {
-    Osmium::init();
-
     std::cout << "sizeof(Osmium::OSM::Object)="           << sizeof(Osmium::OSM::Object) << "\n";
     std::cout << "sizeof(Osmium::OSM::Node)="             << sizeof(Osmium::OSM::Node)             << "  (Object+" << sizeof(Osmium::OSM::Node)             - sizeof(Osmium::OSM::Object) << ")\n";
     std::cout << "sizeof(Osmium::OSM::Way)="              << sizeof(Osmium::OSM::Way)              << "  (Object+" << sizeof(Osmium::OSM::Way)              - sizeof(Osmium::OSM::Object) << ")\n";
     std::cout << "sizeof(Osmium::OSM::Relation)="         << sizeof(Osmium::OSM::Relation)         << "  (Object+" << sizeof(Osmium::OSM::Relation)         - sizeof(Osmium::OSM::Object) << ")\n";
     std::cout << "sizeof(Osmium::OSM::Area)="             << sizeof(Osmium::OSM::Area)             << "  (Object+" << sizeof(Osmium::OSM::Area)             - sizeof(Osmium::OSM::Object) << ")\n";
-    std::cout << "sizeof(Osmium::OSM::AreaFromWay)="      << sizeof(Osmium::OSM::AreaFromWay)      << "  (Object+" << sizeof(Osmium::OSM::AreaFromWay)      - sizeof(Osmium::OSM::Object) << ")\n";
-    std::cout << "sizeof(Osmium::OSM::AreaFromRelation)=" << sizeof(Osmium::OSM::AreaFromRelation) << "  (Object+" << sizeof(Osmium::OSM::AreaFromRelation) - sizeof(Osmium::OSM::Object) << ")\n";
-//    std::cout << "sizeof(Osmium::OSM::WayInfo)="                  << sizeof(Osmium::OSM::WayInfo) << "\n";
-//    std::cout << "sizeof(Osmium::OSM::RingInfo)="                 << sizeof(Osmium::OSM::RingInfo) << "\n";
+
+    std::cout << "sizeof(Osmium::Relations::RelationInfo)=" << sizeof(Osmium::Relations::RelationInfo) << "\n";
+    std::cout << "sizeof(Osmium::Relations::MemberInfo)="   << sizeof(Osmium::Relations::MemberInfo) << "\n";
 
     std::cout << "sizeof(OSMPBF::BlobHeader)="     << sizeof(OSMPBF::BlobHeader)     << "\n";
     std::cout << "sizeof(OSMPBF::Blob)="           << sizeof(OSMPBF::Blob)           << "\n";
@@ -54,8 +38,5 @@ int main() {
     std::cout << "sizeof(OSMPBF::Relation)="       << sizeof(OSMPBF::Relation)       << "\n";
     std::cout << "sizeof(OSMPBF::DenseNodes)="     << sizeof(OSMPBF::DenseNodes)     << "\n";
     std::cout << "sizeof(OSMPBF::StringTable)="    << sizeof(OSMPBF::StringTable)    << "\n";
-
-    std::cout << "sizeof(Osmium::Output::PBF)="    << sizeof(Osmium::Output::PBF)    << "\n";
-    std::cout << "sizeof(Osmium::Output::XML)="    << sizeof(Osmium::Output::XML)    << "\n";
 }
 
diff --git a/examples/osmium_store_and_debug.cpp b/examples/osmium_store_and_debug.cpp
index 97aaf3e..1491002 100644
--- a/examples/osmium_store_and_debug.cpp
+++ b/examples/osmium_store_and_debug.cpp
@@ -1,42 +1,22 @@
 /*
 
-  This is a small tool to dump the contents of the input file.
+  This example program shows how to read an OSM change file and
+  apply it to an OSM file. The results are dumped to stdout.
 
-  If OSMIUM_DEBUG_WITH_ENDTIME is defined when compiling, the
-  Osmium::Handler::EndTime is used.
+  The code in this example file is released into the Public Domain.
 
 */
 
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
 
 #include <osmium.hpp>
 #include <osmium/handler/debug.hpp>
 #include <osmium/storage/objectstore.hpp>
 
-int main(int argc, char *argv[]) {
-    Osmium::init(true);
-
+int main(int argc, char* argv[]) {
     if (argc != 3) {
-        std::cerr << "Usage: " << argv[0] << " OSMFILE1 OSMFILE2\n";
+        std::cerr << "Usage: " << argv[0] << " OSM-CHANGE-FILE OSM-FILE\n";
         exit(1);
     }
 
@@ -44,11 +24,13 @@ int main(int argc, char *argv[]) {
     Osmium::OSMFile infile2(argv[2]);
 
     Osmium::Storage::ObjectStore os;
-    infile1.read(os);
+    Osmium::Input::read(infile1, os);
 
     Osmium::Handler::Debug debug;
     Osmium::OSM::Meta meta;
-    Osmium::Storage::ObjectStore::ApplyHandler<Osmium::Handler::Debug> ah(os, &debug, meta);
-    infile2.read(ah);
+    Osmium::Storage::ObjectStore::ApplyHandler<Osmium::Handler::Debug> ah(os, debug, meta);
+    Osmium::Input::read(infile2, ah);
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_time.cpp b/examples/osmium_time.cpp
index cef881c..25c47f7 100644
--- a/examples/osmium_time.cpp
+++ b/examples/osmium_time.cpp
@@ -2,68 +2,60 @@
 
   This is a small tool to time osmium.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+  The code in this example file is released into the Public Domain.
 
 */
 
+// Set this if you want to report user and system time, too.
+// This will not work on Windows systems.
+//#define REPORT_USER_AND_SYSTEM_TIME
+
 #include <cstdlib>
 #include <time.h>
-#include <sys/times.h>
+
+#ifdef REPORT_USER_AND_SYSTEM_TIME
+# include <sys/times.h>
+#endif
+
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
 
 #include <osmium.hpp>
 
 class MyTimerHandler : public Osmium::Handler::Base {
 
-    uint64_t m_nodes;
-    uint64_t m_ways;
-    uint64_t m_relations;
-
 public:
 
     MyTimerHandler() : m_nodes(0), m_ways(0), m_relations(0) {
     }
 
-    void node(const shared_ptr<Osmium::OSM::Node const>& /*node*/) {
+    void node(const shared_ptr<Osmium::OSM::Node const>&) {
         m_nodes++;
     }
 
-    void way(const shared_ptr<Osmium::OSM::Way const>& /*way*/) {
+    void way(const shared_ptr<Osmium::OSM::Way const>&) {
         m_ways++;
     }
 
-    void relation(const shared_ptr<Osmium::OSM::Relation const>& /*relation*/) {
+    void relation(const shared_ptr<Osmium::OSM::Relation const>&) {
         m_relations++;
     }
 
     void final() {
         std::cout << "nodes: " << m_nodes << "  ways: " << m_ways << "  relations: " << m_relations << std::endl;
     }
+
+private:
+
+    uint64_t m_nodes;
+    uint64_t m_ways;
+    uint64_t m_relations;
+
 };
 
 /* ================================================== */
 
-int main(int argc, char *argv[]) {
-    Osmium::init(true);
-
+int main(int argc, char* argv[]) {
     time_t t0 = time(NULL);
 
     if (argc != 2) {
@@ -73,10 +65,17 @@ int main(int argc, char *argv[]) {
 
     Osmium::OSMFile infile(argv[1]);
     MyTimerHandler handler;
-    infile.read(handler);
+    Osmium::Input::read(infile, handler);
+
+    std::cout << "wallclock time: " << time(NULL) - t0 << "s" << std::endl;
 
+#ifdef REPORT_USER_AND_SYSTEM_TIME
     struct tms tms;
     times(&tms);
-    std::cout << "user time: " << ((double)tms.tms_utime) / sysconf(_SC_CLK_TCK) << "s   system time: " << ((double)tms.tms_stime) / sysconf(_SC_CLK_TCK) << "s  wallclock time: " << time(NULL) - t0 << "s" << std::endl;
+    std::cout << "user time:      " << (static_cast<double>(tms.tms_utime) / sysconf(_SC_CLK_TCK)) << "s" << std::endl;
+    std::cout << "system time:    " << (static_cast<double>(tms.tms_stime) / sysconf(_SC_CLK_TCK)) << "s" << std::endl;
+#endif
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_to_postgis.cpp b/examples/osmium_to_postgis.cpp
new file mode 100644
index 0000000..108bafc
--- /dev/null
+++ b/examples/osmium_to_postgis.cpp
@@ -0,0 +1,183 @@
+/*
+
+  This is an example tool that loads OSM data into a PostGIS
+  database with hstore tags column using the OGR library.
+
+  The database must have the HSTORE and POSTGIS extentions
+  loaded.
+
+  The code in this example file is released into the Public Domain.
+
+*/
+
+#include <getopt.h>
+#include <iostream>
+#include <numeric>
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <ogr_api.h>
+#include <ogrsf_frmts.h>
+
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
+
+#include <osmium.hpp>
+#include <osmium/utils/filter_and_accumulate.hpp>
+#include <osmium/tags/key_filter.hpp>
+#include <osmium/tags/to_string.hpp>
+#include <osmium/geometry/point.hpp>
+#include <osmium/geometry/ogr.hpp>
+
+class MyOGRHandler : public Osmium::Handler::Base {
+
+    OGRDataSource* m_data_source;
+    OGRLayer* m_layer_point;
+    Osmium::Tags::KeyFilter m_filter;
+    Osmium::Tags::TagToHStoreStringOp m_tohstore;
+
+public:
+
+    MyOGRHandler(const std::string& filename) :
+        m_data_source(NULL),
+        m_layer_point(NULL),
+        m_filter(true),
+        m_tohstore() {
+        OGRRegisterAll();
+
+        OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("PostgreSQL");
+        if (driver == NULL) {
+            std::cerr << "PostgreSQL OGR driver not available.\n";
+            exit(1);
+        }
+
+        // using COPY is much faster than INSERT
+        CPLSetConfigOption("PG_USE_COPY", "YES");
+        const char* options[] = { NULL };
+        m_data_source = driver->CreateDataSource(filename.c_str(), const_cast<char**>(options));
+        if (m_data_source == NULL) {
+            std::cerr << "Database open failed.\n";
+            exit(1);
+        }
+
+        // OGR can't create a table with hstore column, so we do it ourselves here
+        OGRLayer* dummy = m_data_source->ExecuteSQL("CREATE TABLE nodes (id VARCHAR, tags hstore);", NULL, NULL);
+        if (dummy) {
+            m_data_source->ReleaseResultSet(dummy);
+        }
+        dummy = m_data_source->ExecuteSQL("SELECT AddGeometryColumn('nodes', 'geom', 4326, 'POINT', 2);", NULL, NULL);
+        if (dummy) {
+            m_data_source->ReleaseResultSet(dummy);
+        }
+
+        m_layer_point = m_data_source->GetLayerByName("nodes");
+        if (!m_layer_point) {
+            std::cerr << "Something went wrong setting up the 'nodes' layer.\n";
+            exit(1);
+        }
+
+        // using transactions makes this much faster than without
+        m_layer_point->StartTransaction();
+
+        m_filter.add(false, "created_by");
+        m_filter.add(false, "odbl");
+    }
+
+    ~MyOGRHandler() {
+        OGRDataSource::DestroyDataSource(m_data_source);
+        OGRCleanupAll();
+    }
+
+    void node(const shared_ptr<Osmium::OSM::Node const>& node) {
+        if (!node->tags().empty()) {
+            std::string tags = Osmium::filter_and_accumulate(node->tags(), m_filter, std::string(), m_tohstore);
+
+            if (!tags.empty()) {
+                try {
+                    Osmium::Geometry::Point point(*node);
+
+                    OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn());
+                    OGRPoint* ogrpoint = Osmium::Geometry::create_ogr_geometry(point);
+                    feature->SetGeometry(ogrpoint);
+                    feature->SetField("id", boost::lexical_cast<std::string>(node->id()).c_str());
+                    feature->SetField("tags", tags.c_str());
+
+                    if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) {
+                        std::cerr << "Failed to create feature.\n";
+                        exit(1);
+                    }
+
+                    OGRFeature::DestroyFeature(feature);
+                    delete ogrpoint;
+                } catch (Osmium::Geometry::IllegalGeometry) {
+                    std::cerr << "Ignoring illegal geometry for node " << node->id() << ".\n";
+                }
+            }
+        }
+    }
+
+    void after_nodes() {
+        m_layer_point->CommitTransaction();
+    }
+
+};
+
+/* ================================================== */
+
+void print_help() {
+    std::cout << "osmium_to_postgis [OPTIONS] INFILE DATABASE\n\n" \
+              << "\nOptions:\n" \
+              << "  -h, --help           This help message\n" \
+              << "  -d, --debug          Enable debugging output\n";
+}
+
+int main(int argc, char* argv[]) {
+    static struct option long_options[] = {
+        {"debug",  no_argument, 0, 'd'},
+        {"help",   no_argument, 0, 'h'},
+        {0, 0, 0, 0}
+    };
+
+    bool debug = false;
+
+    while (true) {
+        int c = getopt_long(argc, argv, "dh", long_options, 0);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            case 'd':
+                debug = true;
+                break;
+            case 'h':
+                print_help();
+                exit(0);
+            default:
+                exit(1);
+        }
+    }
+
+    std::string input_filename;
+    std::string database_descriptor("PG:dbname=");
+    int remaining_args = argc - optind;
+    if (remaining_args != 2) {
+        std::cerr << "Usage: " << argv[0] << " [OPTIONS] INFILE DATABASE" << std::endl;
+        exit(1);
+    } else {
+        input_filename = argv[optind];
+        database_descriptor += argv[optind+1];
+    }
+
+    if (debug) {
+        std::cout << "Reading file '" << input_filename << "'\nOpening database '" << database_descriptor << "'\n";
+    }
+
+    Osmium::OSMFile infile(input_filename);
+    MyOGRHandler handler(database_descriptor);
+    Osmium::Input::read(infile, handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_toogr.cpp b/examples/osmium_toogr.cpp
index b4ad32c..885a3bd 100644
--- a/examples/osmium_toogr.cpp
+++ b/examples/osmium_toogr.cpp
@@ -1,42 +1,30 @@
 /*
 
-  This is an example tool that converts OSM data to a spatialite database using
-  the OGR library.
+  This is an example tool that converts OSM data to some output format
+  like Spatialite or Shapefiles using the OGR library.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+  The code in this example file is released into the Public Domain.
 
 */
 
-#include <cstdlib>
+#include <iostream>
+#include <getopt.h>
 
+#include <ogr_api.h>
 #include <ogrsf_frmts.h>
 
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
+
 #include <osmium.hpp>
-#include <osmium/storage/byid.hpp>
+#include <osmium/storage/byid/sparse_table.hpp>
+#include <osmium/storage/byid/mmap_file.hpp>
 #include <osmium/handler/coordinates_for_ways.hpp>
 #include <osmium/geometry/point.hpp>
+#include <osmium/geometry/ogr.hpp>
 
-typedef Osmium::Storage::SparseTable<Osmium::OSM::Position> storage_sparsetable_t;
-typedef Osmium::Storage::Mmap<Osmium::OSM::Position> storage_mmap_t;
+typedef Osmium::Storage::ById::SparseTable<Osmium::OSM::Position> storage_sparsetable_t;
+typedef Osmium::Storage::ById::MmapFile<Osmium::OSM::Position> storage_mmap_t;
 typedef Osmium::Handler::CoordinatesForWays<storage_sparsetable_t, storage_mmap_t> cfw_handler_t;
 
 class MyOGRHandler : public Osmium::Handler::Base {
@@ -51,13 +39,12 @@ class MyOGRHandler : public Osmium::Handler::Base {
 
 public:
 
-    MyOGRHandler() {
+    MyOGRHandler(const std::string& driver_name, const std::string& filename) {
         handler_cfw = new cfw_handler_t(store_pos, store_neg);
 
         OGRRegisterAll();
 
-        const char* driver_name = "SQLite";
-        OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name);
+        OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str());
         if (driver == NULL) {
             std::cerr << driver_name << " driver not available.\n";
             exit(1);
@@ -65,23 +52,24 @@ public:
 
         CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE");
         const char* options[] = { "SPATIALITE=TRUE", NULL };
-        m_data_source = driver->CreateDataSource("ogr_out.sqlite", const_cast<char**>(options));
+        m_data_source = driver->CreateDataSource(filename.c_str(), const_cast<char**>(options));
         if (m_data_source == NULL) {
             std::cerr << "Creation of output file failed.\n";
             exit(1);
         }
 
-        OGRSpatialReference sparef("EPSG:4326");
+        OGRSpatialReference sparef;
+        sparef.SetWellKnownGeogCS("WGS84");
         m_layer_point = m_data_source->CreateLayer("postboxes", &sparef, wkbPoint, NULL);
         if (m_layer_point == NULL) {
             std::cerr << "Layer creation failed.\n";
             exit(1);
         }
 
-        OGRFieldDefn layer_point_field_id("id", OFTInteger);
+        OGRFieldDefn layer_point_field_id("id", OFTReal);
         layer_point_field_id.SetWidth(10);
 
-        if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE ) {
+        if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) {
             std::cerr << "Creating id field failed.\n";
             exit(1);
         }
@@ -89,37 +77,44 @@ public:
         OGRFieldDefn layer_point_field_operator("operator", OFTString);
         layer_point_field_operator.SetWidth(30);
 
-        if (m_layer_point->CreateField(&layer_point_field_operator) != OGRERR_NONE ) {
+        if (m_layer_point->CreateField(&layer_point_field_operator) != OGRERR_NONE) {
             std::cerr << "Creating operator field failed.\n";
             exit(1);
         }
 
+        /* Transactions might make things faster, then again they might not.
+           Feel free to experiment and benchmark and report back. */
+//        m_layer_point->StartTransaction();
+
         m_layer_linestring = m_data_source->CreateLayer("roads", &sparef, wkbLineString, NULL);
         if (m_layer_linestring == NULL) {
             std::cerr << "Layer creation failed.\n";
             exit(1);
         }
 
-        OGRFieldDefn layer_linestring_field_id("id", OFTInteger);
+        OGRFieldDefn layer_linestring_field_id("id", OFTReal);
         layer_linestring_field_id.SetWidth(10);
 
-        if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE ) {
+        if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) {
             std::cerr << "Creating id field failed.\n";
             exit(1);
         }
 
-        OGRFieldDefn layer_linestring_field_operator("type", OFTString);
-        layer_linestring_field_operator.SetWidth(30);
+        OGRFieldDefn layer_linestring_field_type("type", OFTString);
+        layer_linestring_field_type.SetWidth(30);
 
-        if (m_layer_linestring->CreateField(&layer_linestring_field_operator) != OGRERR_NONE ) {
-            std::cerr << "Creating operator field failed.\n";
+        if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) {
+            std::cerr << "Creating type field failed.\n";
             exit(1);
         }
+
+//        m_layer_linestring->StartTransaction();
     }
 
     ~MyOGRHandler() {
         OGRDataSource::DestroyDataSource(m_data_source);
         delete handler_cfw;
+        OGRCleanupAll();
     }
 
     void init(Osmium::OSM::Meta& meta) {
@@ -128,16 +123,16 @@ public:
 
     void node(const shared_ptr<Osmium::OSM::Node const>& node) {
         handler_cfw->node(node);
-        const char* amenity = node->tags().get_tag_by_key("amenity");
+        const char* amenity = node->tags().get_value_by_key("amenity");
         if (amenity && !strcmp(amenity, "post_box")) {
             try {
                 Osmium::Geometry::Point point(*node);
 
                 OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn());
-                OGRPoint* ogrpoint = point.create_ogr_geometry();
+                OGRPoint* ogrpoint = Osmium::Geometry::create_ogr_geometry(point);
                 feature->SetGeometry(ogrpoint);
-                feature->SetField("id", node->id());
-                feature->SetField("operator", node->tags().get_tag_by_key("operator"));
+                feature->SetField("id", static_cast<double>(node->id()));
+                feature->SetField("operator", node->tags().get_value_by_key("operator"));
 
                 if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) {
                     std::cerr << "Failed to create feature.\n";
@@ -146,27 +141,33 @@ public:
 
                 OGRFeature::DestroyFeature(feature);
                 delete ogrpoint;
-            } catch (Osmium::Exception::IllegalGeometry) {
+            } catch (Osmium::Geometry::IllegalGeometry) {
                 std::cerr << "Ignoring illegal geometry for node " << node->id() << ".\n";
             }
         }
     }
 
     void after_nodes() {
+//        m_layer_point->CommitTransaction();
+        std::cerr << "Memory used for node coordinates storage (approximate):\n  for positive IDs: "
+                  << store_pos.used_memory() / (1024 * 1024)
+                  << " MiB\n  for negative IDs: "
+                  << store_neg.used_memory() / (1024 * 1024)
+                  << " MiB\n";
         handler_cfw->after_nodes();
     }
 
     void way(const shared_ptr<Osmium::OSM::Way>& way) {
         handler_cfw->way(way);
-        const char* highway = way->tags().get_tag_by_key("highway");
+        const char* highway = way->tags().get_value_by_key("highway");
         if (highway) {
             try {
                 Osmium::Geometry::LineString linestring(*way);
 
                 OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn());
-                OGRLineString* ogrlinestring = linestring.create_ogr_geometry();
+                OGRLineString* ogrlinestring = Osmium::Geometry::create_ogr_geometry(linestring);
                 feature->SetGeometry(ogrlinestring);
-                feature->SetField("id", way->id());
+                feature->SetField("id", static_cast<double>(way->id()));
                 feature->SetField("type", highway);
 
                 if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) {
@@ -176,25 +177,75 @@ public:
 
                 OGRFeature::DestroyFeature(feature);
                 delete ogrlinestring;
-            } catch (Osmium::Exception::IllegalGeometry) {
+            } catch (Osmium::Geometry::IllegalGeometry) {
                 std::cerr << "Ignoring illegal geometry for way " << way->id() << ".\n";
             }
         }
     }
+
+//    void after_ways() {
+//        m_layer_linestring->CommitTransaction();
+//    }
+
 };
 
 /* ================================================== */
 
-int main(int argc, char *argv[]) {
-    Osmium::init(true);
+void print_help() {
+    std::cout << "osmium_toogr [OPTIONS] [INFILE [OUTFILE]]\n\n" \
+              << "If INFILE is not given stdin is assumed.\n" \
+              << "If OUTFILE is not given 'ogr_out' is used.\n" \
+              << "\nOptions:\n" \
+              << "  -h, --help           This help message\n" \
+              << "  -f, --format=FORMAT  Output OGR format (Default: 'SQLite')\n";
+}
+
+int main(int argc, char* argv[]) {
+    static struct option long_options[] = {
+        {"help",   no_argument, 0, 'h'},
+        {"format", required_argument, 0, 'f'},
+        {0, 0, 0, 0}
+    };
+
+    std::string output_format("SQLite");
 
-    if (argc != 2) {
-        std::cerr << "Usage: " << argv[0] << " OSMFILE" << std::endl;
+    while (true) {
+        int c = getopt_long(argc, argv, "hf:", long_options, 0);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            case 'h':
+                print_help();
+                exit(0);
+            case 'f':
+                output_format = optarg;
+                break;
+            default:
+                exit(1);
+        }
+    }
+
+    std::string input_filename;
+    std::string output_filename("ogr_out");
+    int remaining_args = argc - optind;
+    if (remaining_args > 2) {
+        std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl;
         exit(1);
+    } else if (remaining_args == 2) {
+        input_filename =  argv[optind];
+        output_filename = argv[optind+1];
+    } else if (remaining_args == 1) {
+        input_filename =  argv[optind];
+    } else {
+        input_filename = "-";
     }
 
-    Osmium::OSMFile infile(argv[1]);
-    MyOGRHandler handler;
-    infile.read(handler);
+    Osmium::OSMFile infile(input_filename);
+    MyOGRHandler handler(output_format, output_filename);
+    Osmium::Input::read(infile, handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_toogr2.cpp b/examples/osmium_toogr2.cpp
index e168972..8a78e7d 100644
--- a/examples/osmium_toogr2.cpp
+++ b/examples/osmium_toogr2.cpp
@@ -5,88 +5,37 @@
 
   This version creates multipolygons and reads the input file twice to do that.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+  The code in this example file is released into the Public Domain.
 
 */
 
-#include <cstdlib>
+#include <iostream>
 
+#include <ogr_api.h>
 #include <ogrsf_frmts.h>
 
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
+
 #include <osmium.hpp>
-#include <osmium/storage/byid.hpp>
+#include <osmium/storage/byid/sparse_table.hpp>
+#include <osmium/storage/byid/mmap_file.hpp>
 #include <osmium/handler/coordinates_for_ways.hpp>
-#include <osmium/handler/multipolygon.hpp>
+#include <osmium/multipolygon/assembler.hpp>
 #include <osmium/geometry/multipolygon.hpp>
-
-typedef Osmium::Storage::SparseTable<Osmium::OSM::Position> storage_sparsetable_t;
-typedef Osmium::Storage::Mmap<Osmium::OSM::Position> storage_mmap_t;
-typedef Osmium::Handler::CoordinatesForWays<storage_sparsetable_t, storage_mmap_t> cfw_handler_t;
-
-class MyOGRHandlerPass1 : public Osmium::Handler::Base {
-
-    Osmium::Handler::Multipolygon* handler_multipolygon;
-
-public:
-
-    MyOGRHandlerPass1(Osmium::Handler::Multipolygon* hmp) : handler_multipolygon(hmp) {
-    }
-
-    ~MyOGRHandlerPass1() {
-    }
-
-    void before_relations() {
-        handler_multipolygon->before_relations();
-    }
-
-    void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
-        handler_multipolygon->relation(relation);
-    }
-
-    void after_relations() {
-        handler_multipolygon->after_relations();
-        std::cerr << "1st pass finished" << std::endl;
-    }
-
-};
+#include <osmium/geometry/ogr.hpp>
+#include <osmium/geometry/ogr_multipolygon.hpp>
 
 /* ================================================== */
 
-class MyOGRHandlerPass2 : public Osmium::Handler::Base {
+class OGROutHandler : public Osmium::Handler::Base {
 
     OGRDataSource* m_data_source;
     OGRLayer* m_layer_mp;
 
-    storage_sparsetable_t store_pos;
-    storage_mmap_t store_neg;
-    cfw_handler_t* handler_cfw;
-
-    Osmium::Handler::Multipolygon* handler_multipolygon;
-
 public:
 
-    MyOGRHandlerPass2(Osmium::Handler::Multipolygon* hmp) : handler_multipolygon(hmp) {
-        handler_cfw = new cfw_handler_t(store_pos, store_neg);
-
+    OGROutHandler() : Osmium::Handler::Base() {
         OGRRegisterAll();
 
         const char* driver_name = "SQLite";
@@ -104,7 +53,8 @@ public:
             exit(1);
         }
 
-        OGRSpatialReference sparef("EPSG:4326");
+        OGRSpatialReference sparef;
+        sparef.SetWellKnownGeogCS("WGS84");
         m_layer_mp = m_data_source->CreateLayer("areas", &sparef, wkbMultiPolygon, NULL);
         if (m_layer_mp == NULL) {
             std::cerr << "Layer creation failed.\n";
@@ -114,44 +64,28 @@ public:
         OGRFieldDefn layer_mp_field_id("id", OFTInteger);
         layer_mp_field_id.SetWidth(10);
 
-        if (m_layer_mp->CreateField(&layer_mp_field_id) != OGRERR_NONE ) {
+        if (m_layer_mp->CreateField(&layer_mp_field_id) != OGRERR_NONE) {
             std::cerr << "Creating id field failed.\n";
             exit(1);
         }
     }
 
-    ~MyOGRHandlerPass2() {
+    ~OGROutHandler() {
         OGRDataSource::DestroyDataSource(m_data_source);
-        delete handler_cfw;
-    }
-
-    void init(Osmium::OSM::Meta& meta) {
-        handler_cfw->init(meta);
-    }
-
-    void node(const shared_ptr<Osmium::OSM::Node const>& node) {
-        handler_cfw->node(node);
-    }
-
-    void after_nodes() {
-        handler_cfw->after_nodes();
+        OGRCleanupAll();
     }
 
-    void way(const shared_ptr<Osmium::OSM::Way>& way) {
-        handler_cfw->way(way);
-        handler_multipolygon->way(way);
-    }
-
-    void area(Osmium::OSM::Area* area) {
-        const char* building = area->tags().get_tag_by_key("building");
+    void area(const shared_ptr<Osmium::OSM::Area const>& area) {
+        const char* building = area->tags().get_value_by_key("building");
         if (building) {
             try {
                 Osmium::Geometry::MultiPolygon mp(*area);
 
                 OGRFeature* feature = OGRFeature::CreateFeature(m_layer_mp->GetLayerDefn());
-                OGRMultiPolygon* ogrmp = mp.create_ogr_geometry();
+                OGRMultiPolygon* ogrmp = Osmium::Geometry::create_ogr_geometry(mp);
                 feature->SetGeometry(ogrmp);
-                feature->SetField("id", area->id());
+                // there are not so many multipolygon relations, so these ids will still fit into a 32bit int
+                feature->SetField("id", static_cast<int>(area->id()));
 
                 if (m_layer_mp->CreateFeature(feature) != OGRERR_NONE) {
                     std::cerr << "Failed to create feature.\n";
@@ -160,7 +94,7 @@ public:
 
                 OGRFeature::DestroyFeature(feature);
                 delete ogrmp;
-            } catch (Osmium::Exception::IllegalGeometry) {
+            } catch (Osmium::Geometry::IllegalGeometry) {
                 std::cerr << "Ignoring illegal geometry for multipolygon " << area->id() << ".\n";
             }
         }
@@ -168,17 +102,12 @@ public:
 
 };
 
-MyOGRHandlerPass2* hpass2;
-
 /* ================================================== */
 
-void cbmp(Osmium::OSM::Area* area) {
-    hpass2->area(area);
-}
-
-int main(int argc, char *argv[]) {
-    Osmium::init(true);
+typedef Osmium::Storage::ById::SparseTable<Osmium::OSM::Position> storage_sparsetable_t;
+typedef Osmium::Storage::ById::MmapFile<Osmium::OSM::Position> storage_mmap_t;
 
+int main(int argc, char* argv[]) {
     if (argc != 2) {
         std::cerr << "Usage: " << argv[0] << " OSMFILE" << std::endl;
         exit(1);
@@ -187,16 +116,29 @@ int main(int argc, char *argv[]) {
     Osmium::OSMFile infile(argv[1]);
 
     bool attempt_repair = true;
-    Osmium::Handler::Multipolygon handler_multipolygon(attempt_repair, cbmp);
 
-    // first pass
-    MyOGRHandlerPass1 handler_pass1(&handler_multipolygon);
-    infile.read(handler_pass1);
+    storage_sparsetable_t store_pos;
+    storage_mmap_t store_neg;
+
+    OGROutHandler ogr_out_handler;
+
+    typedef Osmium::MultiPolygon::Assembler<OGROutHandler> assembler_t;
+    assembler_t assembler(ogr_out_handler, attempt_repair);
+    assembler.set_debug_level(1);
+
+    typedef Osmium::Handler::CoordinatesForWays<storage_sparsetable_t, storage_mmap_t> cfw_handler_t;
+    cfw_handler_t cfw_handler(store_pos, store_neg);
+
+    typedef Osmium::Handler::Sequence<cfw_handler_t, assembler_t::HandlerPass2> sequence_handler_t;
+    sequence_handler_t sequence_handler(cfw_handler, assembler.handler_pass2());
+
+    std::cerr << "First pass...\n";
+    Osmium::Input::read(infile, assembler.handler_pass1());
+    std::cout << "Used memory: " << assembler.used_memory() / (1024 * 1024) << " MB" << std::endl;
 
-    // second pass
-    MyOGRHandlerPass2 handler_pass2(&handler_multipolygon);
-    hpass2 = &handler_pass2;
+    std::cerr << "Second pass...\n";
+    Osmium::Input::read(infile, sequence_handler);
 
-    infile.read(handler_pass2);
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/examples/osmium_toshape.cpp b/examples/osmium_toshape.cpp
index 608995b..d33c8d2 100644
--- a/examples/osmium_toshape.cpp
+++ b/examples/osmium_toshape.cpp
@@ -2,39 +2,24 @@
 
   This is an example tool that converts OSM data to a shapefile.
 
-*/
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
+  The code in this example file is released into the Public Domain.
 
 */
 
-#include <cstdlib>
+#include <iostream>
+
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
 
 #include <osmium.hpp>
-#include <osmium/storage/byid.hpp>
+#include <osmium/storage/byid/sparse_table.hpp>
+#include <osmium/storage/byid/mmap_file.hpp>
 #include <osmium/handler/coordinates_for_ways.hpp>
 #include <osmium/geometry/point.hpp>
 #include <osmium/export/shapefile.hpp>
 
-typedef Osmium::Storage::SparseTable<Osmium::OSM::Position> storage_sparsetable_t;
-typedef Osmium::Storage::Mmap<Osmium::OSM::Position> storage_mmap_t;
+typedef Osmium::Storage::ById::SparseTable<Osmium::OSM::Position> storage_sparsetable_t;
+typedef Osmium::Storage::ById::MmapFile<Osmium::OSM::Position> storage_mmap_t;
 typedef Osmium::Handler::CoordinatesForWays<storage_sparsetable_t, storage_mmap_t> cfw_handler_t;
 
 class MyShapeHandler : public Osmium::Handler::Base {
@@ -51,10 +36,10 @@ public:
     MyShapeHandler() {
         handler_cfw = new cfw_handler_t(store_pos, store_neg);
         shapefile_point = new Osmium::Export::PointShapefile("postboxes");
-        shapefile_point->add_field("id", FTInteger, 10);
+        shapefile_point->add_field("id", FTDouble, 12);
         shapefile_point->add_field("operator", FTString, 30);
         shapefile_linestring = new Osmium::Export::LineStringShapefile("roads");
-        shapefile_linestring->add_field("id", FTInteger, 10);
+        shapefile_linestring->add_field("id", FTDouble, 12);
         shapefile_linestring->add_field("type", FTString, 30);
     }
 
@@ -69,17 +54,17 @@ public:
 
     void node(const shared_ptr<Osmium::OSM::Node const>& node) {
         handler_cfw->node(node);
-        const char* amenity = node->tags().get_tag_by_key("amenity");
+        const char* amenity = node->tags().get_value_by_key("amenity");
         if (amenity && !strcmp(amenity, "post_box")) {
             try {
                 Osmium::Geometry::Point point(*node);
-                shapefile_point->add_geometry(point.create_shp_object());
-                shapefile_point->add_attribute(0, node->id());
-                const char* op = node->tags().get_tag_by_key("operator");
+                shapefile_point->add_geometry(Osmium::Geometry::create_shp_object(point));
+                shapefile_point->add_attribute(0, static_cast<double>(node->id()));
+                const char* op = node->tags().get_value_by_key("operator");
                 if (op) {
-                    shapefile_point->add_attribute(1, std::string(op));
+                    shapefile_point->add_attribute_with_truncate(1, std::string(op));
                 }
-            } catch (Osmium::Exception::IllegalGeometry) {
+            } catch (Osmium::Geometry::IllegalGeometry) {
                 std::cerr << "Ignoring illegal geometry for node " << node->id() << ".\n";
             }
         }
@@ -91,14 +76,14 @@ public:
 
     void way(const shared_ptr<Osmium::OSM::Way>& way) {
         handler_cfw->way(way);
-        const char* highway = way->tags().get_tag_by_key("highway");
+        const char* highway = way->tags().get_value_by_key("highway");
         if (highway) {
             try {
                 Osmium::Geometry::LineString linestring(*way);
-                shapefile_linestring->add_geometry(linestring.create_shp_object());
-                shapefile_linestring->add_attribute(0, way->id());
-                shapefile_linestring->add_attribute(1, std::string(highway));
-            } catch (Osmium::Exception::IllegalGeometry) {
+                shapefile_linestring->add_geometry(Osmium::Geometry::create_shp_object(linestring));
+                shapefile_linestring->add_attribute(0, static_cast<double>(way->id()));
+                shapefile_linestring->add_attribute_with_truncate(1, std::string(highway));
+            } catch (Osmium::Geometry::IllegalGeometry) {
                 std::cerr << "Ignoring illegal geometry for way " << way->id() << ".\n";
             }
         }
@@ -107,9 +92,7 @@ public:
 
 /* ================================================== */
 
-int main(int argc, char *argv[]) {
-    Osmium::init(true);
-
+int main(int argc, char* argv[]) {
     if (argc != 2) {
         std::cerr << "Usage: " << argv[0] << " OSMFILE" << std::endl;
         exit(1);
@@ -117,6 +100,8 @@ int main(int argc, char *argv[]) {
 
     Osmium::OSMFile infile(argv[1]);
     MyShapeHandler handler;
-    infile.read(handler);
+    Osmium::Input::read(infile, handler);
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/get_options.sh b/get_options.sh
new file mode 100755
index 0000000..32661cc
--- /dev/null
+++ b/get_options.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+#  get_options.sh --cflags|--libs FILE...
+#
+
+if [ "x$1" = "x--cflags" ]; then
+    macro=OSMIUM_COMPILE_WITH_CFLAGS_
+    shift
+elif [ "x$1" = "x--libs" ]; then
+    macro=OSMIUM_LINK_WITH_LIBS_
+    shift
+else
+    echo "Usage: $0 --cflags|--libs FILE..."
+    exit 1
+fi
+
+(for input in $*; do
+    cpp -xc++ -dD -E -Iinclude -I../include $input 2>/dev/null | grep $macro | cut -d' ' -f3-
+done) | sort -u | xargs echo
+
diff --git a/include/osmium.hpp b/include/osmium.hpp
index b47840d..74817e3 100644
--- a/include/osmium.hpp
+++ b/include/osmium.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,6 +22,14 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#ifdef OSMIUM_WITH_PBF_INPUT
+# include <osmium/input/pbf.hpp>
+#endif
+
+#ifdef OSMIUM_WITH_XML_INPUT
+# include <osmium/input/xml.hpp>
+#endif
+
 /**
  * @mainpage
  *
@@ -36,90 +44,39 @@ You should have received a copy of the Licenses along with Osmium. If not, see
  * source code is at https://github.com/joto/osmium .
  */
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-# include <v8.h>
-# include <unicode/ustring.h>
-# include <osmium/utils/unicode.hpp>
-#endif // OSMIUM_WITH_JAVASCRIPT
-
-#include <osmpbf/osmpbf.h>
-
 /**
  * @brief All %Osmium code is in this namespace.
  */
 namespace Osmium {
 
-    /**
-     * Internal class to manage global state.
-     */
-    class Framework {
-
-        Framework(bool d) : debug(d) {
-        }
-
-        ~Framework() {
-            // this is needed even if the protobuf lib was never used so that valgrind doesn't report any errors
-            google::protobuf::ShutdownProtobufLibrary();
+#if defined(OSMIUM_WITH_PBF_INPUT) || defined(OSMIUM_WITH_XML_INPUT)
+    namespace Input {
+
+        template <class T>
+        inline void read(const Osmium::OSMFile& file, T& handler) {
+            Osmium::Input::Base<T>* input = NULL;
+
+            if (file.encoding()->is_pbf()) {
+#ifdef OSMIUM_WITH_PBF_INPUT
+                input = static_cast<Osmium::Input::Base<T>*>(new Osmium::Input::PBF<T>(file, handler));
+#else
+                throw Osmium::OSMFile::FileEncodingNotSupported();
+#endif // OSMIUM_WITH_PBF_INPUT
+            } else {
+#ifdef OSMIUM_WITH_XML_INPUT
+                input = static_cast<Osmium::Input::Base<T>*>(new Osmium::Input::XML<T>(file, handler));
+#else
+                throw Osmium::OSMFile::FileEncodingNotSupported();
+#endif // OSMIUM_WITH_XML_INPUT
+            }
+
+            input->parse();
+            delete input;
         }
 
-        bool debug;
-
-        friend Framework& init(bool debug);
-        friend void set_debug(bool d);
-        friend bool debug();
-
-    }; // class Framework
-
-    /**
-     * Initialize the Osmium library. Call this before using any of the Osmium
-     * functions.
-     *
-     * @param debug Enable or disable the debugging output.
-     */
-    Framework& init(bool debug=false) {
-        static Framework f(debug);
-        return f;
-    }
-
-    /**
-     * Enable or disable the debugging output.
-     */
-    void set_debug(bool d) {
-        init().debug = d;
-    }
-
-    /**
-     * Is debugging output set?
-     */
-    bool debug() {
-        return init().debug;
-    }
+    } // namespace Input
+#endif
 
 } // namespace Osmium
 
-// check way geometry before making a shplib object from it
-// normally this should be defined, otherwise you will generate invalid linestring geometries
-#define OSMIUM_CHECK_WAY_GEOMETRY
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-# include <osmium/javascript/template.hpp>
-#endif // OSMIUM_WITH_JAVASCRIPT
-
-#include <osmium/exceptions.hpp>
-#include <osmium/osm.hpp>
-#include <osmium/geometry/null.hpp>
-#include <osmium/geometry/point.hpp>
-#include <osmium/geometry/linestring.hpp>
-#include <osmium/geometry/polygon.hpp>
-#include <osmium/geometry/multipolygon.hpp>
-#include <osmium/osmfile.hpp>
-#include <osmium/input.hpp>
-#include <osmium/output.hpp>
-#include <osmium/export.hpp>
-#include <osmium/osmfile_impl.hpp>
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-# include <osmium/HandlerJavascript.hpp>
-#endif // OSMIUM_WITH_JAVASCRIPT
-
 #endif // OSMIUM_OSMIUM_HPP
diff --git a/include/osmium/CGAlgorithms.h b/include/osmium/CGAlgorithms.h
deleted file mode 100644
index f87bf86..0000000
--- a/include/osmium/CGAlgorithms.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/**********************************************************************
- * $Id: CGAlgorithms.h 1820 2006-09-06 16:54:23Z mloskot $
- *
- * GEOS - Geometry Engine Open Source
- * http://geos.refractions.net
- *
- * Copyright (C) 2005-2006 Refractions Research Inc.
- * Copyright (C) 2001-2002 Vivid Solutions Inc.
- *
- * This is free software; you can redistribute and/or modify it under
- * the terms of the GNU Lesser General Public Licence as published
- * by the Free Software Foundation. 
- * See the COPYING file for more information.
- *
- **********************************************************************
- *
- * Last port: algorithm/CGAlgorithms.java rev. 1.34 (JTS-1.7.1)
- *
- **********************************************************************/
-
-#ifndef GEOS_ALGORITHM_CGALGORITHM_H
-#define GEOS_ALGORITHM_CGALGORITHM_H
-
-#include <vector>
-
-// Forward declarations
-namespace geos {
-	namespace geom {
-		class Coordinate;
-		class CoordinateSequence;
-	}
-}
-
-
-namespace geos {
-namespace algorithm { // geos::algorithm
-
-/**
- * \brief
- * Specifies and implements various fundamental Computational Geometric
- * algorithms.
- * The algorithms supplied in this class are robust for double-precision
- * floating point.
- *
- */
-class CGAlgorithms {
-
-public:
-
-	enum {
-		CLOCKWISE=-1,
-		COLLINEAR,
-		COUNTERCLOCKWISE
-	};
-
-	enum {
-		RIGHT=-1,
-		LEFT,
-		STRAIGHT
-	};
-
-	CGAlgorithms(){};
-
-	/** \brief
-	 * Test whether a point lies inside a ring.
-	 *
-	 * The ring may be oriented in either direction.
-	 * If the point lies on the ring boundary the result
-	 * of this method is unspecified.
-	 * 
-	 * This algorithm does not attempt to first check the
-	 * point against the envelope of the ring.
-	 *
-	 * @param p point to check for ring inclusion
-	 * @param ring assumed to have first point identical to last point
-	 * @return <code>true</code> if p is inside ring
-	 */
-	static bool isPointInRing(const geom::Coordinate& p,
-			const geom::CoordinateSequence* ring);
-
-	/// Same as above, but taking a vector of const Coordinates (faster)
-	static bool isPointInRing(const geom::Coordinate& p,
-			const std::vector<const geom::Coordinate*>& ring);
-
-	/** \brief
-	 * Test whether a point lies on a linestring.
-	 *
-	 * @return true true if
-	 * the point is a vertex of the line or lies in the interior of a line
-	 * segment in the linestring
-	 */
-	static bool isOnLine(const geom::Coordinate& p,
-		const geom::CoordinateSequence* pt);
-
-	/** \brief
-	 * Computes whether a ring defined by an array of Coordinate is
-	 * oriented counter-clockwise.
-	 * 
-	 *  - The list of points is assumed to have the first and last
-	 *    points equal.
-	 *  - This will handle coordinate lists which contain repeated points.
-	 *
-	 * This algorithm is <b>only</b> guaranteed to work with valid rings.
-	 * If the ring is invalid (e.g. self-crosses or touches),
-	 * the computed result <b>may</b> not be correct.
-	 *
-	 * @param ring an array of coordinates forming a ring
-	 * @return <code>true</code> if the ring is oriented counter-clockwise.
-	 */
-	static bool isCCW(const geom::CoordinateSequence* ring);
-
-	/** \brief
-	 * Computes the orientation of a point q to the directed line
-	 * segment p1-p2.
-	 *
-	 * The orientation of a point relative to a directed line
-	 * segment indicates which way you turn to get to q after
-	 * travelling from p1 to p2.
-	 *
-	 * @return 1 if q is counter-clockwise from p1-p2
-	 * @return -1 if q is clockwise from p1-p2
-	 * @return 0 if q is collinear with p1-p2
-	 */
-	static int computeOrientation(const geom::Coordinate& p1,
-			const geom::Coordinate& p2,
-			const geom::Coordinate& q);
-
-	/** \brief
-	 * Computes the distance from a point p to a line segment AB
-	 *
-	 * Note: NON-ROBUST!
-	 *
-	 * @param p the point to compute the distance for
-	 * @param A one point of the line
-	 * @param B another point of the line (must be different to A)
-	 * @return the distance from p to line segment AB
-	 */
-	static double distancePointLine(const geom::Coordinate& p,
-			const geom::Coordinate& A,
-			const geom::Coordinate& B);
-
-	/** \brief
-	 * Computes the perpendicular distance from a point p
-	 * to the (infinite) line containing the points AB
-	 *
-	 * @param p the point to compute the distance for
-	 * @param A one point of the line
-	 * @param B another point of the line (must be different to A)
-	 * @return the distance from p to line AB
-	 */
-	static double distancePointLinePerpendicular(const geom::Coordinate& p,
-			const geom::Coordinate& A,
-			const geom::Coordinate& B);
-
-	/** \brief
-	 * Computes the distance from a line segment AB to a line segment CD
-	 *
-	 * Note: NON-ROBUST!
-	 *
-	 * @param A a point of one line
-	 * @param B the second point of  (must be different to A)
-	 * @param C one point of the line
-	 * @param D another point of the line (must be different to A)
-	 */
-	static double distanceLineLine(const geom::Coordinate& A,
-			const geom::Coordinate& B,
-			const geom::Coordinate& C,
-			const geom::Coordinate& D);
-
-	/** \brief
-	 * Returns the signed area for a ring.  The area is positive if
-	 * the ring is oriented CW.
-	 */
-	static double signedArea(const geom::CoordinateSequence* ring);
-
-	/** \brief
-	 * Computes the length of a linestring specified by a sequence
-	 * of points.
-	 *
-	 * @param pts the points specifying the linestring
-	 * @return the length of the linestring
-	 */
-	static double length(const geom::CoordinateSequence* pts);
-
-	/** \brief
-	 * Returns the index of the direction of the point <code>q</code>
-	 * relative to a vector specified by <code>p1-p2</code>.
-	 *
-	 * @param p1 the origin point of the vector
-	 * @param p2 the final point of the vector
-	 * @param q the point to compute the direction to
-	 *
-	 * @return 1 if q is counter-clockwise (left) from p1-p2
-	 * @return -1 if q is clockwise (right) from p1-p2
-	 * @return 0 if q is collinear with p1-p2
-	 */
-	static int orientationIndex(const geom::Coordinate& p1,
-			const geom::Coordinate& p2,
-			const geom::Coordinate& q);
-
-};
-
-} // namespace geos::algorithm
-} // namespace geos
-
-#endif // GEOS_ALGORITHM_CGALGORITHM_H
-
-/**********************************************************************
- * $Log$
- * Revision 1.2  2006/05/02 14:51:53  strk
- * Added port info and fixed doxygen comments for CGAlgorithms class
- *
- * Revision 1.1  2006/03/09 16:46:48  strk
- * geos::geom namespace definition, first pass at headers split
- *
- **********************************************************************/
-
diff --git a/include/osmium/debug.hpp b/include/osmium/debug.hpp
new file mode 100644
index 0000000..9f600d8
--- /dev/null
+++ b/include/osmium/debug.hpp
@@ -0,0 +1,73 @@
+#ifndef OSMIUM_DEBUG_HPP
+#define OSMIUM_DEBUG_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+namespace Osmium {
+
+    /**
+     * This class can be used as a base class for classes that need
+     * debugging support.
+     *
+     * To set the debug level: set_debug_level(LEVEL);
+     *
+     * Check whether debugging is wanted: if (debug && has_debug_level(LEVEL)) ...
+     *
+     * The const "debug" is set to "true" or "false" depending on whether
+     * Osmium was compiled with or without debugging support, so the debugging
+     * code can be optimized out.
+     */
+    class WithDebug {
+
+        int m_debug_level;
+
+    protected:
+
+#ifdef OSMIUM_WITH_DEBUG
+        static const bool debug = true;
+#else
+        static const bool debug = false;
+#endif // OSMIUM_WITH_DEBUG
+
+    public:
+
+        WithDebug() :
+            m_debug_level(0) {
+        }
+
+        // Destructor is not virtual as this class is not intended to be used polymorphically
+        ~WithDebug() {
+        }
+
+        bool has_debug_level(int debug_level) const {
+            return m_debug_level >= debug_level;
+        }
+
+        void set_debug_level(int debug_level) {
+            m_debug_level = debug_level;
+        }
+
+    }; // class WithDebug
+
+} // namespace Osmium
+
+#endif // OSMIUM_DEBUG_HPP
diff --git a/include/osmium/export.hpp b/include/osmium/export.hpp
index 9302cc3..4ed2710 100644
--- a/include/osmium/export.hpp
+++ b/include/osmium/export.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -32,9 +32,4 @@ namespace Osmium {
 
 } // namespace Osmium
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-# include <osmium/export/csv.hpp>
-# include <osmium/export/shapefile.hpp>
-#endif
-
 #endif // OSMIUM_EXPORT_HPP
diff --git a/include/osmium/export/csv.hpp b/include/osmium/export/csv.hpp
index eb0ad65..77c4921 100644
--- a/include/osmium/export/csv.hpp
+++ b/include/osmium/export/csv.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -24,10 +24,6 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 #include <fstream>
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-# include <v8.h>
-#endif // OSMIUM_WITH_JAVASCRIPT
-
 namespace Osmium {
 
     namespace Export {
@@ -38,8 +34,8 @@ namespace Osmium {
 
             std::ofstream out;
 
-            CSV(const char *filename) {
-                out.open(filename);
+            CSV(const char* filename) :
+                out(filename) {
             }
 
             ~CSV() {
@@ -47,38 +43,6 @@ namespace Osmium {
                 out.close();
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void *)this);
-            }
-
-            v8::Handle<v8::Value> js_print(const v8::Arguments& args) {
-                for (int i = 0; i < args.Length(); i++) {
-                    if (i != 0) {
-                        out << '\t';
-                    }
-                    Osmium::v8_String_to_ostream(args[i]->ToString(), out);
-                }
-                out << std::endl;
-                return v8::Integer::New(1);
-            }
-
-            v8::Handle<v8::Value> js_close(const v8::Arguments& /*args*/) {
-                out.flush();
-                out.close();
-                return v8::Undefined();
-            }
-
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->Set("print", v8::FunctionTemplate::New(function_template<CSV, &CSV::js_print>));
-                    js_template->Set("close", v8::FunctionTemplate::New(function_template<CSV, &CSV::js_close>));
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         }; // class CSV
 
     } // namespace Export
diff --git a/include/osmium/export/shapefile.hpp b/include/osmium/export/shapefile.hpp
index 9d660a3..772d9e2 100644
--- a/include/osmium/export/shapefile.hpp
+++ b/include/osmium/export/shapefile.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,31 +22,57 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#ifdef OSMIUM_WITH_SHPLIB
-
 #include <fstream>
 #include <sstream>
+#include <cerrno>
+#include <stdint.h>
 #include <shapefil.h>
 #include <boost/utility.hpp>
 
+#include <osmium/geometry/shplib.hpp>
+
 namespace Osmium {
 
     namespace Export {
 
         class Shapefile : boost::noncopyable {
 
+        public:
+
             // the following limits are defined by the shapefile spec
-            static const unsigned int max_dbf_fields            =  16;
             static const unsigned int max_dbf_field_name_length =  11;
             static const          int max_dbf_field_length      = 255;
 
+            // this limit has been arrived at experimentally
+            static const unsigned int max_dbf_fields = 2047;
+
+            // shape files with more than 2 GB don't work
+            static const unsigned int max_file_size = 2147483647;
+
+            // size of a shape file header
+            static const size_t size_shapefile_header = 100;
+
+            // size of a DBF file header
+            static const size_t size_dbf_header = 33;
+
+            // size of a DBF field descriptor
+            static const size_t size_dbf_field_header = 32;
+
+        private:
+
             class Field {
 
             public:
 
-                Field(const std::string& name, DBFFieldType type, int width=1, int decimals=0) : m_name(name), m_type(type), m_width(width), m_decimals(decimals) {
+                Field(const std::string& name, DBFFieldType type, int width=1, int decimals=0) :
+                    m_name(name),
+                    m_type(type),
+                    m_width(width),
+                    m_decimals(decimals) {
                     if (name == "" || name.size() > max_dbf_field_name_length) {
                         throw std::invalid_argument("field name must be between 1 and 11 characters long");
+                    } else if (width > max_dbf_field_length) {
+                        throw std::invalid_argument("field width must not exceed 255 characters");
                     }
                 }
 
@@ -98,12 +124,14 @@ namespace Osmium {
             void add_field(Field& field) {
                 if (m_fields.size() < max_dbf_fields) {
                     int field_num = DBFAddField(m_dbf_handle, field.name().c_str(), field.type(), field.width(), field.decimals());
-                    if (field_num != (int)m_fields.size()) {
+                    if (field_num != static_cast<int>(m_fields.size())) {
                         throw std::runtime_error("Failed to add field:" + field.name());
                     }
                     m_fields.push_back(field);
+                    m_dbf_bytes += size_dbf_field_header;
+                    m_record_length += field.width();
                 } else {
-                    throw std::out_of_range("Can't have more than 16 fields in a shapefile.");
+                    throw std::out_of_range("Too many fields in the shapefile.");
                 }
             }
 
@@ -148,6 +176,14 @@ namespace Osmium {
                 add_field(name, ftype, width, decimals);
             }
 
+            const std::vector<Field>& fields() const {
+                return m_fields;
+            }
+
+            const Field& field(int n) const {
+                return m_fields[n];
+            }
+
             /**
              * Add a new geometry (shape object) to the Shapefile. You have to call
              * this first for every new shape. After that you call add_attribute()
@@ -155,22 +191,26 @@ namespace Osmium {
              *
              * @param shp_object A pointer to the shape object to be added. The object
              *                   will be freed for you by calling SHPDestroyObject()!
-             * @exception Osmium::Exception::IllegalGeometry If shp_object is NULL or
+             * @exception Osmium::Geometry::IllegalGeometry If shp_object is NULL or
              *                   the type of geometry does not fit the type of the
              *                   shapefile.
              */
             void add_geometry(SHPObject* shp_object) {
                 if (!shp_object || shp_object->nSHPType != m_shp_handle->nShapeType) {
-                    throw Osmium::Exception::IllegalGeometry();
+                    throw Osmium::Geometry::IllegalGeometry();
                 }
-                m_current_shape = SHPWriteObject(m_shp_handle, -1, shp_object);
-                if (m_current_shape == -1 && errno == EINVAL) {
-                    // second chance if likely cause is having reached the 2GB limit
+                m_dbf_bytes += m_record_length;
+                m_shp_bytes += length_on_disk(shp_object);
+
+                if (m_dbf_bytes > max_file_size || m_shp_bytes > max_file_size) {
                     close();
                     m_sequence_number++;
                     open();
-                    m_current_shape = SHPWriteObject(m_shp_handle, -1, shp_object);
+                    m_dbf_bytes += m_record_length;
+                    m_shp_bytes += length_on_disk(shp_object);
                 }
+
+                m_current_shape = SHPWriteObject(m_shp_handle, -1, shp_object);
                 if (m_current_shape == -1) {
                     throw std::runtime_error("error writing to shapefile");
                 }
@@ -180,43 +220,50 @@ namespace Osmium {
             void add_attribute(const int field, const bool value) const {
                 int ok = DBFWriteLogicalAttribute(m_dbf_handle, m_current_shape, field, value ? 'T' : 'F');
                 if (!ok) {
-                    throw std::runtime_error(std::string("Can't add bool to field"));
+                    throw std::runtime_error("Can't add bool to field");
                 }
             }
 
             void add_attribute(const int field, const int value) const {
                 int ok = DBFWriteIntegerAttribute(m_dbf_handle, m_current_shape, field, value);
                 if (!ok) {
-                    throw std::runtime_error(std::string("Can't add integer to field"));
+                    throw std::runtime_error("Can't add integer to field");
+                }
+            }
+
+            void add_attribute(const int field, const double value) const {
+                int ok = DBFWriteDoubleAttribute(m_dbf_handle, m_current_shape, field, value);
+                if (!ok) {
+                    throw std::runtime_error("Can't add double to field");
                 }
             }
 
             void add_attribute(const int field, const std::string& value) const {
                 int ok = DBFWriteStringAttribute(m_dbf_handle, m_current_shape, field, value.c_str());
                 if (!ok) {
-                    throw std::runtime_error(std::string("Can't add string to field"));
+                    throw std::runtime_error("Can't add string to field");
                 }
             }
 
-            void add_attribute(const int field, const char *value) const {
+            void add_attribute(const int field, const char* value) const {
                 int ok = DBFWriteStringAttribute(m_dbf_handle, m_current_shape, field, value);
                 if (!ok) {
-                    throw std::runtime_error(std::string("Can't add char* to field"));
+                    throw std::runtime_error("Can't add char* to field");
                 }
             }
 
             void add_attribute(const int field) const {
                 int ok = DBFWriteNULLAttribute(m_dbf_handle, m_current_shape, field);
                 if (!ok) {
-                    throw std::runtime_error(std::string("Can't add null to field"));
+                    throw std::runtime_error("Can't add null to field");
                 }
             }
 
             // truncates UTF8 string to fit in shape field
-            void add_attribute_with_truncate(const int field, const char* value) {
+            void add_attribute_with_truncate(const int field, const char* value) const {
                 char dest[max_dbf_field_length+1];
                 size_t length = m_fields[field].width();
-                memset(dest, 0, length+1);
+                dest[length+1] = '\0';
                 strncpy(dest, value, length);
                 size_t i = length-1;
                 if (dest[i] & 128) {
@@ -231,156 +278,27 @@ namespace Osmium {
                 add_attribute(field, dest);
             }
 
-            void add_attribute_with_truncate(const int field, const std::string& value) {
+            void add_attribute_with_truncate(const int field, const std::string& value) const {
                 add_attribute_with_truncate(field, value.c_str());
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            int add_string_attribute(int n, v8::Local<v8::Value> value) const {
-                uint16_t source[(max_dbf_field_length+2)*2];
-                char dest[(max_dbf_field_length+1)*4];
-                memset(source, 0, (max_dbf_field_length+2)*4);
-                memset(dest, 0, (max_dbf_field_length+1)*4);
-                int32_t dest_length;
-                UErrorCode error_code = U_ZERO_ERROR;
-                value->ToString()->Write(source, 0, max_dbf_field_length+1);
-                u_strToUTF8(dest, m_fields[n].width(), &dest_length, source, std::min(max_dbf_field_length+1, value->ToString()->Length()), &error_code);
-                if (error_code == U_BUFFER_OVERFLOW_ERROR) {
-                    // thats ok, it just means we clip the text at that point
-                } else if (U_FAILURE(error_code)) {
-                    throw std::runtime_error("UTF-16 to UTF-8 conversion failed");
-                }
-                return DBFWriteStringAttribute(m_dbf_handle, m_current_shape, n, dest);
-            }
-
-            int add_logical_attribute(int n, v8::Local<v8::Value> value) const {
-                v8::String::Utf8Value str(value);
-
-                if (atoi(*str) == 1 || !strncasecmp(*str, "T", 1) || !strncasecmp(*str, "Y", 1)) {
-                    return DBFWriteLogicalAttribute(m_dbf_handle, m_current_shape, n, 'T');
-                } else if ((!strcmp(*str, "0")) || !strncasecmp(*str, "F", 1) || !strncasecmp(*str, "N", 1)) {
-                    return DBFWriteLogicalAttribute(m_dbf_handle, m_current_shape, n, 'F');
-                } else {
-                    return DBFWriteNULLAttribute(m_dbf_handle, m_current_shape, n);
-                }
-            }
-
-            /**
-            * Add a geometry to the shapefile.
-            */
-            bool add(Osmium::Geometry::Geometry* geometry, ///< the geometry
-                     v8::Local<v8::Object> attributes) {   ///< a %Javascript object (hash) with the attributes
-
-                try {
-                    add_geometry(geometry->create_shp_object());
-                } catch (Osmium::Exception::IllegalGeometry) {
-                    return false;
-                }
-
-                int ok = 0;
-                for (size_t n=0; n < m_fields.size(); n++) {
-                    v8::Local<v8::String> key = v8::String::New(m_fields[n].name().c_str());
-                    if (attributes->HasRealNamedProperty(key)) {
-                        v8::Local<v8::Value> value = attributes->GetRealNamedProperty(key);
-                        if (value->IsUndefined() || value->IsNull()) {
-                            DBFWriteNULLAttribute(m_dbf_handle, m_current_shape, n);
-                        } else {
-                            switch (m_fields[n].type()) {
-                                case FTString:
-                                    ok = add_string_attribute(n, value);
-                                    break;
-                                case FTInteger:
-                                    ok = DBFWriteIntegerAttribute(m_dbf_handle, m_current_shape, n, value->Int32Value());
-                                    break;
-                                case FTDouble:
-                                    throw std::runtime_error("fields of type double not implemented");
-                                    break;
-                                case FTLogical:
-                                    ok = add_logical_attribute(n, value);
-                                    break;
-                                default:
-                                    ok = 0; // should never be here
-                                    break;
-                            }
-                            if (!ok) {
-                                std::string errmsg("failed to add attribute '");
-                                errmsg += m_fields[n].name();
-                                errmsg += "'\n";
-                                throw std::runtime_error(errmsg);
-                            }
-                        }
-                    } else {
-                        DBFWriteNULLAttribute(m_dbf_handle, m_current_shape, n);
-                    }
-                }
-                return true;
-            }
-
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void*)this);
-            }
-
-            v8::Handle<v8::Value> js_add_field(const v8::Arguments& args) {
-                if (args.Length() < 3 || args.Length() > 4) {
-                    throw std::runtime_error("Wrong number of arguments to add_field method.");
-                }
-
-                v8::String::Utf8Value name(args[0]);
-                std::string sname(*name);
-
-                v8::String::Utf8Value type(args[1]);
-                std::string stype(*type);
-
-                int width = args[2]->Int32Value();
-                int decimals = (args.Length() == 4) ? args[3]->Int32Value() : 0;
-
-                add_field(sname, stype, width, decimals);
-
-                return v8::Integer::New(1);
-            }
-
-            v8::Handle<v8::Value> js_add(const v8::Arguments& args) {
-                if (args.Length() != 2) {
-                    throw std::runtime_error("Wrong number of arguments to add method.");
-                }
-
-                v8::Local<v8::Object> xxx = v8::Local<v8::Object>::Cast(args[0]);
-                Osmium::Geometry::Geometry* geometry = (Osmium::Geometry::Geometry*) v8::Local<v8::External>::Cast(xxx->GetInternalField(0))->Value();
-
-                try {
-                    add(geometry, v8::Local<v8::Object>::Cast(args[1]));
-                } catch (Osmium::Exception::IllegalGeometry) {
-                    std::cerr << "Ignoring object with illegal geometry." << std::endl;
-                    return v8::Integer::New(0);
-                }
-
-                return v8::Integer::New(1);
-            }
-
-            v8::Handle<v8::Value> js_close(const v8::Arguments& /*args*/) {
-                close();
-                return v8::Undefined();
-            }
-
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->Set("add_field", v8::FunctionTemplate::New(function_template<Shapefile, &Shapefile::js_add_field>));
-                    js_template->Set("add",       v8::FunctionTemplate::New(function_template<Shapefile, &Shapefile::js_add>));
-                    js_template->Set("close",     v8::FunctionTemplate::New(function_template<Shapefile, &Shapefile::js_close>));
-                }
-
-            };
-
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         protected:
 
             /**
              * The constructor for Shapefile is protected. Use one of
              * PointShapefile, LineShapefile, or PolygonShapefile.
              */
-            Shapefile(const std::string& filename, int type) : m_filename_base(filename), m_fields(), m_type(type), m_sequence_number(0) {
+            Shapefile(const std::string& filename, int type) :
+                m_filename_base(filename),
+                m_fields(),
+                m_shp_handle(NULL),
+                m_dbf_handle(NULL),
+                m_current_shape(0),
+                m_type(type),
+                m_sequence_number(0),
+                m_record_length(1),
+                m_shp_bytes(0), 
+                m_dbf_bytes(0) { 
                 open();
             }
 
@@ -403,7 +321,16 @@ namespace Osmium {
             int m_type;
 
             /// shapefile sequence number for auto-overflow (0=first)
-            int m_sequence_number;
+            unsigned int m_sequence_number;
+
+            /// number of bytes per DBF record
+            unsigned int m_record_length;
+
+            /// number of bytes written to SHP file
+            unsigned int m_shp_bytes;
+
+            /// number of bytes written to DBF file
+            unsigned int m_dbf_bytes;
 
             /**
              * Open and initialize all files belonging to shapefile (.shp/shx/dbf/prj/cpg).
@@ -445,6 +372,59 @@ namespace Osmium {
                 for (std::vector<Field>::const_iterator it = m_fields.begin(); it != m_fields.end(); ++it) {
                     DBFAddField(m_dbf_handle, it->name().c_str(), it->type(), it->width(), it->decimals());
                 }
+
+                m_shp_bytes = size_shapefile_header;
+                m_dbf_bytes = size_dbf_header + size_dbf_field_header * (m_fields.size());
+            }
+
+            /**
+             * Computes the number of bytes a shape will take up when saved to the .shp file.
+             *
+             * @param shp_object A pointer to the shape object we want to size up.
+             */
+            size_t length_on_disk(SHPObject* shp_object) const {
+                // sizes of various elements making up a shape record
+                const size_t size_record_header = 8;  // record header
+                const size_t size_type_field = 4;     // shape type identifier
+                const size_t size_num_points = 4;     // number of points
+                const size_t size_num_parts = 4;      // number of parts
+                const size_t size_box = 32;           // bounding box
+                const size_t size_range = 16;         // measurement or Z range
+                const size_t size_coordinate = 8;     // one coordinate
+                const size_t size_part_index = 4;     // pointer to shape part
+
+                switch(shp_object->nSHPType) {
+                    case SHPT_NULL:             return size_record_header + size_type_field;
+
+                    // standard shapes have 2-dimensional coordinates
+                    case SHPT_POINT:            return size_record_header + size_type_field + 2 * size_coordinate;
+                    case SHPT_MULTIPOINT:       return size_record_header + size_type_field + size_box + size_num_points + 
+                                                    shp_object->nVertices * 2 * size_coordinate;
+                    case SHPT_ARC:              // like polygon
+                    case SHPT_POLYGON:          return size_record_header + size_type_field + size_box + size_num_parts + 
+                                                    shp_object->nParts * size_part_index + size_num_points + shp_object->nVertices * 2 * size_coordinate;
+
+                    // "M" shapes have 3-dimensional coordinates (x/y/measurement)
+                    case SHPT_POINTM:           return size_record_header + size_type_field + 3 * size_coordinate;
+                    case SHPT_MULTIPOINTM:      return size_record_header + size_type_field + size_box + size_range + 
+                                                    size_num_points + shp_object->nVertices * 3 * size_coordinate;
+                    case SHPT_ARCM:             // like polygon
+                    case SHPT_POLYGONM:         return size_record_header + size_type_field + size_box + size_range + 
+                                                    size_num_parts + shp_object->nParts * size_part_index + size_num_points + shp_object->nVertices * 3 * size_coordinate;
+
+                    // "Z" shapes have 4-dimensional coordinates (x/y/z/measurement)
+                    case SHPT_POINTZ:           return size_record_header + size_type_field + 4 * size_coordinate;
+                    case SHPT_MULTIPOINTZ:      return size_record_header + size_type_field + size_box + 2 * size_range + 
+                                                    size_num_points + shp_object->nVertices * 4 * size_coordinate;
+                    case SHPT_ARCZ:             // like polygon
+                    case SHPT_POLYGONZ:         return size_record_header + size_type_field + size_box + 2 * size_range + size_num_parts + 
+                                                    shp_object->nParts * size_part_index + size_num_points + shp_object->nVertices * 4 * size_coordinate;
+
+                    case SHPT_MULTIPATCH:       return size_record_header + size_type_field + size_box + 2 * size_range + size_num_parts +
+                                                    shp_object->nParts * 2 * size_part_index + size_num_points + shp_object->nVertices * 4 * size_coordinate;
+                    default:
+                        throw std::runtime_error("unrecognized shape type");
+                }
             }
 
         }; // class Shapefile
@@ -461,7 +441,8 @@ namespace Osmium {
              *
              * @param filename Filename (optionally including path) without any suffix.
              */
-            PointShapefile(const std::string& filename) : Shapefile(filename, SHPT_POINT) {
+            PointShapefile(const std::string& filename) :
+                Shapefile(filename, SHPT_POINT) {
             }
 
         };
@@ -478,7 +459,8 @@ namespace Osmium {
              *
              * @param filename Filename (optionally including path) without any suffix.
              */
-            LineStringShapefile(const std::string& filename) : Shapefile(filename, SHPT_ARC) {
+            LineStringShapefile(const std::string& filename) :
+                Shapefile(filename, SHPT_ARC) {
             }
 
         };
@@ -495,7 +477,8 @@ namespace Osmium {
              *
              * @param filename Filename (optionally including path) without any suffix.
              */
-            PolygonShapefile(const std::string& filename) : Shapefile(filename, SHPT_POLYGON) {
+            PolygonShapefile(const std::string& filename) :
+                Shapefile(filename, SHPT_POLYGON) {
             }
 
         };
@@ -504,6 +487,4 @@ namespace Osmium {
 
 } // namespace Osmium
 
-#endif // OSMIUM_WITH_SHPLIB
-
 #endif // OSMIUM_EXPORT_SHAPEFILE_HPP
diff --git a/include/osmium/geometry.hpp b/include/osmium/geometry.hpp
index 28794dd..aa94923 100644
--- a/include/osmium/geometry.hpp
+++ b/include/osmium/geometry.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,22 +22,8 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <sstream>
+#include <ostream>
 
-#ifdef OSMIUM_WITH_GEOS
-# include <geos/geom/GeometryFactory.h>
-# include <geos/geom/PrecisionModel.h>
-#endif // OSMIUM_WITH_GEOS
-
-#ifdef OSMIUM_WITH_SHPLIB
-# include <shapefil.h>
-#endif // OSMIUM_WITH_SHPLIB
-
-#ifdef OSMIUM_WITH_OGR
-# include <ogr_geometry.h>
-#endif // OSMIUM_WITH_OGR
-
-#include <osmium/exceptions.hpp>
 #include <osmium/osm/types.hpp>
 
 namespace Osmium {
@@ -78,18 +64,15 @@ namespace Osmium {
             wkbNDR = 1          // Little Endian
         };
 
-#ifdef OSMIUM_WITH_GEOS
-        /**
-         * Return pointer to a static GEOS GeometryFactory object created the
-         * first time this function is run. This is used by all functions in
-         * Osmium that need to create GEOS geometries.
-         */
-        geos::geom::GeometryFactory* geos_geometry_factory() {
-            static geos::geom::PrecisionModel pm;
-            static geos::geom::GeometryFactory factory(&pm, -1);
-            return &factory;
-        }
-#endif // OSMIUM_WITH_GEOS
+        /// Base class for all geometry exception classes
+        struct GeometryException {};
+
+        struct RingNotClosed : public GeometryException {};
+
+        /// Exception thrown if there is no geometry available when there should be
+        struct NoGeometry : public GeometryException {};
+
+        struct IllegalGeometry : public GeometryException {};
 
         class Geometry;
 
@@ -134,7 +117,7 @@ namespace Osmium {
          *
          * @tparam T Type of value.
          */
-        template<typename T>
+        template <typename T>
         inline void write_binary(std::ostream& out, const T value) {
             out.write(reinterpret_cast<const char*>(&value), sizeof(T));
         }
@@ -144,7 +127,7 @@ namespace Osmium {
          *
          * @tparam T Type of value.
          */
-        template<typename T>
+        template <typename T>
         inline void write_hex(std::ostream& out, const T value) {
             static const char* lookup_hex = "0123456789ABCDEF";
             for (const char* in = reinterpret_cast<const char*>(&value); in < reinterpret_cast<const char*>(&value) + sizeof(T); ++in) {
@@ -196,7 +179,8 @@ namespace Osmium {
 
         public:
 
-            Geometry(osm_object_id_t id=0) : m_id(id) {
+            Geometry(osm_object_id_t id=0) :
+                m_id(id) {
             }
 
             virtual ~Geometry() {
@@ -234,54 +218,6 @@ namespace Osmium {
             /// Write geometry as hex encoded WKB to output stream.
             virtual std::ostream& write_to_stream(std::ostream& out, AsHexWKB, bool with_srid=false) const = 0;
 
-#ifdef  OSMIUM_WITH_SHPLIB
-            virtual SHPObject* create_shp_object() const {
-                return NULL;
-            }
-#endif // OSMIUM_WITH_SHPLIB
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Handle<v8::Value> js_to_wkt(const v8::Arguments& args) {
-                std::ostringstream oss;
-                bool with_srid = false;
-                if (args.Length() >= 1) {
-                    with_srid = args[0]->ToBoolean()->Value();
-                }
-                oss << this->as_WKT(with_srid);
-                return v8::String::New(oss.str().c_str());
-            }
-
-            v8::Handle<v8::Value> js_to_wkb(const v8::Arguments& args) {
-                std::ostringstream oss;
-                bool with_srid = false;
-                if (args.Length() >= 1) {
-                    with_srid = args[0]->ToBoolean()->Value();
-                }
-                oss << this->as_WKB(with_srid);
-                return v8::String::New(oss.str().c_str());
-            }
-
-            v8::Handle<v8::Value> js_to_hexwkb(const v8::Arguments& args) {
-                std::ostringstream oss;
-                bool with_srid = false;
-                if (args.Length() >= 1) {
-                    with_srid = args[0]->ToBoolean()->Value();
-                }
-                oss << this->as_HexWKB(with_srid);
-                return v8::String::New(oss.str().c_str());
-            }
-
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->Set("toWKT",    v8::FunctionTemplate::New(function_template<Geometry, &Geometry::js_to_wkt>));
-                    js_template->Set("toWKB",    v8::FunctionTemplate::New(function_template<Geometry, &Geometry::js_to_wkb>));
-                    js_template->Set("toHexWKB", v8::FunctionTemplate::New(function_template<Geometry, &Geometry::js_to_hexwkb>));
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         private:
 
             osm_object_id_t m_id;
@@ -304,10 +240,11 @@ namespace Osmium {
             LonLatListWriter(std::ostream& out,      ///< The output stream
                              char delim_lonlat=' ',  ///< The delimiter between longitude and latitude
                              char delim_items=',')   ///< The delimiter between consecutive coordinates
-                : m_out(out),
-                  m_delim_lonlat(delim_lonlat),
-                  m_delim_items(delim_items),
-                  m_first(true) {
+                :
+                m_out(out),
+                m_delim_lonlat(delim_lonlat),
+                m_delim_items(delim_items),
+                m_first(true) {
             }
 
             void operator()(const TLonLat& lonlat) {
@@ -322,8 +259,8 @@ namespace Osmium {
         private:
 
             std::ostream& m_out;
-            char m_delim_lonlat;
-            char m_delim_items;
+            const char m_delim_lonlat;
+            const char m_delim_items;
             bool m_first;
 
         }; // class LonLatListWriter
diff --git a/include/osmium/geometry/from_way.hpp b/include/osmium/geometry/from_way.hpp
index d30b1b3..4bf33cc 100644
--- a/include/osmium/geometry/from_way.hpp
+++ b/include/osmium/geometry/from_way.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -31,72 +31,33 @@ namespace Osmium {
 
         class FromWay : public Geometry {
 
+        public:
+
+            const Osmium::OSM::WayNodeList& nodes() const {
+                return m_way_node_list;
+            }
+
+            const Osmium::OSM::WayNode& operator[](int i) const {
+                return m_way_node_list[i];
+            }
+
+            bool reverse() const {
+                return m_reverse;
+            }
+
         protected:
 
             FromWay(const Osmium::OSM::WayNodeList& way_node_list,
                     bool reverse=false,
-                    osm_object_id_t id=0)
-                : Geometry(id),
-                  m_way_node_list(&way_node_list),
-                  m_reverse(reverse) {
+                    osm_object_id_t id=0) :
+                Geometry(id),
+                m_way_node_list(way_node_list),
+                m_reverse(reverse) {
             }
 
-#ifdef OSMIUM_WITH_SHPLIB
-            /**
-             * Create a SHPObject for this way and return it.
-             *
-             * Caller takes ownership. You have to call
-             * SHPDestroyObject() with this geometry when you are done.
-             */
-            SHPObject* create_line_or_polygon(int shp_type) const {
-                if (!m_way_node_list->has_position()) {
-                    throw std::runtime_error("node coordinates not available for building way geometry");
-                }
-                int size = m_way_node_list->size();
-                if (size == 0 || size == 1) {
-                    if (Osmium::debug()) {
-                        std::cerr << "error building way geometry for way " << id() << ": must at least contain two nodes" << std::endl;
-                    }
-                    throw Osmium::Exception::IllegalGeometry();
-                }
-
-                std::vector<double> lon_checked;
-                lon_checked.reserve(size);
-                lon_checked.push_back((*m_way_node_list)[0].position().lon());
-
-                std::vector<double> lat_checked;
-                lat_checked.reserve(size);
-                lat_checked.push_back((*m_way_node_list)[0].position().lat());
-
-                for (int i=1; i < size; i++) {
-                    if ((*m_way_node_list)[i] == (*m_way_node_list)[i-1]) {
-                        if (Osmium::debug()) {
-                            std::cerr << "warning building way geometry for way " << id() << ": contains node " << (*m_way_node_list)[i].ref() << " twice" << std::endl;
-                        }
-                    } else if ((*m_way_node_list)[i].position() == (*m_way_node_list)[i-1].position()) {
-                        if (Osmium::debug()) {
-                            std::cerr << "warning building way geometry for way " << id() << ": contains position " << (*m_way_node_list)[i].position() << " twice" << std::endl;
-                        }
-                    } else {
-                        lon_checked.push_back((*m_way_node_list)[i].position().lon());
-                        lat_checked.push_back((*m_way_node_list)[i].position().lat());
-                    }
-                }
-                if (lon_checked.size() == 1) {
-                    if (Osmium::debug()) {
-                        std::cerr << "error building way geometry for way " << id() << ": must at least contain two different points" << std::endl;
-                    }
-                    throw Osmium::Exception::IllegalGeometry();
-                }
-                if (m_reverse) {
-                    reverse(lon_checked.begin(), lon_checked.end());
-                    reverse(lat_checked.begin(), lat_checked.end());
-                }
-                return SHPCreateSimpleObject(shp_type, lon_checked.size(), &(lon_checked[0]), &(lat_checked[0]), NULL);
-            }
-#endif // OSMIUM_WITH_SHPLIB
+        private:
 
-            const Osmium::OSM::WayNodeList* m_way_node_list;
+            const Osmium::OSM::WayNodeList& m_way_node_list;
             const bool m_reverse;
 
         }; // class FromWay
diff --git a/include/osmium/geometry/geos.hpp b/include/osmium/geometry/geos.hpp
new file mode 100644
index 0000000..b41542c
--- /dev/null
+++ b/include/osmium/geometry/geos.hpp
@@ -0,0 +1,119 @@
+#ifndef OSMIUM_GEOMETRY_GEOS_HPP
+#define OSMIUM_GEOMETRY_GEOS_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#define OSMIUM_COMPILE_WITH_CFLAGS_GEOS `geos-config --cflags`
+#define OSMIUM_LINK_WITH_LIBS_GEOS `geos-config --libs`
+
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/PrecisionModel.h>
+#include <geos/geom/Coordinate.h>
+#include <geos/geom/CoordinateSequence.h>
+#include <geos/geom/CoordinateSequenceFactory.h>
+#include <geos/geom/Point.h>
+#include <geos/geom/LinearRing.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/MultiPolygon.h>
+
+#include <osmium/osm/position.hpp>
+#include <osmium/geometry/point.hpp>
+#include <osmium/geometry/linestring.hpp>
+#include <osmium/geometry/polygon.hpp>
+
+namespace Osmium {
+
+    namespace Geometry {
+
+        /**
+         * Return pointer to a static GEOS GeometryFactory object created the
+         * first time this function is run. This is used by all functions in
+         * Osmium that need to create GEOS geometries.
+         */
+        inline geos::geom::GeometryFactory* geos_geometry_factory() {
+            static geos::geom::PrecisionModel pm;
+            static geos::geom::GeometryFactory factory(&pm, -1);
+            return &factory;
+        }
+
+        /**
+         * Creates GEOS coordinate from a Position
+         */
+        inline geos::geom::Coordinate create_geos_coordinate(const Osmium::OSM::Position position) {
+            return geos::geom::Coordinate(position.lon(), position.lat());
+        }
+
+        /**
+         * Creates GEOS geometry of a Point.
+         *
+         * Caller takes ownership.
+         */
+        inline geos::geom::Point* create_geos_geometry(const Osmium::Geometry::Point& point) {
+            return Osmium::Geometry::geos_geometry_factory()->createPoint(Osmium::Geometry::create_geos_coordinate(point.position()));
+        }
+
+        /**
+         * Create GEOS geometry of a LineString.
+         *
+         * Caller takes ownership.
+         */
+        inline geos::geom::LineString* create_geos_geometry(const Osmium::Geometry::LineString& linestring) {
+            std::vector<geos::geom::Coordinate>* c = new std::vector<geos::geom::Coordinate>;
+            if (linestring.reverse()) {
+                for (Osmium::OSM::WayNodeList::const_reverse_iterator it = linestring.nodes().rbegin(); it != linestring.nodes().rend(); ++it) {
+                    c->push_back(Osmium::Geometry::create_geos_coordinate(it->position()));
+                }
+            } else {
+                for (Osmium::OSM::WayNodeList::const_iterator it = linestring.nodes().begin(); it != linestring.nodes().end(); ++it) {
+                    c->push_back(Osmium::Geometry::create_geos_coordinate(it->position()));
+                }
+            }
+            geos::geom::CoordinateSequence* cs = Osmium::Geometry::geos_geometry_factory()->getCoordinateSequenceFactory()->create(c);
+            return Osmium::Geometry::geos_geometry_factory()->createLineString(cs);
+        }
+
+        /**
+         * Creates GEOS geometry of a Polygon.
+         *
+         * Caller takes ownership.
+         */
+        inline geos::geom::Polygon* create_geos_geometry(const Osmium::Geometry::Polygon& polygon) {
+            std::vector<geos::geom::Coordinate>* c = new std::vector<geos::geom::Coordinate>;
+            if (polygon.reverse()) {
+                for (Osmium::OSM::WayNodeList::const_reverse_iterator it = polygon.nodes().rbegin(); it != polygon.nodes().rend(); ++it) {
+                    c->push_back(Osmium::Geometry::create_geos_coordinate(it->position()));
+                }
+            } else {
+                for (Osmium::OSM::WayNodeList::const_iterator it = polygon.nodes().begin(); it != polygon.nodes().end(); ++it) {
+                    c->push_back(Osmium::Geometry::create_geos_coordinate(it->position()));
+                }
+            }
+            geos::geom::CoordinateSequence* cs = Osmium::Geometry::geos_geometry_factory()->getCoordinateSequenceFactory()->create(c);
+            geos::geom::LinearRing* lr = Osmium::Geometry::geos_geometry_factory()->createLinearRing(cs);
+            return Osmium::Geometry::geos_geometry_factory()->createPolygon(lr, NULL);
+        }
+
+    } // namespace Geometry
+
+} // namespace Osmium
+
+#endif // OSMIUM_GEOMETRY_GEOS_HPP
diff --git a/include/osmium/geometry/haversine.hpp b/include/osmium/geometry/haversine.hpp
new file mode 100644
index 0000000..1b90149
--- /dev/null
+++ b/include/osmium/geometry/haversine.hpp
@@ -0,0 +1,83 @@
+#ifndef OSMIUM_GEOMETRY_HAVERSINE_HPP
+#define OSMIUM_GEOMETRY_HAVERSINE_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <osmium/osm/position.hpp>
+#include <osmium/osm/way_node_list.hpp>
+
+namespace Osmium {
+
+    namespace Geometry {
+
+        /**
+         * @brief Functions to calculate arc distance on Earth using the haversine formula.
+         *
+         * See http://en.wikipedia.org/wiki/Haversine_formula
+         *
+         * Implementation derived from
+         * http://blog.julien.cayzac.name/2008/10/arc-and-distance-between-two-points-on.html
+         */
+        namespace Haversine {
+
+            /// @brief The usual PI/180 constant
+            static const double DEG_TO_RAD = 0.017453292519943295769236907684886;
+
+            /// @brief Earth's quadratic mean radius for WGS84
+            static const double EARTH_RADIUS_IN_METERS = 6372797.560856;
+
+            inline double distance(const double x1, const double y1, const double x2, const double y2) {
+                const double lon_arc = (x1 - x2) * DEG_TO_RAD;
+                const double lat_arc = (y1 - y2) * DEG_TO_RAD;
+                double lonh = sin(lon_arc * 0.5);
+                lonh *= lonh;
+                double lath = sin(lat_arc * 0.5);
+                lath *= lath;
+                const double tmp = cos(y1 * DEG_TO_RAD) * cos(y2 * DEG_TO_RAD);
+                return 2.0 * EARTH_RADIUS_IN_METERS * asin(sqrt(lath + tmp*lonh));
+            }
+
+            inline double distance(const Osmium::OSM::Position& from, const Osmium::OSM::Position& to) {
+                return distance(from.lon(), from.lat(), to.lon(), to.lat());
+            }
+
+            inline double distance(const Osmium::OSM::WayNodeList& wnl) {
+                double sum_length=0;
+
+                if (wnl.size() < 2) {
+                    return 0.0;
+                }
+
+                for (Osmium::OSM::WayNodeList::const_iterator it = wnl.begin(); it != wnl.end()-1; ++it) {
+                    sum_length += Osmium::Geometry::Haversine::distance(it->position(), (it+1)->position());
+                }
+
+                return sum_length;
+            }
+
+        } // namespace Haversine
+
+    } // namespace Geometry
+
+} // namespace Osmium
+
+#endif // OSMIUM_GEOMETRY_HAVERSINE_HPP
diff --git a/include/osmium/geometry/linestring.hpp b/include/osmium/geometry/linestring.hpp
index 96e6408..c78dd67 100644
--- a/include/osmium/geometry/linestring.hpp
+++ b/include/osmium/geometry/linestring.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -23,6 +23,7 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 */
 
 #include <algorithm>
+#include <iomanip>
 
 #include <osmium/geometry/from_way.hpp>
 
@@ -43,7 +44,8 @@ namespace Osmium {
             LineString(const Osmium::OSM::WayNodeList& way_node_list, ///< Way node list this geometry should be created from
                        bool reverse=false,                            ///< Create reverse geometry
                        osm_object_id_t id=0)                          ///< Object ID of the way this geometry was created from
-                : FromWay(way_node_list, reverse, id) {
+                :
+                FromWay(way_node_list, reverse, id) {
             }
 
             /**
@@ -51,7 +53,8 @@ namespace Osmium {
              */
             LineString(const Osmium::OSM::Way& way, ///< Way this geometry should be created from
                        bool reverse=false)          ///< Create reverse geometry
-                : FromWay(way.nodes(), reverse, way.id()) {
+                :
+                FromWay(way.nodes(), reverse, way.id()) {
             }
 
             std::ostream& write_to_stream(std::ostream& out, AsWKT, bool with_srid=false) const {
@@ -60,24 +63,24 @@ namespace Osmium {
                 }
                 LonLatListWriter<Osmium::OSM::WayNode> writer(out);
                 out << "LINESTRING(" << std::setprecision(10);
-                if (m_reverse) {
-                    for_each(m_way_node_list->rbegin(), m_way_node_list->rend(), writer);
+                if (reverse()) {
+                    for_each(nodes().rbegin(), nodes().rend(), writer);
                 } else {
-                    for_each(m_way_node_list->begin(), m_way_node_list->end(), writer);
+                    for_each(nodes().begin(), nodes().end(), writer);
                 }
                 return out << ")";
             }
 
             std::ostream& write_to_stream(std::ostream& out, AsWKB, bool with_srid=false) const {
                 write_binary_wkb_header(out, with_srid, wkbLineString);
-                write_binary<uint32_t>(out, m_way_node_list->size());
-                if (m_reverse) {
-                    for (Osmium::OSM::WayNodeList::const_reverse_iterator it = m_way_node_list->rbegin(); it != m_way_node_list->rend(); ++it) {
+                write_binary<uint32_t>(out, nodes().size());
+                if (reverse()) {
+                    for (Osmium::OSM::WayNodeList::const_reverse_iterator it = nodes().rbegin(); it != nodes().rend(); ++it) {
                         write_binary<double>(out, it->lon());
                         write_binary<double>(out, it->lat());
                     }
                 } else {
-                    for (Osmium::OSM::WayNodeList::const_iterator it = m_way_node_list->begin(); it != m_way_node_list->end(); ++it) {
+                    for (Osmium::OSM::WayNodeList::const_iterator it = nodes().begin(); it != nodes().end(); ++it) {
                         write_binary<double>(out, it->lon());
                         write_binary<double>(out, it->lat());
                     }
@@ -87,14 +90,14 @@ namespace Osmium {
 
             std::ostream& write_to_stream(std::ostream& out, AsHexWKB, bool with_srid=false) const {
                 write_hex_wkb_header(out, with_srid, wkbLineString);
-                write_hex<uint32_t>(out, m_way_node_list->size());
-                if (m_reverse) {
-                    for (Osmium::OSM::WayNodeList::const_reverse_iterator it = m_way_node_list->rbegin(); it != m_way_node_list->rend(); ++it) {
+                write_hex<uint32_t>(out, nodes().size());
+                if (reverse()) {
+                    for (Osmium::OSM::WayNodeList::const_reverse_iterator it = nodes().rbegin(); it != nodes().rend(); ++it) {
                         write_hex<double>(out, it->lon());
                         write_hex<double>(out, it->lat());
                     }
                 } else {
-                    for (Osmium::OSM::WayNodeList::const_iterator it = m_way_node_list->begin(); it != m_way_node_list->end(); ++it) {
+                    for (Osmium::OSM::WayNodeList::const_iterator it = nodes().begin(); it != nodes().end(); ++it) {
                         write_hex<double>(out, it->lon());
                         write_hex<double>(out, it->lat());
                     }
@@ -102,125 +105,10 @@ namespace Osmium {
                 return out;
             }
 
-#ifdef OSMIUM_WITH_GEOS
-            /**
-             * Create GEOS geometry of this LineString.
-             *
-             * Caller takes ownership.
-             */
-            geos::geom::Geometry* create_geos_geometry() const {
-                try {
-                    std::vector<geos::geom::Coordinate>* c = new std::vector<geos::geom::Coordinate>;
-                    if (m_reverse) {
-                        for (Osmium::OSM::WayNodeList::const_reverse_iterator it = m_way_node_list->rbegin(); it != m_way_node_list->rend(); ++it) {
-                            c->push_back(it->position());
-                        }
-                    } else {
-                        for (Osmium::OSM::WayNodeList::const_iterator it = m_way_node_list->begin(); it != m_way_node_list->end(); ++it) {
-                            c->push_back(it->position());
-                        }
-                    }
-                    geos::geom::CoordinateSequence* cs = Osmium::Geometry::geos_geometry_factory()->getCoordinateSequenceFactory()->create(c);
-                    return static_cast<geos::geom::Geometry*>(Osmium::Geometry::geos_geometry_factory()->createLineString(cs));
-                } catch (const geos::util::GEOSException& exc) {
-                    if (Osmium::debug()) {
-                        std::cerr << "error building geometry for way #" << id() <<
-                                  " (returning NULL): " << exc.what();
-                    }
-                    return NULL;
-                }
-            }
-#endif // OSMIUM_WITH_GEOS
-
-#ifdef OSMIUM_WITH_SHPLIB
-            /**
-             * Create Shapelib geometry of this LineString.
-             *
-             * Caller takes ownership. You have to call
-             * SHPDestroyObject() with this geometry when you are done.
-             */
-            SHPObject* create_shp_object() const {
-                return create_line_or_polygon(SHPT_ARC);
-            }
-#endif // OSMIUM_WITH_SHPLIB
-
-#ifdef OSMIUM_WITH_OGR
-            /**
-             * Create OGR geometry of this LineString;
-             *
-             * Caller takes ownership.
-             */
-            OGRLineString* create_ogr_geometry() const {
-                OGRLineString* p = new OGRLineString();
-                if (m_reverse) {
-                    for (Osmium::OSM::WayNodeList::const_reverse_iterator it = m_way_node_list->rbegin(); it != m_way_node_list->rend(); ++it) {
-                        p->addPoint(it->lon(), it->lat());
-                    }
-                } else {
-                    for (Osmium::OSM::WayNodeList::const_iterator it = m_way_node_list->begin(); it != m_way_node_list->end(); ++it) {
-                        p->addPoint(it->lon(), it->lat());
-                    }
-                }
-                return p;
-            }
-#endif // OSMIUM_WITH_OGR
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void *)this);
-            }
-
-            v8::Handle<v8::Value> js_to_array(const v8::Arguments& /*args*/) {
-                v8::HandleScope scope;
-                v8::Local<v8::Array> linestring = v8::Array::New(m_way_node_list->size());
-                unsigned int max = m_way_node_list->size() - 1;
-                if (m_reverse) {
-                    for (unsigned int i=0; i <= max; ++i) {
-                        linestring->Set(max - i, (*m_way_node_list)[i].position().js_to_array());
-                    }
-                } else {
-                    for (unsigned int i=0; i <= max; ++i) {
-                        linestring->Set(i, (*m_way_node_list)[i].position().js_to_array());
-                    }
-                }
-                return scope.Close(linestring);
-            }
-
-            struct JavascriptTemplate : public Osmium::Geometry::Geometry::JavascriptTemplate {
-
-                JavascriptTemplate() : Osmium::Geometry::Geometry::JavascriptTemplate() {
-                    js_template->Set("toArray", v8::FunctionTemplate::New(function_template<LineString, &LineString::js_to_array>));
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         }; // class LineString
 
     } // namespace Geometry
 
 } // namespace Osmium
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-v8::Handle<v8::Value> Osmium::OSM::Way::js_geom() const {
-    if (m_node_list.has_position()) {
-        Osmium::Geometry::LineString* geom = new Osmium::Geometry::LineString(*this);
-        return Osmium::Javascript::Template::get<Osmium::Geometry::LineString::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::LineString>(geom);
-    } else {
-        Osmium::Geometry::Null* geom = new Osmium::Geometry::Null();
-        return Osmium::Javascript::Template::get<Osmium::Geometry::Null::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::Null>(geom);
-    }
-}
-
-v8::Handle<v8::Value> Osmium::OSM::Way::js_reverse_geom() const {
-    if (m_node_list.has_position()) {
-        Osmium::Geometry::LineString* geom = new Osmium::Geometry::LineString(*this, true);
-        return Osmium::Javascript::Template::get<Osmium::Geometry::LineString::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::LineString>(geom);
-    } else {
-        Osmium::Geometry::Null* geom = new Osmium::Geometry::Null();
-        return Osmium::Javascript::Template::get<Osmium::Geometry::Null::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::Null>(geom);
-    }
-}
-#endif // OSMIUM_WITH_JAVASCRIPT
-
 #endif // OSMIUM_GEOMETRY_LINESTRING_HPP
diff --git a/include/osmium/geometry/multipolygon.hpp b/include/osmium/geometry/multipolygon.hpp
index 2f039dd..222c1fe 100644
--- a/include/osmium/geometry/multipolygon.hpp
+++ b/include/osmium/geometry/multipolygon.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,12 +22,16 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#ifdef OSMIUM_WITH_GEOS
-# include <geos/io/WKBWriter.h>
-#endif // OSMIUM_WITH_GEOS
+#include <geos/geom/Geometry.h>
+#include <geos/geom/MultiPolygon.h>
+#include <geos/io/WKBWriter.h>
+#include <geos/io/WKTWriter.h>
 
+#include <osmium/smart_ptr.hpp>
 #include <osmium/osm/area.hpp>
 #include <osmium/geometry.hpp>
+#include <osmium/geometry/polygon.hpp>
+#include <osmium/geometry/geos.hpp>
 
 namespace Osmium {
 
@@ -37,242 +41,64 @@ namespace Osmium {
 
         public:
 
-            MultiPolygon(const Osmium::OSM::Area& area) : Geometry(area.id()), m_area(&area) {
-#ifdef OSMIUM_WITH_GEOS
-                if (!m_area->get_geometry()) {
-                    throw Osmium::Exception::IllegalGeometry();
+            MultiPolygon(const Osmium::OSM::Area& area) :
+                Geometry(area.id()),
+                m_area(area) {
+
+                // if the area doesn't have a geometry yet it means it was created from a way
+                // and we create the geometry from the node list.
+                if (!area.geos_geometry()) {
+                    Osmium::Geometry::Polygon polygon(area.m_node_list, false, area.id());
+                    std::vector<geos::geom::Geometry*>* geos_polygons = new std::vector<geos::geom::Geometry*>(1, Osmium::Geometry::create_geos_geometry(polygon));
+                    geos::geom::MultiPolygon* geos_multipolygon = Osmium::Geometry::geos_geometry_factory()->createMultiPolygon(geos_polygons);
+                    assert(geos_multipolygon);
+                    m_area.geos_geometry(geos_multipolygon);
                 }
-#endif // OSMIUM_WITH_GEOS
             }
 
-#ifdef OSMIUM_WITH_GEOS
-# ifdef OSMIUM_WITH_SHPLIB
-
-        private:
-
-            void dump_geometry(const geos::geom::Geometry* g, std::vector<int>& part_start_list, std::vector<double>& x_list, std::vector<double>& y_list) const {
-                switch (g->getGeometryTypeId()) {
-                    case geos::geom::GEOS_MULTIPOLYGON:
-                    case geos::geom::GEOS_MULTILINESTRING: {
-                        for (geos::geom::GeometryCollection::const_iterator it = static_cast<const geos::geom::GeometryCollection*>(g)->begin();
-                                it != static_cast<const geos::geom::GeometryCollection*>(g)->end(); ++it) {
-                            dump_geometry(*it, part_start_list, x_list, y_list);
-                        }
-                        break;
-                    }
-                    case geos::geom::GEOS_POLYGON: {
-                        const geos::geom::Polygon* polygon = static_cast<const geos::geom::Polygon*>(g);
-                        dump_geometry(polygon->getExteriorRing(), part_start_list, x_list, y_list);
-                        for (size_t i=0; i < polygon->getNumInteriorRing(); ++i) {
-                            dump_geometry(polygon->getInteriorRingN(i), part_start_list, x_list, y_list);
-                        }
-                        break;
-                    }
-                    case geos::geom::GEOS_LINESTRING:
-                    case geos::geom::GEOS_LINEARRING: {
-                        part_start_list.push_back(x_list.size());
-                        const geos::geom::CoordinateSequence* cs = static_cast<const geos::geom::LineString*>(g)->getCoordinatesRO();
-                        for (size_t i = 0; i < cs->getSize(); ++i) {
-                            x_list.push_back(cs->getX(i));
-                            y_list.push_back(cs->getY(i));
-                        }
-                        break;
-                    }
-                    default:
-                        throw std::runtime_error("invalid geometry type encountered");
-                }
-            }
-
-        public:
-
-            /**
-             * Create Shapelib geometry of this MultiPolygon.
-             *
-             * Caller takes ownership. You have to call
-             * SHPDestroyObject() with this geometry when you are done.
-             */
-            SHPObject* create_shp_object() const {
-                if (!m_area->get_geometry()) {
-                    throw Osmium::Exception::IllegalGeometry();
-                }
-
-                std::vector<double> x_list;
-                std::vector<double> y_list;
-                std::vector<int> part_start_list;
-
-                dump_geometry(m_area->get_geometry(), part_start_list, x_list, y_list);
-
-                return SHPCreateObject(
-                           SHPT_POLYGON,           // nSHPType
-                           -1,                     // iShape
-                           part_start_list.size(), // nParts
-                           &part_start_list[0],    // panPartStart
-                           NULL,                   // panPartType
-                           x_list.size(),          // nVertices,
-                           &x_list[0],             // padfX
-                           &y_list[0],             // padfY
-                           NULL,                   // padfZ
-                           NULL);                  // padfM
+            ~MultiPolygon() {
             }
-# endif // OSMIUM_WITH_SHPLIB
 
             std::ostream& write_to_stream(std::ostream& out, AsWKT, bool with_srid=false) const {
                 if (with_srid) {
                     out << "SRID=4326;";
                 }
                 geos::io::WKTWriter writer;
-                return out << writer.write(m_area->get_geometry());
+                // XXX only available in later GEOS versions
+//                writer.setRoundingPrecision(10);
+                return out << writer.write(borrow_geos_geometry());
             }
 
             std::ostream& write_to_stream(std::ostream& out, AsWKB, bool with_srid=false) const {
                 geos::io::WKBWriter writer;
                 writer.setIncludeSRID(with_srid);
-                writer.write(*(m_area->get_geometry()), out);
+                writer.write(*borrow_geos_geometry(), out);
                 return out;
             }
 
             std::ostream& write_to_stream(std::ostream& out, AsHexWKB, bool with_srid=false) const {
                 geos::io::WKBWriter writer;
                 writer.setIncludeSRID(with_srid);
-                writer.writeHEX(*(m_area->get_geometry()), out);
+                writer.writeHEX(*borrow_geos_geometry(), out);
                 return out;
             }
 
-# ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void*)this);
-            }
-
-            v8::Handle<v8::Array> js_ring_as_array(const geos::geom::LineString* ring) const {
-                v8::HandleScope scope;
-                const geos::geom::CoordinateSequence* cs = ring->getCoordinatesRO();
-                v8::Local<v8::Array> ring_array = v8::Array::New(cs->getSize());
-                for (size_t i = 0; i < cs->getSize(); ++i) {
-                    v8::Local<v8::Array> coord = v8::Array::New(2);
-                    coord->Set(0, v8::Number::New(cs->getX(i)));
-                    coord->Set(1, v8::Number::New(cs->getY(i)));
-                    ring_array->Set(i, coord);
-                }
-
-                return scope.Close(ring_array);
-            }
-
-            v8::Handle<v8::Value> js_to_array(const v8::Arguments& /*args*/) {
-                v8::HandleScope scope;
-                geos::geom::Geometry* geometry = m_area->get_geometry();
-
-                if (geometry->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
-                    v8::Local<v8::Array> multipolygon_array = v8::Array::New(geometry->getNumGeometries());
-
-                    for (size_t i=0; i < geometry->getNumGeometries(); ++i) {
-                        geos::geom::Polygon* polygon = (geos::geom::Polygon*) geometry->getGeometryN(i);
-                        v8::Local<v8::Array> polygon_array = v8::Array::New(polygon->getNumInteriorRing());
-                        multipolygon_array->Set(i, polygon_array);
-                        polygon_array->Set(0, js_ring_as_array(polygon->getExteriorRing()));
-                        for (size_t j=0; j < polygon->getNumInteriorRing(); ++j) {
-                            polygon_array->Set(j+1, js_ring_as_array(polygon->getInteriorRingN(j)));
-                        }
-                    }
-                    return scope.Close(multipolygon_array);
-                } else if (geometry->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
-                    const Osmium::OSM::AreaFromWay* area_from_way = dynamic_cast<const Osmium::OSM::AreaFromWay*>(m_area);
-                    if (area_from_way) {
-                        v8::Local<v8::Array> polygon = v8::Array::New(1);
-                        v8::Local<v8::Array> ring = v8::Array::New(area_from_way->nodes().size());
-                        int n = 0;
-                        for (Osmium::OSM::WayNodeList::const_iterator it = area_from_way->nodes().begin(); it != area_from_way->nodes().end(); ++it) {
-                            v8::Local<v8::Array> coord = v8::Array::New(2);
-                            coord->Set(0, v8::Number::New(it->lon()));
-                            coord->Set(1, v8::Number::New(it->lat()));
-                            ring->Set(n++, coord);
-                        }
-                        polygon->Set(0, ring);
-                        return scope.Close(polygon);
-                    }
-                }
-
-                return scope.Close(v8::Undefined());
-            }
-
-            struct JavascriptTemplate : public Osmium::Geometry::Geometry::JavascriptTemplate {
-
-                JavascriptTemplate() : Osmium::Geometry::Geometry::JavascriptTemplate() {
-                    js_template->Set("toArray", v8::FunctionTemplate::New(function_template<MultiPolygon, &MultiPolygon::js_to_array>));
-                }
-
-            };
-# endif // OSMIUM_WITH_JAVASCRIPT
-
-# ifdef OSMIUM_WITH_OGR
-        private:
-
-            void add_ring(OGRPolygon* ogrpolygon, const geos::geom::LineString* geosring) const {
-                OGRLinearRing* ogrring = new OGRLinearRing;
-
-                const geos::geom::CoordinateSequence* cs = geosring->getCoordinatesRO();
-                ogrring->setNumPoints(cs->getSize());
-
-                for (size_t i = 0; i < cs->getSize(); ++i) {
-                    ogrring->setPoint(i, cs->getX(i), cs->getY(i), 0);
-                }
-
-                ogrpolygon->addRingDirectly(ogrring);
-            };
-
-            OGRPolygon* make_polygon(const geos::geom::Polygon* geospolygon) const {
-                OGRPolygon* ogrpolygon = new OGRPolygon;
-
-                add_ring(ogrpolygon, geospolygon->getExteriorRing());
-                for (size_t i=0; i < geospolygon->getNumInteriorRing(); ++i) {
-                    add_ring(ogrpolygon, geospolygon->getInteriorRingN(i));
-                }
-
-                return ogrpolygon;
-            }
-
-        public:
-
             /**
-             * Create OGR geometry of this MultiPolygon.
-             *
-             * Caller takes ownership.
+             * Get GEOS MultiPolygon geometry. The geometry still
+             * belongs to this object, you are not allowed to change
+             * or free it.
              */
-            OGRMultiPolygon* create_ogr_geometry() const {
-                OGRMultiPolygon* ogrmp = new OGRMultiPolygon;
-
-                if (m_area->get_geometry()->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
-                    OGRPolygon* ogrpolygon = make_polygon(dynamic_cast<const geos::geom::Polygon*>(m_area->get_geometry()));
-
-                    OGRErr result = ogrmp->addGeometryDirectly(ogrpolygon);
-                    if (result != OGRERR_NONE) {
-                        throw Osmium::Exception::IllegalGeometry();
-                    }
-                    return ogrmp;
+            const geos::geom::MultiPolygon* borrow_geos_geometry() const {
+                if (!m_area.geos_geometry()) {
+                    throw Osmium::Geometry::NoGeometry();
                 }
 
-                if (m_area->get_geometry()->getGeometryTypeId() != geos::geom::GEOS_MULTIPOLYGON) {
-                    throw Osmium::Exception::IllegalGeometry();
-                }
-
-                const geos::geom::GeometryCollection* geosgeom = dynamic_cast<const geos::geom::GeometryCollection*>(m_area->get_geometry());
-                for (geos::geom::GeometryCollection::const_iterator it = geosgeom->begin(); it != geosgeom->end(); ++it) {
-
-                    OGRPolygon* ogrpolygon = make_polygon(dynamic_cast<const geos::geom::Polygon*>(*it));
-
-                    OGRErr result = ogrmp->addGeometryDirectly(ogrpolygon);
-                    if (result != OGRERR_NONE) {
-                        throw Osmium::Exception::IllegalGeometry();
-                    }
-                }
-
-                return ogrmp;
+                return m_area.geos_geometry();
             }
-# endif // OSMIUM_WITH_OGR
-#endif // OSMIUM_WITH_GEOS
 
         private:
 
-            const Osmium::OSM::Area* m_area;
+            const Osmium::OSM::Area& m_area;
 
         }; // class MultiPolygon
 
@@ -280,16 +106,4 @@ namespace Osmium {
 
 } // namespace Osmium
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-v8::Handle<v8::Value> Osmium::OSM::Area::js_geom() const {
-    if (get_geometry()) {
-        Osmium::Geometry::MultiPolygon* geom = new Osmium::Geometry::MultiPolygon(*this);
-        return Osmium::Javascript::Template::get<Osmium::Geometry::MultiPolygon::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::MultiPolygon>(geom);
-    } else {
-        Osmium::Geometry::Null* geom = new Osmium::Geometry::Null();
-        return Osmium::Javascript::Template::get<Osmium::Geometry::Null::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::Null>(geom);
-    }
-}
-#endif // OSMIUM_WITH_JAVASCRIPT
-
 #endif // OSMIUM_GEOMETRY_MULTIPOLYGON_HPP
diff --git a/include/osmium/geometry/null.hpp b/include/osmium/geometry/null.hpp
index d73112f..22e23d9 100644
--- a/include/osmium/geometry/null.hpp
+++ b/include/osmium/geometry/null.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -51,19 +51,6 @@ namespace Osmium {
                 return out;
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->Set("toWKT",    v8::FunctionTemplate::New(function_template<Osmium::Javascript::Template, &Osmium::Javascript::Template::js_undefined>));
-                    js_template->Set("toWKB",    v8::FunctionTemplate::New(function_template<Osmium::Javascript::Template, &Osmium::Javascript::Template::js_undefined>));
-                    js_template->Set("toHexWKB", v8::FunctionTemplate::New(function_template<Osmium::Javascript::Template, &Osmium::Javascript::Template::js_undefined>));
-                    js_template->Set("toArray",  v8::FunctionTemplate::New(function_template<Osmium::Javascript::Template, &Osmium::Javascript::Template::js_undefined>));
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         }; // class Null
 
     } // namespace Geometry
diff --git a/include/osmium/geometry/ogr.hpp b/include/osmium/geometry/ogr.hpp
new file mode 100644
index 0000000..84a3586
--- /dev/null
+++ b/include/osmium/geometry/ogr.hpp
@@ -0,0 +1,93 @@
+#ifndef OSMIUM_GEOMETRY_OGR_HPP
+#define OSMIUM_GEOMETRY_OGR_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#define OSMIUM_COMPILE_WITH_CFLAGS_OGR `gdal-config --cflags`
+#define OSMIUM_LINK_WITH_LIBS_OGR `gdal-config --libs`
+
+#include <boost/foreach.hpp>
+
+#include <ogr_geometry.h>
+
+#include <osmium/geometry/point.hpp>
+#include <osmium/geometry/linestring.hpp>
+#include <osmium/geometry/polygon.hpp>
+
+namespace Osmium {
+
+    namespace Geometry {
+
+        /**
+         * Create OGR geometry of a Point.
+         *
+         * Caller takes ownership.
+         */
+        inline OGRPoint* create_ogr_geometry(const Osmium::Geometry::Point& point) {
+            return new OGRPoint(point.lon(), point.lat());
+        }
+
+        /**
+         * Create OGR geometry of a LineString;
+         *
+         * Caller takes ownership.
+         */
+        inline OGRLineString* create_ogr_geometry(const Osmium::Geometry::LineString& linestring) {
+            OGRLineString* ls = new OGRLineString();
+            if (linestring.reverse()) {
+                BOOST_REVERSE_FOREACH(const Osmium::OSM::WayNode& wn, linestring.nodes()) {
+                    ls->addPoint(wn.lon(), wn.lat());
+                }
+            } else {
+                BOOST_FOREACH(const Osmium::OSM::WayNode& wn, linestring.nodes()) {
+                    ls->addPoint(wn.lon(), wn.lat());
+                }
+            }
+            return ls;
+        }
+
+        /**
+         * Create OGR geometry of a Polygon.
+         *
+         * Caller takes ownership.
+         */
+        inline OGRPolygon* create_ogr_geometry(const Osmium::Geometry::Polygon& polygon) {
+            OGRPolygon* p = new OGRPolygon();
+            OGRLinearRing* r = new OGRLinearRing();
+            if (polygon.reverse()) {
+                BOOST_REVERSE_FOREACH(const Osmium::OSM::WayNode& wn, polygon.nodes()) {
+                    r->addPoint(wn.lon(), wn.lat());
+                }
+            } else {
+                BOOST_FOREACH(const Osmium::OSM::WayNode& wn, polygon.nodes()) {
+                    r->addPoint(wn.lon(), wn.lat());
+                }
+            }
+            p->addRingDirectly(r);
+            return p;
+        }
+
+    } // namespace Geometry
+
+} // namespace Osmium
+
+#endif // OSMIUM_GEOMETRY_OGR_HPP
diff --git a/include/osmium/geometry/ogr_multipolygon.hpp b/include/osmium/geometry/ogr_multipolygon.hpp
new file mode 100644
index 0000000..1d01d55
--- /dev/null
+++ b/include/osmium/geometry/ogr_multipolygon.hpp
@@ -0,0 +1,97 @@
+#ifndef OSMIUM_GEOMETRY_OGR_MULTIPOLYGON_HPP
+#define OSMIUM_GEOMETRY_OGR_MULTIPOLYGON_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#define OSMIUM_COMPILE_WITH_CFLAGS_OGR `gdal-config --cflags`
+#define OSMIUM_LINK_WITH_LIBS_OGR `gdal-config --libs`
+
+#include <ogr_geometry.h>
+
+#include <geos/geom/LineString.h>
+#include <geos/geom/CoordinateSequence.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/GeometryCollection.h>
+
+#include <osmium/geometry/multipolygon.hpp>
+
+namespace Osmium {
+
+    namespace Geometry {
+
+        namespace {
+
+            inline void add_ring(OGRPolygon* ogrpolygon, const geos::geom::LineString* geosring) {
+                OGRLinearRing* ogrring = new OGRLinearRing;
+
+                const geos::geom::CoordinateSequence* cs = geosring->getCoordinatesRO();
+                ogrring->setNumPoints(cs->getSize());
+
+                for (size_t i = 0; i < cs->getSize(); ++i) {
+                    ogrring->setPoint(i, cs->getX(i), cs->getY(i));
+                }
+
+                ogrpolygon->addRingDirectly(ogrring);
+            }
+
+            inline OGRPolygon* make_polygon(const geos::geom::Polygon* geospolygon) {
+                OGRPolygon* ogrpolygon = new OGRPolygon;
+
+                add_ring(ogrpolygon, geospolygon->getExteriorRing());
+                for (size_t i=0; i < geospolygon->getNumInteriorRing(); ++i) {
+                    add_ring(ogrpolygon, geospolygon->getInteriorRingN(i));
+                }
+
+                return ogrpolygon;
+            }
+
+        }
+
+        /**
+         * Create OGR geometry of a MultiPolygon.
+         *
+         * Caller takes ownership.
+         */
+        inline OGRMultiPolygon* create_ogr_geometry(const Osmium::Geometry::MultiPolygon& multipolygon) {
+            OGRMultiPolygon* ogrmp = new OGRMultiPolygon;
+
+            const geos::geom::GeometryCollection* geosgeom = dynamic_cast<const geos::geom::GeometryCollection*>(multipolygon.borrow_geos_geometry());
+            for (geos::geom::GeometryCollection::const_iterator it = geosgeom->begin(); it != geosgeom->end(); ++it) {
+
+                OGRPolygon* ogrpolygon = make_polygon(dynamic_cast<const geos::geom::Polygon*>(*it));
+
+                OGRErr result = ogrmp->addGeometryDirectly(ogrpolygon);
+                if (result != OGRERR_NONE) {
+                    // delete ogrpolygon;    XXX are we supposed to delete this ourselves?
+                    delete ogrmp;
+                    throw Osmium::Geometry::IllegalGeometry();
+                }
+            }
+
+            return ogrmp;
+        }
+
+    } // namespace Geometry
+
+} // namespace Osmium
+
+#endif // OSMIUM_GEOMETRY_OGR_MULTIPOLYGON_HPP
diff --git a/include/osmium/geometry/point.hpp b/include/osmium/geometry/point.hpp
index 532ccb4..5ec9828 100644
--- a/include/osmium/geometry/point.hpp
+++ b/include/osmium/geometry/point.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,14 +22,9 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <vector>
-#include <sstream>
+#include <ostream>
 #include <iomanip>
 
-#ifdef OSMIUM_WITH_GEOS
-# include <geos/geom/Point.h>
-#endif // OSMIUM_WITH_GEOS
-
 #include <osmium/geometry.hpp>
 #include <osmium/osm/node.hpp>
 
@@ -47,15 +42,21 @@ namespace Osmium {
             /**
              * Create point geometry from a position.
              */
-            Point(const Osmium::OSM::Position& position, osm_object_id_t id=0) : Geometry(id), m_position(position) {
+            Point(const Osmium::OSM::Position& position, osm_object_id_t id=0) :
+                Geometry(id),
+                m_position(position) {
             }
 
             /**
              * Create point geometry from position of a node.
              */
-            Point(const Osmium::OSM::Node& node)
-                : Geometry(node.id()),
-                  m_position(Osmium::OSM::Position(node.get_lon(), node.get_lat())) {
+            Point(const Osmium::OSM::Node& node) :
+                Geometry(node.id()),
+                m_position(node.position()) {
+            }
+
+            const Osmium::OSM::Position& position() const {
+                return m_position;
             }
 
             double lon() const {
@@ -87,71 +88,6 @@ namespace Osmium {
                 return out;
             }
 
-#ifdef OSMIUM_WITH_GEOS
-            /**
-             * Creates GEOS geometry of this Point.
-             *
-             * Caller takes ownership.
-             */
-            geos::geom::Point* create_geos_geometry() const {
-                return Osmium::Geometry::geos_geometry_factory()->createPoint(m_position);
-            }
-#endif // OSMIUM_WITH_GEOS
-
-#ifdef OSMIUM_WITH_SHPLIB
-            /**
-             * Create Shapelib geometry of this Point.
-             *
-             * Caller takes ownership. You have to call
-             * SHPDestroyObject() with this geometry when you are done.
-             */
-            SHPObject* create_shp_object() const {
-                double x = lon();
-                double y = lat();
-                return SHPCreateSimpleObject(SHPT_POINT, 1, &x, &y, NULL);
-            }
-#endif // OSMIUM_WITH_SHPLIB
-
-#ifdef OSMIUM_WITH_OGR
-            /**
-             * Create OGR geometry of this Point.
-             *
-             * Caller takes ownership.
-             */
-            OGRPoint* create_ogr_geometry() const {
-                OGRPoint* p = new OGRPoint(lon(), lat());
-                return p;
-            }
-#endif // OSMIUM_WITH_OGR
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void*)this);
-            }
-
-            v8::Handle<v8::Value> js_lon() const {
-                return v8::Number::New(lon());
-            }
-
-            v8::Handle<v8::Value> js_lat() const {
-                return v8::Number::New(lat());
-            }
-
-            v8::Handle<v8::Value> js_to_array(const v8::Arguments& /*args*/) {
-                return m_position.js_to_array();
-            }
-
-            struct JavascriptTemplate : public Osmium::Geometry::Geometry::JavascriptTemplate {
-
-                JavascriptTemplate() : Osmium::Geometry::Geometry::JavascriptTemplate() {
-                    js_template->SetAccessor(v8::String::NewSymbol("lon"), accessor_getter<Point, &Point::js_lon>);
-                    js_template->SetAccessor(v8::String::NewSymbol("lat"), accessor_getter<Point, &Point::js_lat>);
-                    js_template->Set("toArray", v8::FunctionTemplate::New(function_template<Point, &Point::js_to_array>));
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         private:
 
             const Osmium::OSM::Position m_position;
@@ -162,12 +98,4 @@ namespace Osmium {
 
 } // namespace Osmium
 
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-v8::Handle<v8::Value> Osmium::OSM::Node::js_get_geom() const {
-    Osmium::Geometry::Point* geom = new Osmium::Geometry::Point(*this);
-    return Osmium::Javascript::Template::get<Osmium::Geometry::Point::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::Point>(geom);
-}
-#endif // OSMIUM_WITH_JAVASCRIPT
-
 #endif // OSMIUM_GEOMETRY_POINT_HPP
diff --git a/include/osmium/geometry/polygon.hpp b/include/osmium/geometry/polygon.hpp
index 8e0db44..5128c0d 100644
--- a/include/osmium/geometry/polygon.hpp
+++ b/include/osmium/geometry/polygon.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -23,9 +23,10 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 */
 
 #include <algorithm>
+#include <iomanip>
+#include <ostream>
 
 #include <osmium/geometry/from_way.hpp>
-#include <osmium/exceptions.hpp>
 
 namespace Osmium {
 
@@ -42,20 +43,21 @@ namespace Osmium {
              * Create Polygon geometry from a list of nodes.
              */
             Polygon(const Osmium::OSM::WayNodeList& way_node_list,
-                    osm_object_id_t id=0)
-                : FromWay(way_node_list, false, id) {
+                    bool reverse=false,
+                    osm_object_id_t id=0) :
+                FromWay(way_node_list, reverse, id) {
                 if (!way_node_list.is_closed()) {
-                    throw Osmium::Exception::IllegalGeometry();
+                    throw RingNotClosed();
                 }
             }
 
             /**
              * Create Polygon geometry from a list of nodes in a way.
              */
-            Polygon(const Osmium::OSM::Way& way)
-                : FromWay(way.nodes(), false, way.id()) {
+            Polygon(const Osmium::OSM::Way& way, bool reverse=false) :
+                FromWay(way.nodes(), reverse, way.id()) {
                 if (!way.nodes().is_closed()) {
-                    throw Osmium::Exception::IllegalGeometry();
+                    throw RingNotClosed();
                 }
             }
 
@@ -65,15 +67,15 @@ namespace Osmium {
                 }
                 LonLatListWriter<Osmium::OSM::WayNode> writer(out);
                 out << "POLYGON((" << std::setprecision(10);
-                for_each(m_way_node_list->begin(), m_way_node_list->end(), writer);
+                for_each(nodes().begin(), nodes().end(), writer);
                 return out << "))";
             }
 
             std::ostream& write_to_stream(std::ostream& out, AsWKB, bool with_srid=false) const {
                 write_binary_wkb_header(out, with_srid, wkbPolygon);
                 write_binary<uint32_t>(out, 1); // ring count
-                write_binary<uint32_t>(out, m_way_node_list->size()); // ring #1 point count
-                for (Osmium::OSM::WayNodeList::const_iterator it = m_way_node_list->begin(); it != m_way_node_list->end(); ++it) {
+                write_binary<uint32_t>(out, nodes().size()); // ring #1 point count
+                for (Osmium::OSM::WayNodeList::const_iterator it = nodes().begin(); it != nodes().end(); ++it) {
                     write_binary<double>(out, it->lon());
                     write_binary<double>(out, it->lat());
                 }
@@ -83,113 +85,18 @@ namespace Osmium {
             std::ostream& write_to_stream(std::ostream& out, AsHexWKB, bool with_srid=false) const {
                 write_hex_wkb_header(out, with_srid, wkbPolygon);
                 write_hex<uint32_t>(out, 1); // ring count
-                write_hex<uint32_t>(out, m_way_node_list->size()); // ring #1 point count
-                for (Osmium::OSM::WayNodeList::const_iterator it = m_way_node_list->begin(); it != m_way_node_list->end(); ++it) {
+                write_hex<uint32_t>(out, nodes().size()); // ring #1 point count
+                for (Osmium::OSM::WayNodeList::const_iterator it = nodes().begin(); it != nodes().end(); ++it) {
                     write_hex<double>(out, it->lon());
                     write_hex<double>(out, it->lat());
                 }
                 return out;
             }
 
-#ifdef OSMIUM_WITH_GEOS
-            /**
-             * Creates GEOS geometry of this Polygon.
-             *
-             * Caller takes ownership.
-             */
-            geos::geom::Geometry* create_geos_geometry() const {
-                try {
-                    std::vector<geos::geom::Coordinate>* c = new std::vector<geos::geom::Coordinate>;
-                    for (Osmium::OSM::WayNodeList::const_iterator it = m_way_node_list->begin(); it != m_way_node_list->end(); ++it) {
-                        c->push_back(it->position());
-                    }
-                    geos::geom::CoordinateSequence* cs = Osmium::Geometry::geos_geometry_factory()->getCoordinateSequenceFactory()->create(c);
-                    geos::geom::LinearRing* lr = Osmium::Geometry::geos_geometry_factory()->createLinearRing(cs);
-                    return static_cast<geos::geom::Geometry*>(Osmium::Geometry::geos_geometry_factory()->createPolygon(lr, NULL));
-                } catch (const geos::util::GEOSException& exc) {
-                    std::cerr << "error building polygon geometry, leave it as NULL\n";
-                    return NULL;
-                }
-            }
-#endif // OSMIUM_WITH_GEOS
-
-#ifdef OSMIUM_WITH_SHPLIB
-            /**
-             * Create Shapelib geometry of this Polygon.
-             *
-             * Caller takes ownership. You have to call
-             * SHPDestroyObject() with this geometry when you are done.
-             */
-            SHPObject* create_shp_object() const {
-                return create_line_or_polygon(SHPT_POLYGON);
-            }
-#endif // OSMIUM_WITH_SHPLIB
-
-#ifdef OSMIUM_WITH_OGR
-            /**
-             * Create OGR geometry of this Polygon.
-             *
-             * Caller takes ownership.
-             */
-            OGRPolygon* create_ogr_geometry() const {
-                OGRPolygon* p = new OGRPolygon();
-                OGRLinearRing* r = new OGRLinearRing();
-                for (Osmium::OSM::WayNodeList::const_reverse_iterator it = m_way_node_list->rbegin(); it != m_way_node_list->rend(); ++it) {
-                    r->addPoint(it->lon(), it->lat());
-                }
-                p->addRingDirectly(r);
-                return p;
-            }
-#endif // OSMIUM_WITH_OGR
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void *)this);
-            }
-
-            v8::Handle<v8::Value> js_to_array(const v8::Arguments& /*args*/) {
-                v8::HandleScope scope;
-                v8::Local<v8::Array> polygon = v8::Array::New(1);
-                v8::Local<v8::Array> linear_ring = v8::Array::New(m_way_node_list->size());
-                polygon->Set(0, linear_ring);
-                unsigned int max = m_way_node_list->size() - 1;
-                if (m_reverse) {
-                    for (unsigned int i=0; i <= max; ++i) {
-                        linear_ring->Set(max - i, (*m_way_node_list)[i].position().js_to_array());
-                    }
-                } else {
-                    for (unsigned int i=0; i <= max; ++i) {
-                        linear_ring->Set(i, (*m_way_node_list)[i].position().js_to_array());
-                    }
-                }
-                return scope.Close(polygon);
-            }
-
-            struct JavascriptTemplate : public Osmium::Geometry::Geometry::JavascriptTemplate {
-
-                JavascriptTemplate() : Osmium::Geometry::Geometry::JavascriptTemplate() {
-                    js_template->Set("toArray", v8::FunctionTemplate::New(function_template<Polygon, &Polygon::js_to_array>));
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         }; // class Polygon
 
     } // namespace Geometry
 
 } // namespace Osmium
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-v8::Handle<v8::Value> Osmium::OSM::Way::js_polygon_geom() const {
-    if (m_node_list.has_position() && m_node_list.is_closed()) {
-        Osmium::Geometry::Polygon* geom = new Osmium::Geometry::Polygon(*this);
-        return Osmium::Javascript::Template::get<Osmium::Geometry::Polygon::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::Polygon>(geom);
-    } else {
-        Osmium::Geometry::Null* geom = new Osmium::Geometry::Null();
-        return Osmium::Javascript::Template::get<Osmium::Geometry::Null::JavascriptTemplate>().create_persistent_instance<Osmium::Geometry::Null>(geom);
-    }
-}
-#endif // OSMIUM_WITH_JAVASCRIPT
-
 #endif // OSMIUM_GEOMETRY_POLYGON_HPP
diff --git a/include/osmium/geometry/shplib.hpp b/include/osmium/geometry/shplib.hpp
new file mode 100644
index 0000000..f15565a
--- /dev/null
+++ b/include/osmium/geometry/shplib.hpp
@@ -0,0 +1,222 @@
+#ifndef OSMIUM_GEOMETRY_SHPLIB_HPP
+#define OSMIUM_GEOMETRY_SHPLIB_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#define OSMIUM_COMPILE_WITH_CFLAGS_SHP `geos-config --cflags`
+#define OSMIUM_LINK_WITH_LIBS_SHP -lshp `geos-config --libs`
+
+#include <vector>
+#include <shapefil.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/GeometryCollection.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/LineString.h>
+
+#include <osmium/geometry/point.hpp>
+#include <osmium/geometry/linestring.hpp>
+#include <osmium/geometry/polygon.hpp>
+#include <osmium/geometry/multipolygon.hpp>
+
+namespace Osmium {
+
+    namespace Geometry {
+
+        /**
+         * Create Shapelib geometry of a Point.
+         *
+         * Caller takes ownership. You have to call
+         * SHPDestroyObject() with this geometry when you are done.
+         */
+        inline SHPObject* create_shp_object(const Osmium::Geometry::Point& point) {
+            double x = point.lon();
+            double y = point.lat();
+            return SHPCreateSimpleObject(SHPT_POINT, 1, &x, &y, NULL);
+        }
+
+        namespace {
+
+            /**
+            * Create a SHPObject for a Geometry created from a way and return it.
+            *
+            * Caller takes ownership. You have to call
+            * SHPDestroyObject() with this geometry when you are done.
+            */
+            inline SHPObject* create_line_or_polygon(const Osmium::Geometry::FromWay& from_way, int shp_type) {
+                if (!from_way.nodes().has_position()) {
+                    throw std::runtime_error("node coordinates not available for building way geometry");
+                }
+                int size = from_way.nodes().size();
+                if (size == 0 || size == 1) {
+                    std::cerr << "error building way geometry for way " << from_way.id() << ": must at least contain two nodes" << std::endl;
+                    throw Osmium::Geometry::IllegalGeometry();
+                }
+
+                std::vector<double> lon_checked;
+                lon_checked.reserve(size);
+                lon_checked.push_back(from_way[0].position().lon());
+
+                std::vector<double> lat_checked;
+                lat_checked.reserve(size);
+                lat_checked.push_back(from_way[0].position().lat());
+
+                for (int i=1; i < size; i++) {
+                    if (from_way[i] == from_way[i-1]) {
+                        std::cerr << "warning building way geometry for way " << from_way.id() << ": contains node " << from_way[i].ref() << " twice" << std::endl;
+                    } else if (from_way[i].position() == from_way[i-1].position()) {
+                        std::cerr << "warning building way geometry for way " << from_way.id() << ": contains position " << from_way[i].position() << " twice" << std::endl;
+                    } else {
+                        lon_checked.push_back(from_way[i].position().lon());
+                        lat_checked.push_back(from_way[i].position().lat());
+                    }
+                }
+                if (lon_checked.size() == 1) {
+                    std::cerr << "error building way geometry for way " << from_way.id() << ": must at least contain two different points" << std::endl;
+                    throw Osmium::Geometry::IllegalGeometry();
+                }
+                if (from_way.reverse()) {
+                    std::reverse(lon_checked.begin(), lon_checked.end());
+                    std::reverse(lat_checked.begin(), lat_checked.end());
+                }
+                return SHPCreateSimpleObject(shp_type, lon_checked.size(), &(lon_checked[0]), &(lat_checked[0]), NULL);
+            }
+
+        }
+
+        /**
+         * Create Shapelib geometry of a LineString.
+         *
+         * Caller takes ownership. You have to call
+         * SHPDestroyObject() with this geometry when you are done.
+         */
+        inline SHPObject* create_shp_object(const Osmium::Geometry::LineString& linestring) {
+            return create_line_or_polygon(linestring, SHPT_ARC);
+        }
+
+        /**
+         * Create Shapelib geometry of a Polygon.
+         *
+         * Caller takes ownership. You have to call
+         * SHPDestroyObject() with this geometry when you are done.
+         */
+        inline SHPObject* create_shp_object(const Osmium::Geometry::Polygon& polygon) {
+            return create_line_or_polygon(polygon, SHPT_POLYGON);
+        }
+
+        namespace {
+
+            inline void dump_geometry(const geos::geom::Geometry* g, std::vector<int>& part_start_list, std::vector<double>& x_list, std::vector<double>& y_list) {
+                switch (g->getGeometryTypeId()) {
+                    case geos::geom::GEOS_MULTIPOLYGON:
+                    case geos::geom::GEOS_MULTILINESTRING: {
+                        for (geos::geom::GeometryCollection::const_iterator it = dynamic_cast<const geos::geom::GeometryCollection*>(g)->begin();
+                                it != dynamic_cast<const geos::geom::GeometryCollection*>(g)->end(); ++it) {
+                            dump_geometry(*it, part_start_list, x_list, y_list);
+                        }
+                        break;
+                    }
+                    case geos::geom::GEOS_POLYGON: {
+                        const geos::geom::Polygon* polygon = dynamic_cast<const geos::geom::Polygon*>(g);
+                        dump_geometry(polygon->getExteriorRing(), part_start_list, x_list, y_list);
+                        for (size_t i=0; i < polygon->getNumInteriorRing(); ++i) {
+                            dump_geometry(polygon->getInteriorRingN(i), part_start_list, x_list, y_list);
+                        }
+                        break;
+                    }
+                    case geos::geom::GEOS_LINESTRING:
+                    case geos::geom::GEOS_LINEARRING: {
+                        part_start_list.push_back(x_list.size());
+                        const geos::geom::CoordinateSequence* cs = dynamic_cast<const geos::geom::LineString*>(g)->getCoordinatesRO();
+                        for (size_t i = 0; i < cs->getSize(); ++i) {
+                            x_list.push_back(cs->getX(i));
+                            y_list.push_back(cs->getY(i));
+                        }
+                        break;
+                    }
+                    default:
+                        throw std::runtime_error("invalid geometry type encountered");
+                }
+            }
+
+        }
+
+        /**
+         * Create Shapelib geometry of a MultiPolygon.
+         *
+         * Caller takes ownership. You have to call
+         * SHPDestroyObject() with this geometry when you are done.
+         */
+        inline SHPObject* create_shp_object(const Osmium::Geometry::MultiPolygon& multipolygon) {
+            const geos::geom::MultiPolygon* geos_multipolygon = multipolygon.borrow_geos_geometry();
+
+            std::vector<double> x_list;
+            std::vector<double> y_list;
+            std::vector<int> part_start_list;
+
+            dump_geometry(geos_multipolygon, part_start_list, x_list, y_list);
+
+            return SHPCreateObject(
+                       SHPT_POLYGON,           // nSHPType
+                       -1,                     // iShape
+                       part_start_list.size(), // nParts
+                       &part_start_list[0],    // panPartStart
+                       NULL,                   // panPartType
+                       x_list.size(),          // nVertices,
+                       &x_list[0],             // padfX
+                       &y_list[0],             // padfY
+                       NULL,                   // padfZ
+                       NULL);                  // padfM
+        }
+
+        /**
+         * Create Shapelib geometry of a Geometry.
+         *
+         * Caller takes ownership. You have to call
+         * SHPDestroyObject() with this geometry when you are done.
+         */
+        inline SHPObject* create_shp_object(const Osmium::Geometry::Geometry& geometry) {
+            /* this is rather ugly code but we have to do this here because we can't make this
+               free function into a member function where C++ would to the polymorphy thing for us. */
+            const Osmium::Geometry::Point* point = dynamic_cast<const Osmium::Geometry::Point*>(&geometry);
+            if (point) {
+                return create_shp_object(*point);
+            }
+            const Osmium::Geometry::LineString* linestring = dynamic_cast<const Osmium::Geometry::LineString*>(&geometry);
+            if (linestring) {
+                return create_shp_object(*linestring);
+            }
+            const Osmium::Geometry::Polygon* polygon = dynamic_cast<const Osmium::Geometry::Polygon*>(&geometry);
+            if (polygon) {
+                return create_shp_object(*polygon);
+            }
+            const Osmium::Geometry::MultiPolygon* multipolygon = dynamic_cast<const Osmium::Geometry::MultiPolygon*>(&geometry);
+            if (multipolygon) {
+                return create_shp_object(*multipolygon);
+            }
+            return NULL;
+        }
+
+    } // namespace Geometry
+
+} // namespace Osmium
+
+#endif // OSMIUM_GEOMETRY_SHPLIB_HPP
diff --git a/include/osmium/handler.hpp b/include/osmium/handler.hpp
index 4de2913..27c4229 100644
--- a/include/osmium/handler.hpp
+++ b/include/osmium/handler.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -24,14 +24,19 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 #include <boost/utility.hpp>
 
+#include <osmium/debug.hpp>
+
 #include <osmium/osm/meta.hpp>
 #include <osmium/osm/node.hpp>
 #include <osmium/osm/way.hpp>
 #include <osmium/osm/relation.hpp>
-#include <osmium/osm/area.hpp>
 
 namespace Osmium {
 
+    namespace OSM {
+       class Area;
+    }  // namespace OSM
+
     /**
      * @brief Handlers operate on %OSM data through callbacks.
      *
@@ -41,6 +46,19 @@ namespace Osmium {
     namespace Handler {
 
         /**
+         * Handlers can throw this exception to show that they are done.
+         * When a handler, for instance, is only interested in nodes, it
+         * can throw this in the after_nodes() method. The parser will
+         * stop reading the input file after this.
+         *
+         * Note that when you write a handler that calls other handlers
+         * that can throw this, you might have to catch this exception
+         * in your handler.
+         */
+        class StopReading {
+        };
+
+        /**
          * Base class for all handler classes.
          * Defines empty methods that can be overwritten in child classes.
          *
@@ -48,11 +66,16 @@ namespace Osmium {
          * Only overwrite the methods you actually use. They must be declared public.
          * If you overwrite the constructor, call the Base constructor without arguments.
          */
-        class Base : boost::noncopyable {
+        class Base : boost::noncopyable, public Osmium::WithDebug {
 
         public:
 
-            Base() {
+            Base() :
+                Osmium::WithDebug() {
+            }
+
+            // Destructor is not virtual as this class is not intended to be used polymorphically
+            ~Base() {
             }
 
             void init(Osmium::OSM::Meta&) const {
@@ -85,7 +108,7 @@ namespace Osmium {
             void after_relations() const {
             }
 
-            void area(Osmium::OSM::Area*) const {
+            void area(const shared_ptr<Osmium::OSM::Area const>&) const {
             }
 
             void final() const {
@@ -103,63 +126,161 @@ namespace Osmium {
 
         public:
 
-            Forward(THandler* handler) : Base(), m_handler(handler) {
+            Forward(THandler& next_handler) :
+                Base(),
+                m_next_handler(next_handler) {
             }
 
             void init(Osmium::OSM::Meta& meta) const {
-                m_handler->init(meta);
+                m_next_handler.init(meta);
             }
 
             void before_nodes() const {
-                m_handler->before_nodes();
+                m_next_handler.before_nodes();
             }
 
             void node(const shared_ptr<Osmium::OSM::Node>& node) const {
-                m_handler->node(node);
+                m_next_handler.node(node);
             }
 
             void after_nodes() const {
-                m_handler->after_nodes();
+                m_next_handler.after_nodes();
             }
 
             void before_ways() const {
-                m_handler->before_ways();
+                m_next_handler.before_ways();
             }
 
             void way(const shared_ptr<Osmium::OSM::Way>& way) const {
-                m_handler->way(way);
+                m_next_handler.way(way);
             }
 
             void after_ways() const {
-                m_handler->after_ways();
+                m_next_handler.after_ways();
             }
 
             void before_relations() const {
-                m_handler->before_relations();
+                m_next_handler.before_relations();
             }
 
             void relation(const shared_ptr<Osmium::OSM::Relation>& relation) const {
-                m_handler->relation(relation);
+                m_next_handler.relation(relation);
             }
 
             void after_relations() const {
-                m_handler->after_relations();
+                m_next_handler.after_relations();
             }
 
-            void area(Osmium::OSM::Area* area) const {
-                m_handler->area(area);
+            void area(const shared_ptr<Osmium::OSM::Area>& area) const {
+                m_next_handler.area(area);
             }
 
             void final() const {
-                m_handler->final();
+                m_next_handler.final();
+            }
+
+            void set_debug_level(int debug) {
+                m_next_handler.set_debug_level(debug);
+            }
+
+        protected:
+
+            THandler& next_handler() const {
+                return m_next_handler;
             }
 
         private:
 
-            THandler* m_handler;
+            THandler& m_next_handler;
 
         }; // class Forward
 
+        /**
+         * This handler calls the two handlers given as argument in sequence
+         * in each method.
+         */
+        template <class THandler1, class THandler2>
+        class Sequence {
+
+        public:
+
+            Sequence(THandler1& handler1, THandler2& handler2) :
+                m_handler1(handler1),
+                m_handler2(handler2) {
+            }
+
+            void init(Osmium::OSM::Meta& meta) const {
+                m_handler1.init(meta);
+                m_handler2.init(meta);
+            }
+
+            void before_nodes() const {
+                m_handler1.before_nodes();
+                m_handler2.before_nodes();
+            }
+
+            void node(const shared_ptr<Osmium::OSM::Node>& node) const {
+                m_handler1.node(node);
+                m_handler2.node(node);
+            }
+
+            void after_nodes() const {
+                m_handler1.after_nodes();
+                m_handler2.after_nodes();
+            }
+
+            void before_ways() const {
+                m_handler1.before_ways();
+                m_handler2.before_ways();
+            }
+
+            void way(const shared_ptr<Osmium::OSM::Way>& way) const {
+                m_handler1.way(way);
+                m_handler2.way(way);
+            }
+
+            void after_ways() const {
+                m_handler1.after_ways();
+                m_handler2.after_ways();
+            }
+
+            void before_relations() const {
+                m_handler1.before_relations();
+                m_handler2.before_relations();
+            }
+
+            void relation(const shared_ptr<Osmium::OSM::Relation>& relation) const {
+                m_handler1.relation(relation);
+                m_handler2.relation(relation);
+            }
+
+            void after_relations() const {
+                m_handler1.after_relations();
+                m_handler2.after_relations();
+            }
+
+            void area(const shared_ptr<Osmium::OSM::Area>& area) const {
+                m_handler1.area(area);
+                m_handler2.area(area);
+            }
+
+            void final() const {
+                m_handler1.final();
+                m_handler2.final();
+            }
+
+            void set_debug_level(int debug) {
+                m_handler1.set_debug_level(debug);
+                m_handler2.set_debug_level(debug);
+            }
+
+        private:
+
+            THandler1& m_handler1;
+            THandler2& m_handler2;
+
+        }; // class Sequence
+
     } // namespace Handler
 
 } // namespace Osmium
diff --git a/include/osmium/handler/coordinates_for_ways.hpp b/include/osmium/handler/coordinates_for_ways.hpp
index 407807b..69ae5d2 100644
--- a/include/osmium/handler/coordinates_for_ways.hpp
+++ b/include/osmium/handler/coordinates_for_ways.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -41,9 +41,9 @@ namespace Osmium {
         public:
 
             CoordinatesForWays(TStoragePosIDs& storage_pos,
-                               TStorageNegIDs& storage_neg)
-                : m_storage_pos(storage_pos),
-                  m_storage_neg(storage_neg) {
+                               TStorageNegIDs& storage_neg) :
+                m_storage_pos(storage_pos),
+                m_storage_neg(storage_neg) {
             }
 
             /**
@@ -52,20 +52,14 @@ namespace Osmium {
             void node(const shared_ptr<Osmium::OSM::Node const>& node) {
                 int64_t id = node->id();
                 if (id >= 0) {
-                    m_storage_pos.set( id, node->position());
+                    m_storage_pos.set(id, node->position());
                 } else {
                     m_storage_neg.set(-id, node->position());
                 }
             }
 
-            void after_nodes() const {
-                if (Osmium::debug()) {
-                    std::cerr << "Memory used for node coordinates storage (approximate):\n  for positive IDs: "
-                              << m_storage_pos.used_memory() / (1024 * 1024)
-                              << " MiB\n  for negative IDs: "
-                              << m_storage_neg.used_memory() / (1024 * 1024)
-                              << " MiB\n";
-                }
+            Osmium::OSM::Position get_node_pos(const int64_t id) const {
+                return id >= 0 ? m_storage_pos[id] : m_storage_neg[-id];
             }
 
             /**
diff --git a/include/osmium/handler/debug.hpp b/include/osmium/handler/debug.hpp
index 2645481..01a8237 100644
--- a/include/osmium/handler/debug.hpp
+++ b/include/osmium/handler/debug.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,6 +22,11 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#include <iomanip>
+#include <iostream>
+
+#include <osmium/handler.hpp>
+
 namespace Osmium {
 
     namespace Handler {
@@ -34,96 +39,100 @@ namespace Osmium {
 
         public:
 
-            Debug(bool has_multiple_object_versions=false) : Base(), m_has_multiple_object_versions(has_multiple_object_versions) {
+            Debug(bool has_multiple_object_versions=false, std::ostream &output_stream = std::cout) :
+                Base(),
+                m_has_multiple_object_versions(has_multiple_object_versions),
+                m_output_stream(output_stream) {
             }
 
             void init(Osmium::OSM::Meta& meta) {
-                std::cout << "meta:\n";
+                m_output_stream << "meta:\n  generator=" << meta.generator() << "\n";
                 if (meta.has_multiple_object_versions()) {
                     m_has_multiple_object_versions = true;
                 }
 
                 if (meta.bounds().defined()) {
-                    std::cout << "  bounds=" << meta.bounds() << "\n";
+                    m_output_stream << "  bounds=" << meta.bounds() << "\n";
                 }
             }
 
             void before_nodes() const {
-                std::cout << "before_nodes\n";
+                m_output_stream << "before_nodes\n";
             }
 
             void node(const shared_ptr<Osmium::OSM::Node const>& node) const {
-                std::cout << "node:\n";
+                m_output_stream << "node:\n";
                 print_meta(node);
                 const Osmium::OSM::Position& position = node->position();
-                std::cout << "  lon=" << std::fixed << std::setprecision(7) << position.lon() << "\n";
-                std::cout << "  lat=" << std::fixed << std::setprecision(7) << position.lat() << "\n";
+                m_output_stream << "  lon=" << std::fixed << std::setprecision(7) << position.lon() << "\n";
+                m_output_stream << "  lat=" << std::fixed << std::setprecision(7) << position.lat() << "\n";
             }
 
             void after_nodes() const {
-                std::cout << "after_nodes\n";
+                m_output_stream << "after_nodes\n";
             }
 
             void before_ways() const {
-                std::cout << "before_ways\n";
+                m_output_stream << "before_ways\n";
             }
 
             void way(const shared_ptr<Osmium::OSM::Way const>& way) const {
-                std::cout << "way:\n";
+                m_output_stream << "way:\n";
                 print_meta(way);
-                std::cout << "  node_count=" << way->node_count() << "\n";
-                std::cout << "  nodes:\n";
+                m_output_stream << "  node_count=" << way->nodes().size() << "\n";
+                m_output_stream << "  nodes:\n";
                 Osmium::OSM::WayNodeList::const_iterator end = way->nodes().end();
                 for (Osmium::OSM::WayNodeList::const_iterator it = way->nodes().begin(); it != end; ++it) {
-                    std::cout << "    ref=" << it->ref() << "\n";
+                    m_output_stream << "    ref=" << it->ref() << "\n";
                 }
             }
 
             void after_ways() const {
-                std::cout << "after_ways\n";
+                m_output_stream << "after_ways\n";
             }
 
             void before_relations() const {
-                std::cout << "before_relations\n";
+                m_output_stream << "before_relations\n";
             }
 
             void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) const {
-                std::cout << "relation:\n";
+                m_output_stream << "relation:\n";
                 print_meta(relation);
-                std::cout << "  members: (count=" << relation->members().size() << ")\n";
+                m_output_stream << "  members: (count=" << relation->members().size() << ")\n";
                 Osmium::OSM::RelationMemberList::const_iterator end = relation->members().end();
                 for (Osmium::OSM::RelationMemberList::const_iterator it = relation->members().begin(); it != end; ++it) {
-                    std::cout << "    type=" << it->type() << " ref=" << it->ref() << " role=|" << it->role() << "|" << "\n";
+                    m_output_stream << "    type=" << it->type() << " ref=" << it->ref() << " role=|" << it->role() << "|" << "\n";
                 }
             }
 
             void after_relations() const {
-                std::cout << "after_relations\n";
+                m_output_stream << "after_relations\n";
             }
 
             void final() const {
-                std::cout << "final\n";
+                m_output_stream << "final\n";
             }
 
         private:
 
             bool m_has_multiple_object_versions;
+            std::ostream& m_output_stream;
 
             void print_meta(const shared_ptr<Osmium::OSM::Object const>& object) const {
-                std::cout <<   "  id="        << object->id()
+                m_output_stream <<   "  id="        << object->id()
                           << "\n  version="   << object->version()
                           << "\n  uid="       << object->uid()
                           << "\n  user=|"     << object->user() << "|"
                           << "\n  changeset=" << object->changeset()
                           << "\n  timestamp=" << object->timestamp_as_string();
                 if (m_has_multiple_object_versions) {
-                    std::cout << "\n  visible=" << (object->visible() ? "yes" : "no")
+                    m_output_stream << "\n  visible=" << (object->visible() ? "yes" : "no")
                               << "\n  endtime=" << object->endtime_as_string();
                 }
-                std::cout << "\n  tags: (count=" << object->tags().size() << ")\n";
+                m_output_stream << "\n  tags: (count=" << object->tags().size() << ")\n";
                 Osmium::OSM::TagList::const_iterator end = object->tags().end();
                 for (Osmium::OSM::TagList::const_iterator it = object->tags().begin(); it != end; ++it) {
-                    std::cout << "    k=|" << it->key() << "| v=|" << it->value() << "|" << "\n";
+                    m_output_stream << "    k=|" << it->key() << "| v=|" << it->value() << "|" << "\n";
                 }
             }
 
diff --git a/include/osmium/handler/endtime.hpp b/include/osmium/handler/endtime.hpp
index 5a78670..3129275 100644
--- a/include/osmium/handler/endtime.hpp
+++ b/include/osmium/handler/endtime.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,6 +22,8 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#include <osmium/handler.hpp>
+
 namespace Osmium {
 
     namespace Handler {
@@ -36,15 +38,20 @@ namespace Osmium {
 
         public:
 
-            EndTime(THandler* handler) : Base(), m_handler(handler) {
+            EndTime(THandler& handler) :
+                Base(),
+                m_handler(handler),
+                m_last_node(),
+                m_last_way(),
+                m_last_relation() {
             }
 
             void init(Osmium::OSM::Meta& meta) {
-                m_handler->init(meta);
+                m_handler.init(meta);
             }
 
             void before_nodes() {
-                m_handler->before_nodes();
+                m_handler.before_nodes();
             }
 
             void node(const shared_ptr<Osmium::OSM::Node>& node) {
@@ -52,21 +59,21 @@ namespace Osmium {
                     if (node->id() == m_last_node->id()) {
                         m_last_node->endtime(node->timestamp());
                     }
-                    m_handler->node(m_last_node);
+                    m_handler.node(m_last_node);
                 }
                 m_last_node = node;
             }
 
             void after_nodes() {
                 if (m_last_node) {
-                    m_handler->node(m_last_node);
+                    m_handler.node(m_last_node);
                     m_last_node.reset();
                 }
-                m_handler->after_nodes();
+                m_handler.after_nodes();
             }
 
             void before_ways() {
-                m_handler->before_ways();
+                m_handler.before_ways();
             }
 
             void way(const shared_ptr<Osmium::OSM::Way>& way) {
@@ -74,21 +81,21 @@ namespace Osmium {
                     if (way->id() == m_last_way->id()) {
                         m_last_way->endtime(way->timestamp());
                     }
-                    m_handler->way(m_last_way);
+                    m_handler.way(m_last_way);
                 }
                 m_last_way = way;
             }
 
             void after_ways() {
                 if (m_last_way) {
-                    m_handler->way(m_last_way);
+                    m_handler.way(m_last_way);
                     m_last_way.reset();
                 }
-                m_handler->after_ways();
+                m_handler.after_ways();
             }
 
             void before_relations() {
-                m_handler->before_relations();
+                m_handler.before_relations();
             }
 
             void relation(const shared_ptr<Osmium::OSM::Relation>& relation) {
@@ -96,26 +103,26 @@ namespace Osmium {
                     if (relation->id() == m_last_relation->id()) {
                         m_last_relation->endtime(relation->timestamp());
                     }
-                    m_handler->relation(m_last_relation);
+                    m_handler.relation(m_last_relation);
                 }
                 m_last_relation = relation;
             }
 
             void after_relations() {
                 if (m_last_relation) {
-                    m_handler->relation(m_last_relation);
+                    m_handler.relation(m_last_relation);
                     m_last_relation.reset();
                 }
-                m_handler->after_relations();
+                m_handler.after_relations();
             }
 
             void final() {
-                m_handler->final();
+                m_handler.final();
             }
 
         private:
 
-            THandler* m_handler;
+            THandler& m_handler;
 
             shared_ptr<Osmium::OSM::Node>     m_last_node;
             shared_ptr<Osmium::OSM::Way>      m_last_way;
diff --git a/include/osmium/handler/find_bbox.hpp b/include/osmium/handler/find_bbox.hpp
index c11b8b4..23e792e 100644
--- a/include/osmium/handler/find_bbox.hpp
+++ b/include/osmium/handler/find_bbox.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -33,7 +33,9 @@ namespace Osmium {
 
         public:
 
-            FindBbox() : Base(), m_bounds() {
+            FindBbox() :
+                Base(),
+                m_bounds() {
             }
 
             const Osmium::OSM::Bounds bounds() const {
@@ -45,7 +47,7 @@ namespace Osmium {
             }
 
             void after_nodes() const {
-                throw Osmium::Input::StopReading();
+                throw StopReading();
             }
 
         private:
diff --git a/include/osmium/handler/multipolygon.hpp b/include/osmium/handler/multipolygon.hpp
deleted file mode 100644
index 3cf2aac..0000000
--- a/include/osmium/handler/multipolygon.hpp
+++ /dev/null
@@ -1,181 +0,0 @@
-#ifndef OSMIUM_HANDLER_MULTIPOLYGON_HPP
-#define OSMIUM_HANDLER_MULTIPOLYGON_HPP
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#include <google/sparse_hash_map>
-
-#include <osmium/handler.hpp>
-#include <osmium/osm/way.hpp>
-#include <osmium/osm/area.hpp>
-
-namespace Osmium {
-
-    namespace Handler {
-
-        class Multipolygon : public Base {
-
-            /// a list of areas that need to be completed
-            std::vector<Osmium::OSM::AreaFromRelation*> m_areas;
-
-            // a map from way_id to a vector of indexes into the areas array
-            // this is used to find in which multipolygon relations a way is
-            typedef google::sparse_hash_map<osm_object_id_t, std::vector<osm_object_id_t> > way2areaidx_t;
-            way2areaidx_t m_way2areaidx;
-
-            bool m_attempt_repair;
-            void (*m_callback_area)(Osmium::OSM::Area*);
-
-            uint64_t m_count_ways_in_all_areas;
-
-        public:
-
-            Multipolygon(bool attempt_repair,
-                         void (*callback_area)(Osmium::OSM::Area*))
-                : Base(),
-                  m_areas(),
-                  m_way2areaidx(),
-                  m_attempt_repair(attempt_repair),
-                  m_callback_area(callback_area),
-                  m_count_ways_in_all_areas(0) {
-            }
-
-            // in pass 1
-            void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
-                const char* type = relation->tags().get_tag_by_key("type");
-
-                // ignore relations without "type" tag
-                if (!type) {
-                    return;
-                }
-
-                bool is_boundary;
-                if (strcmp(type, "multipolygon") == 0) {
-                    is_boundary = false;
-                } else if (strcmp(type, "boundary") == 0) {
-                    is_boundary = true;
-                } else {
-                    return;
-                }
-
-                int num_ways = 0;
-                for (osm_sequence_id_t i=0; i < relation->members().size(); i++) {
-                    const Osmium::OSM::RelationMember* member = relation->get_member(i);
-                    if (member->type() == 'w') {
-                        m_way2areaidx[member->ref()].push_back(m_areas.size());
-                        num_ways++;
-                    } else {
-                        std::cerr << "warning: multipolygon/boundary relation "
-                                  << relation->id()
-                                  << " has a non-way member which was ignored\n";
-                    }
-                }
-
-                m_count_ways_in_all_areas += num_ways;
-
-                Osmium::OSM::AreaFromRelation* area = new Osmium::OSM::AreaFromRelation(new Osmium::OSM::Relation(*relation), is_boundary, num_ways, m_callback_area, m_attempt_repair);
-                m_areas.push_back(area);
-            }
-
-            // in pass 1
-            void after_relations() {
-                if (Osmium::debug()) {
-                    std::cerr << "found " << m_areas.size() << " areas (each needs "
-                              << sizeof(Osmium::OSM::Area) << " bytes, thats together about "
-                              << sizeof(Osmium::OSM::Area) * m_areas.size() / (1024 * 1024) << "MB)\n"
-                              << "they used " << m_count_ways_in_all_areas << " ways (each will need "
-                              << sizeof(Osmium::OSM::Way) << " bytes, thats in the worst case together about "
-                              << sizeof(Osmium::OSM::Way) * m_count_ways_in_all_areas / (1024 * 1024) << "MB)\n";
-                }
-            }
-
-            // in pass 2
-            void way(const shared_ptr<Osmium::OSM::Way>& way) {
-                way2areaidx_t::const_iterator way2areaidx_iterator(m_way2areaidx.find(way->id()));
-
-                if (way2areaidx_iterator == m_way2areaidx.end()) { // not in any relation
-                    if (way->is_closed() && way->node_count() >= 4) { // way is closed and has enough nodes, build simple multipolygon
-#ifdef OSMIUM_WITH_GEOS
-                        Osmium::Geometry::Polygon polygon(*way);
-                        Osmium::OSM::AreaFromWay* area = new Osmium::OSM::AreaFromWay(way.get(), polygon.create_geos_geometry());
-#else
-                        Osmium::OSM::AreaFromWay* area = new Osmium::OSM::AreaFromWay(way.get());
-#endif // OSMIUM_WITH_GEOS
-                        if (Osmium::debug()) {
-                            std::cerr << "MP simple way_id=" << way->id() << "\n";
-                        }
-                        m_callback_area(area);
-                        delete area;
-                    }
-                    return;
-                }
-
-                // is in at least one multipolygon relation
-
-                std::vector<osm_object_id_t> v = way2areaidx_iterator->second;
-                if (Osmium::debug()) std::cerr << "MP way_id=" << way->id() << " is in " << v.size() << " areas\n";
-
-                // go through all the areas this way is in
-                for (unsigned int i=0; i < v.size(); i++) {
-                    Osmium::OSM::AreaFromRelation* area = m_areas[v[i]];
-                    if (!area) {
-                        throw std::runtime_error("Zero multipolygon. This should not happen. Reason can be a way appearing more than once in your input file.");
-                    }
-                    if (Osmium::debug()) {
-                        std::cerr << "MP multi way_id=" << way->id() << " is in relation_id=" << area->id() << "\n";
-                    }
-
-                    // store copy of current way in multipolygon
-                    area->add_member_way(way.get());
-
-                    if (area->is_complete()) {
-                        area->handle_complete_multipolygon();
-                        m_areas[v[i]] = NULL;
-                        delete area;
-                    }
-                }
-            }
-
-            // in pass 2
-            void after_ways() {
-                m_way2areaidx.clear();
-            }
-
-            void init(Osmium::OSM::Meta&) {
-#ifdef OSMIUM_WITH_MULTIPOLYGON_PROFILING
-                Osmium::OSM::AreaFromRelation::init_timings();
-#endif // OSMIUM_WITH_MULTIPOLYGON_PROFILING
-            }
-
-            void final() {
-#ifdef OSMIUM_WITH_MULTIPOLYGON_PROFILING
-                Osmium::OSM::AreaFromRelation::print_timings();
-#endif // OSMIUM_WITH_MULTIPOLYGON_PROFILING
-            }
-
-        }; // class Multipolygon
-
-    } // namespace Handler
-
-} // namespace Osmium
-
-#endif // OSMIUM_HANDLER_MULTIPOLYGON_HPP
diff --git a/include/osmium/handler/progress.hpp b/include/osmium/handler/progress.hpp
index a453c5d..dfb5b59 100644
--- a/include/osmium/handler/progress.hpp
+++ b/include/osmium/handler/progress.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -24,6 +24,9 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 #include <unistd.h>
 #include <sys/time.h>
+#include <iostream>
+
+#include <osmium/handler.hpp>
 
 namespace Osmium {
 
@@ -43,24 +46,24 @@ namespace Osmium {
          */
         class Progress : public Base {
 
-            uint64_t count_nodes;
-            uint64_t count_ways;
-            uint64_t count_relations;
+            uint64_t m_count_nodes;
+            uint64_t m_count_ways;
+            uint64_t m_count_relations;
 
-            int step;
+            int m_step;
 
-            bool is_a_tty;
+            bool m_is_a_tty;
 
-            timeval first_node;
-            timeval first_way;
-            timeval first_relation;
+            timeval m_first_node;
+            timeval m_first_way;
+            timeval m_first_relation;
 
             void update_display(bool show_per_second=true) const {
-                std::cout << "[" << count_nodes << "]";
-                if (count_ways > 0 || count_relations > 0) {
-                    std::cout << " [" << count_ways << "]";
-                    if (count_relations > 0) {
-                        std::cout << " [" << count_relations << "]";
+                std::cout << "[" << m_count_nodes << "]";
+                if (m_count_ways > 0 || m_count_relations > 0) {
+                    std::cout << " [" << m_count_ways << "]";
+                    if (m_count_relations > 0) {
+                        std::cout << " [" << m_count_relations << "]";
                     }
                 }
 
@@ -68,17 +71,17 @@ namespace Osmium {
                     timeval now;
                     gettimeofday(&now, 0);
 
-                    if (count_relations > 0) {
-                        float relation_diff = (now.tv_sec - first_relation.tv_sec) * 1000000 + (now.tv_usec - first_relation.tv_usec);
-                        int relations_per_sec = (float)count_relations / relation_diff * 1000000;
+                    if (m_count_relations > 0) {
+                        float relation_diff = (now.tv_sec - m_first_relation.tv_sec) * 1000000 + (now.tv_usec - m_first_relation.tv_usec);
+                        int relations_per_sec = static_cast<float>(m_count_relations) / relation_diff * 1000000;
                         std::cout << " (" << relations_per_sec << " Relations per second)   ";
-                    } else if (count_ways > 0) {
-                        float way_diff = (now.tv_sec - first_way.tv_sec) * 1000000 + (now.tv_usec - first_way.tv_usec);
-                        int ways_per_sec = (float)count_ways / way_diff * 1000000;
+                    } else if (m_count_ways > 0) {
+                        float way_diff = (now.tv_sec - m_first_way.tv_sec) * 1000000 + (now.tv_usec - m_first_way.tv_usec);
+                        int ways_per_sec = static_cast<float>(m_count_ways) / way_diff * 1000000;
                         std::cout << " (" << ways_per_sec << " Ways per second)   ";
-                    } else if (count_nodes > 0) {
-                        float node_diff = (now.tv_sec - first_node.tv_sec) * 1000000 + (now.tv_usec - first_node.tv_usec);
-                        int nodes_per_sec = (float)count_nodes / node_diff * 1000000;
+                    } else if (m_count_nodes > 0) {
+                        float node_diff = (now.tv_sec - m_first_node.tv_sec) * 1000000 + (now.tv_usec - m_first_node.tv_usec);
+                        int nodes_per_sec = static_cast<float>(m_count_nodes) / node_diff * 1000000;
                         std::cout << " (" << nodes_per_sec << " Nodes per second)   ";
                     }
                 } else {
@@ -93,13 +96,19 @@ namespace Osmium {
 
             /**
              * Initialize handler.
-             * @param s Step, after how many nodes/ways/relations the display
-             *          should be updated. (default 1000).
+             * @param step after how many nodes/ways/relations the display
+             *             should be updated. (default 1000).
              */
-            Progress(int s=1000) : Base(), count_nodes(0), count_ways(0), count_relations(0), step(s), is_a_tty(false), first_node(), first_way(), first_relation() {
-                if (isatty(1)) {
-                    is_a_tty = true;
-                }
+            Progress(int step=1000) :
+                Base(),
+                m_count_nodes(0),
+                m_count_ways(0),
+                m_count_relations(0),
+                m_step(step),
+                m_is_a_tty(isatty(1)),
+                m_first_node(),
+                m_first_way(),
+                m_first_relation() {
             }
 
             void hide_cursor() const {
@@ -110,72 +119,73 @@ namespace Osmium {
                 std::cout << "\x1b[?25h";
             }
 
-            void init(Osmium::OSM::Meta&) const {
-                if (is_a_tty) {
+            void init(const Osmium::OSM::Meta&) const {
+                if (m_is_a_tty) {
                     hide_cursor();
                     update_display();
                 }
             }
 
             void node(const shared_ptr<Osmium::OSM::Node const>& /*object*/) {
-                if (first_node.tv_sec == 0) {
-                    gettimeofday(&first_node, 0);
+                if (m_first_node.tv_sec == 0) {
+                    gettimeofday(&m_first_node, 0);
                 }
-                if (is_a_tty && ++count_nodes % step == 0) {
+                if (m_is_a_tty && ++m_count_nodes % m_step == 0) {
                     update_display();
                 }
             }
 
             void way(const shared_ptr<Osmium::OSM::Way const>& /*object*/) {
-                if (first_way.tv_sec == 0) {
-                    gettimeofday(&first_way, 0);
+                if (m_first_way.tv_sec == 0) {
+                    gettimeofday(&m_first_way, 0);
                 }
-                if (is_a_tty && ++count_ways % step == 0) {
+                if (m_is_a_tty && ++m_count_ways % m_step == 0) {
                     update_display();
                 }
             }
 
             void relation(const shared_ptr<Osmium::OSM::Relation const>& /*object*/) {
-                if (first_relation.tv_sec == 0) {
-                    gettimeofday(&first_relation, 0);
+                if (m_first_relation.tv_sec == 0) {
+                    gettimeofday(&m_first_relation, 0);
                 }
-                if (is_a_tty && ++count_relations % step == 0) {
+                if (m_is_a_tty && ++m_count_relations % m_step == 0) {
                     update_display();
                 }
             }
 
             void final() const {
-                if (is_a_tty) {
-                    update_display(false);
-                    std::cout << std::endl;
+                if (! m_is_a_tty) {
+                    return;
+                }
 
-                    std::cout << "  Average: ";
+                update_display(false);
 
-                    timeval now;
-                    gettimeofday(&now, 0);
+                std::cout << "\n  Average: ";
 
-                    if (count_nodes > 0) {
-                        float node_diff = (first_way.tv_sec - first_node.tv_sec) * 1000000 + (first_way.tv_usec - first_node.tv_usec);
-                        int nodes_per_sec = (float)count_nodes / node_diff * 1000000;
-                        std::cout << nodes_per_sec << " Nodes ";
-                    }
+                timeval now;
+                gettimeofday(&now, 0);
 
-                    if (count_ways > 0) {
-                        float way_diff = (first_relation.tv_sec - first_way.tv_sec) * 1000000 + (first_relation.tv_usec - first_way.tv_usec);
-                        int ways_per_sec = (float)count_ways / way_diff * 1000000;
-                        std::cout << ways_per_sec << " Ways ";
-                    }
+                if (m_count_nodes > 0) {
+                    float node_diff = (m_first_way.tv_sec - m_first_node.tv_sec) * 1000000 + (m_first_way.tv_usec - m_first_node.tv_usec);
+                    int nodes_per_sec = static_cast<float>(m_count_nodes) / node_diff * 1000000;
+                    std::cout << nodes_per_sec << " Nodes ";
+                }
 
-                    if (count_relations > 0) {
-                        float relation_diff = (now.tv_sec - first_relation.tv_sec) * 1000000 + (now.tv_usec - first_relation.tv_usec);
-                        int relations_per_sec = (float)count_relations / relation_diff * 1000000;
-                        std::cout << relations_per_sec << " Relations ";
-                    }
+                if (m_count_ways > 0) {
+                    float way_diff = (m_first_relation.tv_sec - m_first_way.tv_sec) * 1000000 + (m_first_relation.tv_usec - m_first_way.tv_usec);
+                    int ways_per_sec = static_cast<float>(m_count_ways) / way_diff * 1000000;
+                    std::cout << ways_per_sec << " Ways ";
+                }
 
-                    show_cursor();
-                    std::cout  << "per second" << std::endl;
-                    std::cout.flush();
+                if (m_count_relations > 0) {
+                    float relation_diff = (now.tv_sec - m_first_relation.tv_sec) * 1000000 + (now.tv_usec - m_first_relation.tv_usec);
+                    int relations_per_sec = static_cast<float>(m_count_relations) / relation_diff * 1000000;
+                    std::cout << relations_per_sec << " Relations ";
                 }
+
+                show_cursor();
+                std::cout  << "per second\n";
+                std::cout.flush();
             }
 
         }; // class Progress
diff --git a/include/osmium/handler/range_from_history.hpp b/include/osmium/handler/range_from_history.hpp
index 5a122da..d7ee164 100644
--- a/include/osmium/handler/range_from_history.hpp
+++ b/include/osmium/handler/range_from_history.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,6 +22,8 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#include <osmium/handler.hpp>
+
 namespace Osmium {
 
     namespace Handler {
@@ -34,67 +36,36 @@ namespace Osmium {
          * after the EndTime() handler.
          */
         template <class THandler>
-        class RangeFromHistory : public Base {
+        class RangeFromHistory : public Osmium::Handler::Forward<THandler> {
 
         public:
 
-            RangeFromHistory(THandler* handler, time_t from, time_t to) : Base(), m_handler(handler), m_from(from), m_to(to) {
-            }
-
-            void init(Osmium::OSM::Meta& meta) {
-                m_handler->init(meta);
-            }
-
-            void before_nodes() {
-                m_handler->before_nodes();
+            RangeFromHistory(THandler& handler, time_t from, time_t to) :
+                Forward<THandler>(handler),
+                m_from(from),
+                m_to(to) {
             }
 
             void node(const shared_ptr<Osmium::OSM::Node>& node) {
                 if ((node->endtime() == 0 || node->endtime() >= m_from) && node->timestamp() <= m_to) {
-                    m_handler->node(node);
+                    Forward<THandler>::next_handler().node(node);
                 }
             }
 
-            void after_nodes() {
-                m_handler->after_nodes();
-            }
-
-            void before_ways() {
-                m_handler->before_ways();
-            }
-
             void way(const shared_ptr<Osmium::OSM::Way>& way) {
                 if ((way->endtime() == 0 || way->endtime() >= m_from) && way->timestamp() <= m_to) {
-                    m_handler->way(way);
+                    Forward<THandler>::next_handler().way(way);
                 }
             }
 
-            void after_ways() {
-                m_handler->after_ways();
-            }
-
-            void before_relations() {
-                m_handler->before_relations();
-            }
-
             void relation(const shared_ptr<Osmium::OSM::Relation>& relation) {
                 if ((relation->endtime() == 0 || relation->endtime() >= m_from) && relation->timestamp() <= m_to) {
-                    m_handler->relation(relation);
+                    Forward<THandler>::next_handler().relation(relation);
                 }
             }
 
-            void after_relations() {
-                m_handler->after_relations();
-            }
-
-            void final() {
-                m_handler->final();
-            }
-
         private:
 
-            THandler* m_handler;
-
             const time_t m_from;
             const time_t m_to;
 
diff --git a/include/osmium/handler/statistics.hpp b/include/osmium/handler/statistics.hpp
deleted file mode 100644
index 1f6f883..0000000
--- a/include/osmium/handler/statistics.hpp
+++ /dev/null
@@ -1,240 +0,0 @@
-#ifndef OSMIUM_HANDLER_STATISTICS_HPP
-#define OSMIUM_HANDLER_STATISTICS_HPP
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#include <osmium/utils/sqlite.hpp>
-
-namespace Osmium {
-
-    namespace Handler {
-
-        /**
-         * Osmium handler that collects basic statistics from OSM data and
-         * writes it to a Sqlite database.
-         */
-        class Statistics : public Base {
-
-        public:
-
-            Statistics() : Base() {
-                // if you change anything in this array, also change the corresponding struct below
-                static const char *sn[] = {
-                    "nodes",
-                    "nodes_without_tags",
-                    "node_tags",
-                    "max_node_id",
-                    "max_tags_on_node",
-                    "ways",
-                    "way_tags",
-                    "way_nodes",
-                    "max_way_id",
-                    "max_tags_on_way",
-                    "max_nodes_on_way",
-                    "closed_ways",
-                    "relations",
-                    "relation_tags",
-                    "relation_members",
-                    "max_relation_id",
-                    "max_tags_on_relation",
-                    "max_members_on_relation",
-                    "max_user_id",
-                    "anon_user_objects",
-                    "max_node_version",
-                    "max_way_version",
-                    "max_relation_version",
-                    "sum_node_version",
-                    "sum_way_version",
-                    "sum_relation_version",
-                    "max_changeset_id",
-                    0    // last element (sentinel) must always be 0
-                };
-                m_stat_names = sn;
-
-                // initialize all statistics to zero
-                for (int i=0; m_stat_names[i]; ++i) {
-                    ((uint64_t*) &m_stats)[i] = 0;
-                }
-            }
-
-            void node(const shared_ptr<Osmium::OSM::Node const>& node) {
-                update_common_stats(node);
-                m_stats.nodes++;
-                if (m_tag_count == 0) {
-                    m_stats.nodes_without_tags++;
-                }
-                if (m_id > (int64_t) m_stats.max_node_id) {
-                    m_stats.max_node_id = m_id;
-                }
-                m_stats.node_tags += m_tag_count;
-                if (m_tag_count > (int64_t) m_stats.max_tags_on_node) {
-                    m_stats.max_tags_on_node = m_tag_count;
-                }
-                if (m_version > (int64_t) m_stats.max_node_version) {
-                    m_stats.max_node_version = m_version;
-                }
-                m_stats.sum_node_version += m_version;
-            }
-
-            void way(const shared_ptr<Osmium::OSM::Way const>& way) {
-                update_common_stats(way);
-                m_stats.ways++;
-                if (way->is_closed()) {
-                    m_stats.closed_ways++;
-                }
-                if (m_id > (int64_t) m_stats.max_way_id) {
-                    m_stats.max_way_id = m_id;
-                }
-                m_stats.way_tags += m_tag_count;
-                m_stats.way_nodes += way->node_count();
-                if (m_tag_count > (int64_t) m_stats.max_tags_on_way) {
-                    m_stats.max_tags_on_way = m_tag_count;
-                }
-                if (way->node_count() > (int64_t) m_stats.max_nodes_on_way) {
-                    m_stats.max_nodes_on_way = way->node_count();
-                }
-                if (m_version > (int64_t) m_stats.max_way_version) {
-                    m_stats.max_way_version = m_version;
-                }
-                m_stats.sum_way_version += m_version;
-            }
-
-            void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
-                update_common_stats(relation);
-                m_stats.relations++;
-                if (m_id > (int64_t) m_stats.max_relation_id) {
-                    m_stats.max_relation_id = m_id;
-                }
-                m_stats.relation_tags += m_tag_count;
-                osm_sequence_id_t member_count = relation->members().size();
-                m_stats.relation_members += member_count;
-                if (m_tag_count > (int64_t) m_stats.max_tags_on_relation) {
-                    m_stats.max_tags_on_relation = m_tag_count;
-                }
-                if (member_count > (int64_t) m_stats.max_members_on_relation) {
-                    m_stats.max_members_on_relation = member_count;
-                }
-                if (m_version > (int64_t) m_stats.max_relation_version) {
-                    m_stats.max_relation_version = m_version;
-                }
-                m_stats.sum_relation_version += m_version;
-            }
-
-            void final() {
-                unlink("count.db");
-                Sqlite::Database db("count.db");
-
-                sqlite3* sqlite_db = db.get_sqlite3();
-                if (SQLITE_OK != sqlite3_exec(sqlite_db, \
-                                              "CREATE TABLE stats (" \
-                                              "  key    TEXT, " \
-                                              "  value  INT64 " \
-                                              ");", 0, 0, 0)) {
-                    std::cerr << "Database error: " << sqlite3_errmsg(sqlite_db) << "\n";
-                    sqlite3_close(sqlite_db);
-                    exit(1);
-                }
-
-                Sqlite::Statement* statement_insert_into_main_stats = db.prepare("INSERT INTO stats (key, value) VALUES (?, ?);");
-                db.begin_transaction();
-
-                for (int i=0; m_stat_names[i]; ++i) {
-                    statement_insert_into_main_stats
-                    ->bind_text(m_stat_names[i])
-                    ->bind_int64( ((uint64_t*) &m_stats)[i] )
-                    ->execute();
-                }
-                statement_insert_into_main_stats
-                ->bind_text("nodes_with_tags")
-                ->bind_int64( ((uint64_t*) &m_stats)[0] - ((uint64_t*) &m_stats)[1] )
-                ->execute();
-
-                db.commit();
-
-                delete statement_insert_into_main_stats;
-            }
-
-        private:
-
-            // if you change anything in this struct, also change the corresponding array above
-            struct statistics {
-                uint64_t nodes;
-                uint64_t nodes_without_tags;
-                uint64_t node_tags;
-                uint64_t max_node_id;
-                uint64_t max_tags_on_node;
-                uint64_t ways;
-                uint64_t way_tags;
-                uint64_t way_nodes;
-                uint64_t max_way_id;
-                uint64_t max_tags_on_way;
-                uint64_t max_nodes_on_way;
-                uint64_t closed_ways;
-                uint64_t relations;
-                uint64_t relation_tags;
-                uint64_t relation_members;
-                uint64_t max_relation_id;
-                uint64_t max_tags_on_relation;
-                uint64_t max_members_on_relation;
-                uint64_t max_user_id;
-                uint64_t anon_user_objects;
-                uint64_t max_node_version;
-                uint64_t max_way_version;
-                uint64_t max_relation_version;
-                uint64_t sum_node_version;
-                uint64_t sum_way_version;
-                uint64_t sum_relation_version;
-                uint64_t max_changeset_id;
-            } m_stats;
-
-            const char **m_stat_names;
-
-            osm_object_id_t m_id;
-            osm_version_t   m_version;
-            int             m_tag_count;
-
-            void update_common_stats(const shared_ptr<Osmium::OSM::Object const>& object) {
-                m_id        = object->id();
-                m_version   = object->version();
-                m_tag_count = object->tags().size();
-
-                osm_user_id_t uid = object->uid();
-                if (uid == 0) {
-                    m_stats.anon_user_objects++;
-                }
-                if (uid > (int64_t) m_stats.max_user_id) {
-                    m_stats.max_user_id = uid;
-                }
-
-                osm_changeset_id_t changeset = object->changeset();
-                if (changeset > (int64_t) m_stats.max_changeset_id) {
-                    m_stats.max_changeset_id = changeset;
-                }
-            }
-
-        }; // class Statistics
-
-    } // namespace Handler
-
-} // namespace Osmium
-
-#endif // OSMIUM_HANDLER_STATISTICS_HPP
diff --git a/include/osmium/input.hpp b/include/osmium/input.hpp
index 1d951a6..d444918 100644
--- a/include/osmium/input.hpp
+++ b/include/osmium/input.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -23,17 +23,10 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 */
 
 #include <boost/utility.hpp>
-#include <boost/tr1/memory.hpp>
-#include <boost/make_shared.hpp>
 
-using std::tr1::shared_ptr;
-using std::tr1::static_pointer_cast;
-using std::tr1::const_pointer_cast;
-using std::tr1::dynamic_pointer_cast;
-using boost::make_shared;
-
-#include <osmium/handler.hpp>
+#include <osmium/smart_ptr.hpp>
 #include <osmium/osmfile.hpp>
+#include <osmium/handler.hpp>
 
 namespace Osmium {
 
@@ -43,19 +36,6 @@ namespace Osmium {
     namespace Input {
 
         /**
-         * Handlers can throw this exception to show that they are done.
-         * When a handler, for instance, is only interested in nodes, it
-         * can throw this in the after_nodes() method. The parser will
-         * stop reading the input file after this.
-         *
-         * Note that when you write a handler that calls other handlers
-         * that can throw this, you might have to catch this exception
-         * in your handler.
-         */
-        class StopReading {
-        };
-
-        /**
          * Virtual base class for all input classes.
          *
          * The THandler template parameter of this class (and child classes)
@@ -110,15 +90,15 @@ namespace Osmium {
 
         protected:
 
-            Base(Osmium::OSMFile& file,
-                 THandler& handler)
-                : m_last_object_type(UNKNOWN),
-                  m_file(file),
-                  m_handler(handler),
-                  m_meta(),
-                  m_node(),
-                  m_way(),
-                  m_relation() {
+            Base(const Osmium::OSMFile& file,
+                 THandler& handler) :
+                m_last_object_type(UNKNOWN),
+                m_file(file),
+                m_handler(handler),
+                m_meta(),
+                m_node(),
+                m_way(),
+                m_relation() {
 
                 m_meta.has_multiple_object_versions(m_file.has_multiple_object_versions());
                 m_file.open_for_input();
@@ -128,6 +108,9 @@ namespace Osmium {
             void call_after_and_before_on_handler(osm_object_type_t current_object_type) {
                 if (current_object_type != m_last_object_type) {
                     switch (m_last_object_type) {
+                        case UNKNOWN:
+                            m_handler.init(m_meta);
+                            break;
                         case NODE:
                             m_handler.after_nodes();
                             break;
@@ -142,9 +125,6 @@ namespace Osmium {
                     }
                     switch (current_object_type) {
                         case NODE:
-                            if (m_last_object_type == UNKNOWN) {
-                                m_handler.init(m_meta);
-                            }
                             m_handler.before_nodes();
                             break;
                         case WAY:
@@ -180,11 +160,11 @@ namespace Osmium {
                 return m_meta;
             }
 
-            int get_fd() const {
-                return m_file.get_fd();
+            int fd() const {
+                return m_file.fd();
             }
 
-            const Osmium::OSMFile& get_file() const {
+            const Osmium::OSMFile& file() const {
                 return m_file;
             }
 
@@ -259,7 +239,4 @@ namespace Osmium {
 
 } // namespace Osmium
 
-#include <osmium/input/xml.hpp>
-#include <osmium/input/pbf.hpp>
-
 #endif // OSMIUM_INPUT_HPP
diff --git a/include/osmium/input/pbf.hpp b/include/osmium/input/pbf.hpp
index 857d661..d987e6c 100644
--- a/include/osmium/input/pbf.hpp
+++ b/include/osmium/input/pbf.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,11 +22,18 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#define OSMIUM_LINK_WITH_LIBS_PBF -lz -lpthread -lprotobuf-lite -losmpbf
+
+#include <sstream>
+#include <stdexcept>
 #include <string>
+#include <utility>
 #include <zlib.h>
 
 #include <osmpbf/osmpbf.h>
 
+#include <osmium/input.hpp>
+
 namespace Osmium {
 
     namespace Input {
@@ -35,7 +42,7 @@ namespace Osmium {
         * Class for parsing PBF files.
         *
         * Generally you are not supposed to instantiate this class yourself.
-        * Instead create an OSMFile object and call its read() method.
+        * Use the Osmium::Input::read() function instead.
         *
         * @tparam THandler A handler class (subclass of Osmium::Handler::Base).
         */
@@ -61,7 +68,14 @@ namespace Osmium {
             * @param file OSMFile instance.
             * @param handler Instance of THandler.
             */
-            PBF(OSMFile& file, THandler& handler) : Base<THandler>(file, handler) {
+            PBF(const OSMFile& file, THandler& handler) :
+                Base<THandler>(file, handler),
+                m_input_buffer(),
+                m_unpack_buffer(),
+                m_pbf_blob(),
+                m_pbf_blob_header(),
+                m_pbf_primitive_block(),
+                m_date_factor() {
                 GOOGLE_PROTOBUF_VERIFY_VERSION;
             }
 
@@ -107,7 +121,7 @@ namespace Osmium {
                                 throw std::runtime_error(errmsg.str());
                             }
 
-                            const Osmium::OSMFile::FileType* expected_file_type = this->get_file().get_type();
+                            const Osmium::OSMFile::FileType* expected_file_type = this->file().type();
                             if (expected_file_type == Osmium::OSMFile::FileType::OSM() && has_historical_information_feature) {
                                 throw Osmium::OSMFile::FileTypeOSMExpected();
                             }
@@ -115,19 +129,21 @@ namespace Osmium {
                                 throw Osmium::OSMFile::FileTypeHistoryExpected();
                             }
 
+                            if (pbf_header_block.has_writingprogram()) {
+                                this->meta().generator(pbf_header_block.writingprogram());
+                            }
                             if (pbf_header_block.has_bbox()) {
                                 const OSMPBF::HeaderBBox& bbox = pbf_header_block.bbox();
-                                this->meta().bounds().extend(Osmium::OSM::Position((double)bbox.left()  / OSMPBF::lonlat_resolution, (double)bbox.bottom() / OSMPBF::lonlat_resolution));
-                                this->meta().bounds().extend(Osmium::OSM::Position((double)bbox.right() / OSMPBF::lonlat_resolution, (double)bbox.top()    / OSMPBF::lonlat_resolution));
+                                const int64_t resolution_convert = OSMPBF::lonlat_resolution / Osmium::OSM::coordinate_precision;
+                                this->meta().bounds().extend(Osmium::OSM::Position(bbox.left()  / resolution_convert, bbox.bottom() / resolution_convert));
+                                this->meta().bounds().extend(Osmium::OSM::Position(bbox.right() / resolution_convert, bbox.top()    / resolution_convert));
                             }
                         } else {
-                            if (Osmium::debug()) {
-                                std::cerr << "Ignoring unknown blob type (" << m_pbf_blob_header.type().data() << ").\n";
-                            }
+//                            std::cerr << "Ignoring unknown blob type (" << m_pbf_blob_header.type().data() << ").\n";
                         }
                     }
                     this->call_after_and_before_on_handler(UNKNOWN);
-                } catch (Osmium::Input::StopReading) {
+                } catch (Osmium::Handler::StopReading) {
                     // if a handler says to stop reading, we do
                 }
                 this->call_final_on_handler();
@@ -189,13 +205,13 @@ namespace Osmium {
 
                     Osmium::OSM::TagList& tags = node.tags();
                     for (int tag=0; tag < pbf_node.keys_size(); ++tag) {
-                        tags.add(stringtable.s( pbf_node.keys( tag ) ).data(),
-                                 stringtable.s( pbf_node.vals( tag ) ).data());
+                        tags.add(stringtable.s(pbf_node.keys(tag)).data(),
+                                 stringtable.s(pbf_node.vals(tag)).data());
                     }
 
                     node.position(Osmium::OSM::Position(
-                                      ( (double) pbf_node.lon() * m_pbf_primitive_block.granularity() + m_pbf_primitive_block.lon_offset() ) / OSMPBF::lonlat_resolution,
-                                      ( (double) pbf_node.lat() * m_pbf_primitive_block.granularity() + m_pbf_primitive_block.lat_offset() ) / OSMPBF::lonlat_resolution));
+                                      (pbf_node.lon() * m_pbf_primitive_block.granularity() + m_pbf_primitive_block.lon_offset()) / (OSMPBF::lonlat_resolution / Osmium::OSM::coordinate_precision),
+                                      (pbf_node.lat() * m_pbf_primitive_block.granularity() + m_pbf_primitive_block.lat_offset()) / (OSMPBF::lonlat_resolution / Osmium::OSM::coordinate_precision)));
                     this->call_node_on_handler();
                 }
             }
@@ -227,8 +243,8 @@ namespace Osmium {
 
                     Osmium::OSM::TagList& tags = way.tags();
                     for (int tag=0; tag < pbf_way.keys_size(); ++tag) {
-                        tags.add(stringtable.s( pbf_way.keys( tag ) ).data(),
-                                 stringtable.s( pbf_way.vals( tag ) ).data());
+                        tags.add(stringtable.s(pbf_way.keys(tag)).data(),
+                                 stringtable.s(pbf_way.vals(tag)).data());
                     }
 
                     uint64_t ref = 0;
@@ -268,8 +284,8 @@ namespace Osmium {
 
                     Osmium::OSM::TagList& tags = relation.tags();
                     for (int tag=0; tag < pbf_relation.keys_size(); ++tag) {
-                        tags.add(stringtable.s( pbf_relation.keys(tag) ).data(),
-                                 stringtable.s( pbf_relation.vals(tag) ).data());
+                        tags.add(stringtable.s(pbf_relation.keys(tag)).data(),
+                                 stringtable.s(pbf_relation.vals(tag)).data());
                     }
 
                     uint64_t ref = 0;
@@ -287,7 +303,7 @@ namespace Osmium {
                                 break;
                         }
                         ref += pbf_relation.memids(i);
-                        relation.add_member(type, ref, stringtable.s( pbf_relation.roles_sid( i ) ).data());
+                        relation.add_member(type, ref, stringtable.s(pbf_relation.roles_sid(i)).data());
                     }
 
                     this->call_relation_on_handler();
@@ -338,8 +354,8 @@ namespace Osmium {
                     last_dense_latitude  += dense.lat(entity);
                     last_dense_longitude += dense.lon(entity);
                     node.position(Osmium::OSM::Position(
-                                      ( (double) last_dense_longitude * m_pbf_primitive_block.granularity() + m_pbf_primitive_block.lon_offset() ) / OSMPBF::lonlat_resolution,
-                                      ( (double) last_dense_latitude  * m_pbf_primitive_block.granularity() + m_pbf_primitive_block.lat_offset() ) / OSMPBF::lonlat_resolution));
+                                      (last_dense_longitude * m_pbf_primitive_block.granularity() + m_pbf_primitive_block.lon_offset()) / (OSMPBF::lonlat_resolution / Osmium::OSM::coordinate_precision),
+                                      (last_dense_latitude  * m_pbf_primitive_block.granularity() + m_pbf_primitive_block.lat_offset()) / (OSMPBF::lonlat_resolution / Osmium::OSM::coordinate_precision)));
 
                     while (last_dense_tag < dense.keys_vals_size()) {
                         int tag_key_pos = dense.keys_vals(last_dense_tag);
@@ -376,7 +392,7 @@ namespace Osmium {
                 unsigned char size_in_network_byte_order[4];
                 int offset = 0;
                 while (offset < static_cast<int>(sizeof(size_in_network_byte_order))) {
-                    int nread = read(this->get_fd(), size_in_network_byte_order + offset, sizeof(size_in_network_byte_order) - offset);
+                    int nread = ::read(this->fd(), size_in_network_byte_order + offset, sizeof(size_in_network_byte_order) - offset);
                     if (nread < 0) {
                         throw std::runtime_error("read error");
                     } else if (nread == 0) {
@@ -394,7 +410,7 @@ namespace Osmium {
 
                 offset = 0;
                 while (offset < size) {
-                    int nread = read(this->get_fd(), m_input_buffer + offset, size - offset);
+                    int nread = ::read(this->fd(), m_input_buffer + offset, size - offset);
                     if (nread < 1) {
                         throw std::runtime_error("failed to read BlobHeader");
                     }
@@ -418,7 +434,7 @@ namespace Osmium {
                 }
                 int offset = 0;
                 while (offset < size) {
-                    int nread = read(this->get_fd(), m_input_buffer + offset, size - offset);
+                    int nread = ::read(this->fd(), m_input_buffer + offset, size - offset);
                     if (nread < 1) {
                         throw std::runtime_error("failed to read blob");
                     }
diff --git a/include/osmium/input/xml.hpp b/include/osmium/input/xml.hpp
index 2a333a8..985acd7 100644
--- a/include/osmium/input/xml.hpp
+++ b/include/osmium/input/xml.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,16 +22,19 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#define OSMIUM_LINK_WITH_LIBS_EXPAT -lexpat
+
 #include <cstdio>
 #include <cstdlib>
-#include <iostream>
-#include <string>
-#include <unistd.h>
 #include <cstring>
+#include <iostream>
 #include <sstream>
-
+#include <stdexcept>
+#include <string>
 #include <expat.h>
 
+#include <osmium/input.hpp>
+
 namespace Osmium {
 
     namespace Input {
@@ -40,7 +43,7 @@ namespace Osmium {
         * Class for parsing OSM XML files.
         *
         * Generally you are not supposed to instantiate this class yourself.
-        * Instead create an OSMFile object and call its read() method.
+        * Use the Osmium::Input::read() function instead.
         *
         * @tparam THandler A handler class (subclass of Osmium::Handler::Base).
         */
@@ -55,12 +58,15 @@ namespace Osmium {
             * @param file OSMFile instance.
             * @param handler Instance of THandler.
             */
-            XML(Osmium::OSMFile& file, THandler& handler) : Base<THandler>(file, handler), m_current_object(NULL), m_in_delete_section(false) {
+            XML(const Osmium::OSMFile& file, THandler& handler) :
+                Base<THandler>(file, handler),
+                m_current_object(NULL),
+                m_context(context_root),
+                m_last_context(context_root),
+                m_in_delete_section(false) {
             }
 
             void parse() {
-                int done;
-
                 XML_Parser parser = XML_ParserCreate(0);
                 if (!parser) {
                     throw std::runtime_error("Error creating parser");
@@ -71,22 +77,23 @@ namespace Osmium {
                 XML_SetElementHandler(parser, Osmium::Input::XML<THandler>::start_element_wrapper, Osmium::Input::XML<THandler>::end_element_wrapper);
 
                 try {
+                    int done;
                     do {
                         void* buffer = XML_GetBuffer(parser, c_buffer_size);
                         if (buffer == 0) {
                             throw std::runtime_error("out of memory");
                         }
 
-                        int result = read(this->get_fd(), buffer, c_buffer_size);
+                        int result = ::read(this->fd(), buffer, c_buffer_size);
                         if (result < 0) {
-                            exit(1);
+                            throw std::runtime_error("read error");
                         }
                         done = (result == 0);
                         if (XML_ParseBuffer(parser, result, done) == XML_STATUS_ERROR) {
                             XML_Error errorCode = XML_GetErrorCode(parser);
                             long errorLine = XML_GetCurrentLineNumber(parser);
                             long errorCol = XML_GetCurrentColumnNumber(parser);
-                            const XML_LChar *errorString = XML_ErrorString(errorCode);
+                            const XML_LChar* errorString = XML_ErrorString(errorCode);
 
                             std::stringstream errorDesc;
                             errorDesc << "XML parsing error at line " << errorLine << ":" << errorCol;
@@ -97,7 +104,7 @@ namespace Osmium {
                     XML_ParserFree(parser);
 
                     this->call_after_and_before_on_handler(UNKNOWN);
-                } catch (Osmium::Input::StopReading) {
+                } catch (Osmium::Handler::StopReading) {
                     // if a handler says to stop reading, we do
                 }
                 this->call_final_on_handler();
@@ -109,6 +116,18 @@ namespace Osmium {
 
             Osmium::OSM::Object* m_current_object;
 
+            enum context_t {
+                context_root,
+                context_top,
+                context_node,
+                context_way,
+                context_relation,
+                context_in_object
+            };
+
+            context_t m_context;
+            context_t m_last_context;
+
             /**
              * This is used only for change files which contain create, modify,
              * and delete sections.
@@ -116,11 +135,11 @@ namespace Osmium {
             bool m_in_delete_section;
 
             static void XMLCALL start_element_wrapper(void* data, const XML_Char* element, const XML_Char** attrs) {
-                ((Osmium::Input::XML<THandler> *)data)->start_element(element, attrs);
+                static_cast<Osmium::Input::XML<THandler> *>(data)->start_element(element, attrs);
             }
 
             static void XMLCALL end_element_wrapper(void* data, const XML_Char* element) {
-                ((Osmium::Input::XML<THandler> *)data)->end_element(element);
+                static_cast<Osmium::Input::XML<THandler> *>(data)->end_element(element);
             }
 
             void init_object(Osmium::OSM::Object& obj, const XML_Char** attrs) {
@@ -131,11 +150,11 @@ namespace Osmium {
                 for (int count = 0; attrs[count]; count += 2) {
                     if (!strcmp(attrs[count], "lon")) {
                         if (this->m_node) {
-                            this->m_node->set_x(atof(attrs[count+1]));
+                            this->m_node->lon(atof(attrs[count+1]));
                         }
                     } else if (!strcmp(attrs[count], "lat")) {
                         if (this->m_node) {
-                            this->m_node->set_y(atof(attrs[count+1]));
+                            this->m_node->lat(atof(attrs[count+1]));
                         }
                     } else {
                         m_current_object->set_attribute(attrs[count], attrs[count+1]);
@@ -143,19 +162,10 @@ namespace Osmium {
                 }
             }
 
-            void start_element(const XML_Char* element, const XML_Char** attrs) {
-                // order in the following "if" statements is based on frequency of tags in planet file
-                if (!strcmp(element, "nd")) {
-                    for (int count = 0; attrs[count]; count += 2) {
-                        if (!strcmp(attrs[count], "ref")) {
-                            this->m_way->add_node(atoll(attrs[count+1]));
-                        }
-                    }
-                } else if (!strcmp(element, "node")) {
-                    this->call_after_and_before_on_handler(NODE);
-                    init_object(this->prepare_node(), attrs);
-                } else if (!strcmp(element, "tag")) {
-                    const char *key = "", *value = "";
+            void check_tag(const XML_Char* element, const XML_Char** attrs) {
+                if (!strcmp(element, "tag")) {
+                    const char* key = "";
+                    const char* value = "";
                     for (int count = 0; attrs[count]; count += 2) {
                         if (attrs[count][0] == 'k' && attrs[count][1] == 0) {
                             key = attrs[count+1];
@@ -164,65 +174,139 @@ namespace Osmium {
                             value = attrs[count+1];
                         }
                     }
-                    // XXX assert key, value exist
-                    if (m_current_object) {
-                        m_current_object->tags().add(key, value);
-                    }
-                } else if (!strcmp(element, "way")) {
-                    this->call_after_and_before_on_handler(WAY);
-                    init_object(this->prepare_way(), attrs);
-                } else if (!strcmp(element, "member")) {
-                    char        type = 'x';
-                    uint64_t    ref  = 0;
-                    const char *role = "";
-                    for (int count = 0; attrs[count]; count += 2) {
-                        if (!strcmp(attrs[count], "type")) {
-                            type = (char)attrs[count+1][0];
-                        } else if (!strcmp(attrs[count], "ref")) {
-                            ref = atoll(attrs[count+1]);
-                        } else if (!strcmp(attrs[count], "role")) {
-                            role = (char *)attrs[count+1];
+                    m_current_object->tags().add(key, value);
+                }
+            }
+
+            void start_element(const XML_Char* element, const XML_Char** attrs) {
+                switch (m_context) {
+                    case context_root:
+                        if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
+                            for (int count = 0; attrs[count]; count += 2) {
+                                if (!strcmp(attrs[count], "version")) {
+                                    if (strcmp(attrs[count+1], "0.6")) {
+                                        throw std::runtime_error("can only read version 0.6 files");
+                                    }
+                                } else if (!strcmp(attrs[count], "generator")) {
+                                    this->meta().generator(attrs[count+1]);
+                                }
+                            }
                         }
-                    }
-                    // XXX assert type, ref, role are set
-                    if (m_current_object && this->m_relation) {
-                        this->m_relation->add_member(type, ref, role);
-                    }
-                } else if (!strcmp(element, "relation")) {
-                    this->call_after_and_before_on_handler(RELATION);
-                    init_object(this->prepare_relation(), attrs);
-                } else if (!strcmp(element, "bounds")) {
-                    Osmium::OSM::Position min;
-                    Osmium::OSM::Position max;
-                    for (int count = 0; attrs[count]; count += 2) {
-                        if (!strcmp(attrs[count], "minlon")) {
-                            min.lon(atof(attrs[count+1]));
-                        } else if (!strcmp(attrs[count], "minlat")) {
-                            min.lat(atof(attrs[count+1]));
-                        } else if (!strcmp(attrs[count], "maxlon")) {
-                            max.lon(atof(attrs[count+1]));
-                        } else if (!strcmp(attrs[count], "maxlat")) {
-                            max.lat(atof(attrs[count+1]));
+                        m_context = context_top;
+                        break;
+                    case context_top:
+                        if (!strcmp(element, "node")) {
+                            this->call_after_and_before_on_handler(NODE);
+                            init_object(this->prepare_node(), attrs);
+                            m_context = context_node;
+                        } else if (!strcmp(element, "way")) {
+                            this->call_after_and_before_on_handler(WAY);
+                            init_object(this->prepare_way(), attrs);
+                            m_context = context_way;
+                        } else if (!strcmp(element, "relation")) {
+                            this->call_after_and_before_on_handler(RELATION);
+                            init_object(this->prepare_relation(), attrs);
+                            m_context = context_relation;
+                        } else if (!strcmp(element, "bounds")) {
+                            Osmium::OSM::Position min;
+                            Osmium::OSM::Position max;
+                            for (int count = 0; attrs[count]; count += 2) {
+                                if (!strcmp(attrs[count], "minlon")) {
+                                    min.lon(atof(attrs[count+1]));
+                                } else if (!strcmp(attrs[count], "minlat")) {
+                                    min.lat(atof(attrs[count+1]));
+                                } else if (!strcmp(attrs[count], "maxlon")) {
+                                    max.lon(atof(attrs[count+1]));
+                                } else if (!strcmp(attrs[count], "maxlat")) {
+                                    max.lat(atof(attrs[count+1]));
+                                }
+                            }
+                            this->meta().bounds().extend(min).extend(max);
+                        } else if (!strcmp(element, "delete")) {
+                            m_in_delete_section = true;
                         }
-                    }
-                    this->meta().bounds().extend(min).extend(max);
-                } else if (!strcmp(element, "delete")) {
-                    m_in_delete_section = true;
+                        break;
+                    case context_node:
+                        m_last_context = context_node;
+                        m_context = context_in_object;
+                        check_tag(element, attrs);
+                        break;
+                    case context_way:
+                        m_last_context = context_way;
+                        m_context = context_in_object;
+                        if (!strcmp(element, "nd")) {
+                            for (int count = 0; attrs[count]; count += 2) {
+                                if (!strcmp(attrs[count], "ref")) {
+                                    this->m_way->add_node(Osmium::string_to_osm_object_id_t(attrs[count+1]));
+                                }
+                            }
+                        } else {
+                            check_tag(element, attrs);
+                        }
+                        break;
+                    case context_relation:
+                        m_last_context = context_relation;
+                        m_context = context_in_object;
+                        if (!strcmp(element, "member")) {
+                            char        type = 'x';
+                            uint64_t    ref  = 0;
+                            const char* role = "";
+                            for (int count = 0; attrs[count]; count += 2) {
+                                if (!strcmp(attrs[count], "type")) {
+                                    type = static_cast<char>(attrs[count+1][0]);
+                                } else if (!strcmp(attrs[count], "ref")) {
+                                    ref = Osmium::string_to_osm_object_id_t(attrs[count+1]);
+                                } else if (!strcmp(attrs[count], "role")) {
+                                    role = static_cast<const char*>(attrs[count+1]);
+                                }
+                            }
+                            // XXX assert type, ref, role are set
+                            if (m_current_object && this->m_relation) {
+                                this->m_relation->add_member(type, ref, role);
+                            }
+                        } else {
+                            check_tag(element, attrs);
+                        }
+                        break;
+                    case context_in_object:
+                        // fallthrough
+                    default:
+                        assert(false); // should never be here
                 }
             }
 
             void end_element(const XML_Char* element) {
-                if (!strcmp(element, "node")) {
-                    this->call_node_on_handler();
-                    m_current_object = NULL;
-                } else if (!strcmp(element, "way")) {
-                    this->call_way_on_handler();
-                    m_current_object = NULL;
-                } else if (!strcmp(element, "relation")) {
-                    this->call_relation_on_handler();
-                    m_current_object = NULL;
-                } else if (!strcmp(element, "delete")) {
-                    m_in_delete_section = false;
+                switch (m_context) {
+                    case context_root:
+                        assert(false); // should never be here
+                        break;
+                    case context_top:
+                        if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
+                            m_context = context_root;
+                        } else if (!strcmp(element, "delete")) {
+                            m_in_delete_section = false;
+                        }
+                        break;
+                    case context_node:
+                        this->call_node_on_handler();
+                        m_current_object = NULL;
+                        m_context = context_top;
+                        break;
+                    case context_way:
+                        this->call_way_on_handler();
+                        m_current_object = NULL;
+                        m_context = context_top;
+                        break;
+                    case context_relation:
+                        this->call_relation_on_handler();
+                        m_current_object = NULL;
+                        m_context = context_top;
+                        break;
+                    case context_in_object:
+                        m_context = m_last_context;
+                        break;
+                    default:
+                        assert(false); // should never be here
                 }
             }
 
diff --git a/examples/osmium_stats.cpp b/include/osmium/javascript.hpp
similarity index 58%
rename from examples/osmium_stats.cpp
rename to include/osmium/javascript.hpp
index 9b0938d..5ce44dc 100644
--- a/examples/osmium_stats.cpp
+++ b/include/osmium/javascript.hpp
@@ -1,12 +1,9 @@
-/*
-
-  This is a small tool to try the Statistics handler
-
-*/
+#ifndef OSMIUM_JAVASCRIPT_HPP
+#define OSMIUM_JAVASCRIPT_HPP
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -25,23 +22,8 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <cstdlib>
-
-#include <osmium.hpp>
-#include <osmium/handler/statistics.hpp>
-
-/* ================================================== */
-
-int main(int argc, char *argv[]) {
-    Osmium::init();
-
-    if (argc != 2) {
-        std::cerr << "Usage: " << argv[0] << " OSMFILE" << std::endl;
-        exit(1);
-    }
+#define OSMIUM_LINK_WITH_LIBS_V8 -lv8 -licuuc
 
-    Osmium::OSMFile infile(argv[1]);
-    Osmium::Handler::Statistics handler;
-    infile.read(handler);
-}
+#include <osmium/javascript/handler.hpp>
 
+#endif // OSMIUM_JAVASCRIPT_HPP
diff --git a/include/osmium/HandlerJavascript.hpp b/include/osmium/javascript/handler.hpp
similarity index 65%
rename from include/osmium/HandlerJavascript.hpp
rename to include/osmium/javascript/handler.hpp
index b7ea87c..03aaffb 100644
--- a/include/osmium/HandlerJavascript.hpp
+++ b/include/osmium/javascript/handler.hpp
@@ -1,9 +1,9 @@
-#ifndef OSMIUM_HANDLER_JAVASCRIPT_HPP
-#define OSMIUM_HANDLER_JAVASCRIPT_HPP
+#ifndef OSMIUM_JAVASCRIPT_HANDLER_HPP
+#define OSMIUM_JAVASCRIPT_HANDLER_HPP
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -24,15 +24,23 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 #include <fstream>
 #include <iostream>
+#include <string>
 #include <v8.h>
 
+#include <osmium/handler.hpp>
+#include <osmium/javascript/wrapper/position.hpp>
+#include <osmium/javascript/wrapper/geometry.hpp>
+#include <osmium/javascript/wrapper/osm.hpp>
+#include <osmium/javascript/wrapper/export_csv.hpp>
+#include <osmium/javascript/wrapper/export_shapefile.hpp>
+
 extern v8::Persistent<v8::Context> global_context;
 
 namespace Osmium {
 
-    namespace Handler {
+    namespace Javascript {
 
-        class Javascript : public Base {
+        class Handler : public Osmium::Handler::Base {
 
             /***
             * Load Javascript file into string
@@ -88,9 +96,15 @@ namespace Osmium {
 
             struct js_cb {
                 v8::Handle<v8::Function> init;
+                v8::Handle<v8::Function> before_nodes;
                 v8::Handle<v8::Function> node;
+                v8::Handle<v8::Function> after_nodes;
+                v8::Handle<v8::Function> before_ways;
                 v8::Handle<v8::Function> way;
+                v8::Handle<v8::Function> after_ways;
+                v8::Handle<v8::Function> before_relations;
                 v8::Handle<v8::Function> relation;
+                v8::Handle<v8::Function> after_relations;
                 v8::Handle<v8::Function> area;
                 v8::Handle<v8::Function> end;
             } cb;
@@ -135,66 +149,31 @@ namespace Osmium {
                 return v8::Undefined();
             }
 
-            static v8::Handle<v8::Value> OutputCSVOpen(const v8::Arguments& args) {
-                if (args.Length() != 1) {
-                    return v8::Undefined();
-                } else {
-                    v8::String::Utf8Value str(args[0]);
-                    Osmium::Export::CSV* oc = new Osmium::Export::CSV(*str);
-                    return oc->js_instance();
-                }
-            }
-
-#ifdef OSMIUM_WITH_SHPLIB
-            static v8::Handle<v8::Value> OutputShapefileOpen(const v8::Arguments& args) {
-                if (args.Length() != 2) {
-                    return v8::Undefined();
-                } else {
-                    v8::String::Utf8Value str(args[0]);
-                    v8::String::AsciiValue type(args[1]);
-                    std::string filename(*str);
-                    Osmium::Export::Shapefile* oc;
-                    if (!strcmp(*type, "point")) {
-                        oc = new Osmium::Export::PointShapefile(filename);
-                    } else if (!strcmp(*type, "line")) {
-                        oc = new Osmium::Export::LineStringShapefile(filename);
-                    } else if (!strcmp(*type, "polygon")) {
-                        oc = new Osmium::Export::PolygonShapefile(filename);
-                    } else {
-                        throw std::runtime_error("unkown shapefile type");
-                    }
-
-                    return oc->js_instance();
-                }
-            }
-#endif // OSMIUM_WITH_SHPLIB
-
-            Javascript(std::vector<std::string> include_files, const char* filename) : Base() {
+            Handler(std::vector<std::string> include_files, const char* filename) :
+                Osmium::Handler::Base() {
 //                v8::HandleScope handle_scope;
                 v8::Handle<v8::String> init_source = v8::String::New("Osmium = { Callbacks: {}, Output: { } };");
                 v8::Handle<v8::Script> init_script = v8::Script::Compile(init_source);
                 osmium_object = v8::Persistent<v8::Object>::New(init_script->Run()->ToObject());
                 v8::Handle<v8::Object> output_object = osmium_object->Get(v8::String::NewSymbol("Output"))->ToObject();
 
-                osmium_object->Set(v8::String::NewSymbol("debug"), v8::Boolean::New(Osmium::debug()));
+                osmium_object->Set(v8::String::NewSymbol("debug"), v8::Boolean::New(has_debug_level(1)));
 
                 v8::Handle<v8::ObjectTemplate> output_csv_template = v8::ObjectTemplate::New();
-                output_csv_template->Set(v8::String::NewSymbol("open"), v8::FunctionTemplate::New(OutputCSVOpen));
+                output_csv_template->Set(v8::String::NewSymbol("open"), v8::FunctionTemplate::New(Osmium::Javascript::Wrapper::ExportCSV::open));
                 output_object->Set(v8::String::NewSymbol("CSV"), output_csv_template->NewInstance());
 
-#ifdef OSMIUM_WITH_SHPLIB
                 v8::Handle<v8::ObjectTemplate> output_shapefile_template = v8::ObjectTemplate::New();
-                output_shapefile_template->Set(v8::String::NewSymbol("open"), v8::FunctionTemplate::New(OutputShapefileOpen));
+                output_shapefile_template->Set(v8::String::NewSymbol("open"), v8::FunctionTemplate::New(Osmium::Javascript::Wrapper::ExportShapefile::open));
                 output_object->Set(v8::String::NewSymbol("Shapefile"), output_shapefile_template->NewInstance());
-#endif // OSMIUM_WITH_SHPLIB
 
                 v8::Handle<v8::Object> callbacks_object = osmium_object->Get(v8::String::NewSymbol("Callbacks"))->ToObject();
 
                 v8::TryCatch tryCatch;
 
-                for (std::vector<std::string>::const_iterator vi(include_files.begin()); vi != include_files.end(); vi++) {
-                    if (Osmium::debug()) {
-                        std::cerr << "include javascript file: " << *vi << std::endl;
+                for (std::vector<std::string>::const_iterator vi(include_files.begin()); vi != include_files.end(); ++vi) {
+                    if (debug && has_debug_level(1)) {
+                        std::cout << "include javascript file: " << *vi << std::endl;
                     }
                     std::string javascript_source = load_file((*vi).c_str());
                     v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(javascript_source.c_str()), v8::String::New((*vi).c_str()));
@@ -238,18 +217,46 @@ namespace Osmium {
                 if (cc->IsFunction()) {
                     cb.init = v8::Handle<v8::Function>::Cast(cc);
                 }
+
+                cc = callbacks_object->Get(v8::String::NewSymbol("before_nodes"));
+                if (cc->IsFunction()) {
+                    cb.before_nodes = v8::Handle<v8::Function>::Cast(cc);
+                }
                 cc = callbacks_object->Get(v8::String::NewSymbol("node"));
                 if (cc->IsFunction()) {
                     cb.node = v8::Handle<v8::Function>::Cast(cc);
                 }
+                cc = callbacks_object->Get(v8::String::NewSymbol("after_nodes"));
+                if (cc->IsFunction()) {
+                    cb.after_nodes = v8::Handle<v8::Function>::Cast(cc);
+                }
+
+                cc = callbacks_object->Get(v8::String::NewSymbol("before_ways"));
+                if (cc->IsFunction()) {
+                    cb.before_ways = v8::Handle<v8::Function>::Cast(cc);
+                }
                 cc = callbacks_object->Get(v8::String::NewSymbol("way"));
                 if (cc->IsFunction()) {
                     cb.way = v8::Handle<v8::Function>::Cast(cc);
                 }
+                cc = callbacks_object->Get(v8::String::NewSymbol("after_ways"));
+                if (cc->IsFunction()) {
+                    cb.after_ways = v8::Handle<v8::Function>::Cast(cc);
+                }
+
+                cc = callbacks_object->Get(v8::String::NewSymbol("before_relations"));
+                if (cc->IsFunction()) {
+                    cb.before_relations = v8::Handle<v8::Function>::Cast(cc);
+                }
                 cc = callbacks_object->Get(v8::String::NewSymbol("relation"));
                 if (cc->IsFunction()) {
                     cb.relation = v8::Handle<v8::Function>::Cast(cc);
                 }
+                cc = callbacks_object->Get(v8::String::NewSymbol("after_relations"));
+                if (cc->IsFunction()) {
+                    cb.after_relations = v8::Handle<v8::Function>::Cast(cc);
+                }
+
                 cc = callbacks_object->Get(v8::String::NewSymbol("area"));
                 if (cc->IsFunction()) {
                     cb.area = v8::Handle<v8::Function>::Cast(cc);
@@ -260,7 +267,7 @@ namespace Osmium {
                 }
             }
 
-            ~Javascript() {
+            ~Handler() {
                 callbacks_object.Dispose();
             }
 
@@ -270,36 +277,86 @@ namespace Osmium {
                 }
             }
 
+            void before_nodes() {
+                if (!cb.before_nodes.IsEmpty()) {
+                    v8::HandleScope handle_scope;
+                    (void) cb.before_nodes->Call(cb.before_nodes, 0, 0);
+                }
+            }
+
             void node(const shared_ptr<Osmium::OSM::Node const>& node) {
                 if (!cb.node.IsEmpty()) {
-                    (void) cb.node->Call(node->get_instance(), 0, 0);
+                    v8::HandleScope handle_scope;
+                    v8::Handle<v8::Object> js_object_instance = v8::Local<v8::Object>::New(Osmium::Javascript::Wrapper::OSMNode::get<Osmium::Javascript::Wrapper::OSMNode>().create_instance((void*)(node.get())));
+                    (void) cb.node->Call(js_object_instance, 0, 0);
                 }
 #ifdef OSMIUM_V8_FORCE_GC
                 while (!v8::V8::IdleNotification()) { };
 #endif // OSMIUM_V8_FORCE_GC
             }
 
+            void after_nodes() {
+                if (!cb.after_nodes.IsEmpty()) {
+                    v8::HandleScope handle_scope;
+                    (void) cb.after_nodes->Call(cb.after_nodes, 0, 0);
+                }
+            }
+
+            void before_ways() {
+                if (!cb.before_ways.IsEmpty()) {
+                    v8::HandleScope handle_scope;
+                    (void) cb.before_ways->Call(cb.before_ways, 0, 0);
+                }
+            }
+
             void way(const shared_ptr<Osmium::OSM::Way const>& way) {
                 if (!cb.way.IsEmpty()) {
-                    (void) cb.way->Call(way->get_instance(), 0, 0);
+                    v8::HandleScope handle_scope;
+                    v8::Handle<v8::Object> js_object_instance = v8::Local<v8::Object>::New(Osmium::Javascript::Wrapper::OSMWay::get<Osmium::Javascript::Wrapper::OSMWay>().create_instance((void*)(way.get())));
+                    (void) cb.way->Call(js_object_instance, 0, 0);
                 }
 #ifdef OSMIUM_V8_FORCE_GC
                 while (!v8::V8::IdleNotification()) { };
 #endif // OSMIUM_V8_FORCE_GC
             }
 
+            void after_ways() {
+                if (!cb.after_ways.IsEmpty()) {
+                    v8::HandleScope handle_scope;
+                    (void) cb.after_ways->Call(cb.after_ways, 0, 0);
+                }
+            }
+
+            void before_relations() {
+                if (!cb.before_relations.IsEmpty()) {
+                    v8::HandleScope handle_scope;
+                    (void) cb.before_relations->Call(cb.before_relations, 0, 0);
+                }
+            }
+
             void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
                 if (!cb.relation.IsEmpty()) {
-                    (void) cb.relation->Call(relation->get_instance(), 0, 0);
+                    v8::HandleScope handle_scope;
+                    v8::Handle<v8::Object> js_object_instance = v8::Local<v8::Object>::New(Osmium::Javascript::Wrapper::OSMRelation::get<Osmium::Javascript::Wrapper::OSMRelation>().create_instance((void*)(relation.get())));
+                    (void) cb.relation->Call(js_object_instance, 0, 0);
                 }
 #ifdef OSMIUM_V8_FORCE_GC
                 while (!v8::V8::IdleNotification()) { };
 #endif // OSMIUM_V8_FORCE_GC
             }
 
-            void area(Osmium::OSM::Area* area) {
+            void after_relations() {
+                if (!cb.after_relations.IsEmpty()) {
+                    v8::HandleScope handle_scope;
+                    (void) cb.after_relations->Call(cb.after_relations, 0, 0);
+                }
+            }
+
+            void area(const shared_ptr<Osmium::OSM::Area const>& area) {
                 if (!cb.area.IsEmpty()) {
-                    (void) cb.area->Call(area->get_instance(), 0, 0);
+                    v8::HandleScope handle_scope;
+                    v8::Handle<v8::Object> js_object_instance = v8::Local<v8::Object>::New(Osmium::Javascript::Wrapper::OSMArea::get<Osmium::Javascript::Wrapper::OSMArea>().create_instance((void*)(area.get())));
+                    (void) cb.area->Call(js_object_instance, 0, 0);
                 }
 #ifdef OSMIUM_V8_FORCE_GC
                 while (!v8::V8::IdleNotification()) { };
@@ -312,10 +369,10 @@ namespace Osmium {
                 }
             }
 
-        }; // class Javascript
+        }; // class Handler
 
-    } // namespace Handler
+    } // namespace Javascript
 
 } // namespace Osmium
 
-#endif // OSMIUM_HANDLER_JAVASCRIPT_HPP
+#endif // OSMIUM_JAVASCRIPT_HANDLER_HPP
diff --git a/include/osmium/javascript/template.hpp b/include/osmium/javascript/template.hpp
index 60db50d..bb94817 100644
--- a/include/osmium/javascript/template.hpp
+++ b/include/osmium/javascript/template.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -45,7 +45,7 @@ namespace Osmium {
 
         public:
 
-            template<class T>
+            template <class T>
             static T& get() {
                 static T t;
                 return t;
@@ -77,7 +77,7 @@ namespace Osmium {
             /**
              * Function that always returns undefined.
              */
-            v8::Handle<v8::Value> js_undefined(const v8::Arguments& /*args*/) {
+            static v8::Handle<v8::Value> undefined(const v8::Arguments& /*args*/) {
                 return v8::Undefined();
             }
 
@@ -95,45 +95,37 @@ namespace Osmium {
                 from Javascript.
 
             */
-            template<class TObject, v8::Handle<v8::Value> (TObject::*func)() const>
-            static v8::Handle<v8::Value> accessor_getter(v8::Local<v8::String>, const v8::AccessorInfo &info) {
-                return (( reinterpret_cast<TObject*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()) )->*(func))();
+            template <class TWrapped, v8::Handle<v8::Value> (func)(TWrapped*)>
+            static v8::Handle<v8::Value> accessor_getter(v8::Local<v8::String>, const v8::AccessorInfo& info) {
+                return func(reinterpret_cast<TWrapped*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()));
             }
 
-            template<class TObject, v8::Handle<v8::Value> (TObject::*func)(v8::Local<v8::String>) const>
-            static v8::Handle<v8::Value> named_property_getter(v8::Local<v8::String> property, const v8::AccessorInfo &info) {
-                return (( reinterpret_cast<TObject*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()) )->*(func))(property);
+            template <class TWrapped, v8::Handle<v8::Value> func(v8::Local<v8::String>, TWrapped*)>
+            static v8::Handle<v8::Value> named_property_getter(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
+                return func(property, reinterpret_cast<TWrapped*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()));
             }
 
-            template<class TObject, v8::Handle<v8::Value> (TObject::*func)(uint32_t) const>
-            static v8::Handle<v8::Value> indexed_property_getter(uint32_t index, const v8::AccessorInfo &info) {
-                return (( reinterpret_cast<TObject*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()) )->*(func))(index);
+            template <class TWrapped, v8::Handle<v8::Value> func(uint32_t, TWrapped*)>
+            static v8::Handle<v8::Value> indexed_property_getter(uint32_t index, const v8::AccessorInfo& info) {
+                return func(index, reinterpret_cast<TWrapped*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()));
             }
 
-            template<class TObject, v8::Handle<v8::Value> (TObject::*func)(uint32_t)>
-            static v8::Handle<v8::Value> indexed_property_getter(uint32_t index, const v8::AccessorInfo &info) {
-                return (( reinterpret_cast<TObject*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()) )->*(func))(index);
+            template <class TWrapped, v8::Handle<v8::Array> func(TWrapped*)>
+            static v8::Handle<v8::Array> property_enumerator(const v8::AccessorInfo& info) {
+                return func(reinterpret_cast<TWrapped*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()));
             }
 
-            template<class TObject, v8::Handle<v8::Array> (TObject::*func)() const>
-            static v8::Handle<v8::Array> property_enumerator(const v8::AccessorInfo &info) {
-                return (( reinterpret_cast<TObject*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()) )->*(func))();
-            }
-
-            template<class TObject, v8::Handle<v8::Value> (TObject::*func)(const v8::Arguments&)>
+            template <class TWrapped, v8::Handle<v8::Value> (func)(const v8::Arguments&, TWrapped*)>
             static v8::Handle<v8::Value> function_template(const v8::Arguments& args) {
-                return (( reinterpret_cast<TObject*>(v8::Local<v8::External>::Cast(args.Holder()->GetInternalField(0))->Value()) )->*(func))(args);
+                return func(args, reinterpret_cast<TWrapped*>(v8::Local<v8::External>::Cast(args.Holder()->GetInternalField(0))->Value()));
             }
 
         protected:
 
             v8::Persistent<v8::ObjectTemplate> js_template;
 
-            /**
-             * Constructor.
-             */
-            Template(int field_count=1) {
-                js_template = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
+            Template(int field_count=1) :
+                js_template(v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New())) {
                 js_template->SetInternalFieldCount(field_count);
             }
 
@@ -143,7 +135,7 @@ namespace Osmium {
 
         private:
 
-            // copy constructor and assignment operator are private and can't be used
+            // objects of this class can't be copied
             Template(const Template&);
             Template& operator=(const Template&);
 
diff --git a/include/osmium/utils/unicode.hpp b/include/osmium/javascript/unicode.hpp
similarity index 79%
rename from include/osmium/utils/unicode.hpp
rename to include/osmium/javascript/unicode.hpp
index a0ce3b2..1d387d7 100644
--- a/include/osmium/utils/unicode.hpp
+++ b/include/osmium/javascript/unicode.hpp
@@ -1,9 +1,9 @@
-#ifndef OSMIUM_UTILS_UNICODE_HPP
-#define OSMIUM_UTILS_UNICODE_HPP
+#ifndef OSMIUM_JAVASCRIPT_UNICODE_HPP
+#define OSMIUM_JAVASCRIPT_UNICODE_HPP
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -23,7 +23,7 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 */
 
 #include <cstdlib>
-#include <fstream>
+#include <ostream>
 
 #include <v8.h>
 #include <unicode/ustring.h>
@@ -40,32 +40,35 @@ namespace Osmium {
     public:
 
         UErrorCode error_code;
-        Unicode_Conversion_Error(UErrorCode ec) : error_code(ec) { }
+        Unicode_Conversion_Error(UErrorCode ec) :
+            error_code(ec) { }
 
         /// Is this a buffer overflow?
         bool buffer_overflow() const {
             return error_code == U_BUFFER_OVERFLOW_ERROR;
         }
 
-    };
+    }; // class Unicode_Conversion_Error
 
     /// Exception thrown when a UTF-8 to UTF-16 conversion failed.
     class UTF8_to_UTF16_Conversion_Error : public Unicode_Conversion_Error {
 
     public:
 
-        UTF8_to_UTF16_Conversion_Error(UErrorCode ec) : Unicode_Conversion_Error(ec) { }
+        UTF8_to_UTF16_Conversion_Error(UErrorCode ec) :
+            Unicode_Conversion_Error(ec) { }
 
-    };
+    }; // class UTF8_to_UTF16_Conversion_Error
 
     /// Exception thrown when a UTF-16 to UTF-8 conversion failed.
     class UTF16_to_UTF8_Conversion_Error : public Unicode_Conversion_Error {
 
     public:
 
-        UTF16_to_UTF8_Conversion_Error(UErrorCode ec) : Unicode_Conversion_Error(ec) { }
+        UTF16_to_UTF8_Conversion_Error(UErrorCode ec) :
+            Unicode_Conversion_Error(ec) { }
 
-    };
+    }; // class UTF16_to_UTF8_Conversion_Error
 
     /**
     * Convert C string with UTF-8 codes into v8::String.
@@ -75,7 +78,8 @@ namespace Osmium {
     * @param cstring A NULL terminated C string.
     * @return A local handle to a v8 String.
     */
-    template<int characters> v8::Local<v8::String> utf8_to_v8_String(const char *cstring) {
+    template <int characters>
+    inline v8::Local<v8::String> utf8_to_v8_String(const char* cstring) {
         UErrorCode error_code = U_ZERO_ERROR;
         UChar dest[characters*2];
         int32_t dest_length;
@@ -94,7 +98,8 @@ namespace Osmium {
     * @param string A v8::String.
     * @return Returns a pointer to a static buffer with a NULL terminated C string.
     */
-    template<int characters> const char *v8_String_to_utf8(v8::Local<v8::String> string) {
+    template <int characters>
+    inline const char* v8_String_to_utf8(v8::Local<v8::String> string) {
         UErrorCode error_code = U_ZERO_ERROR;
         uint16_t src[characters*2];
         static char buffer[characters*4];
@@ -107,7 +112,6 @@ namespace Osmium {
         return buffer;
     }
 
-    // this function does not work without the inline. strange.
     /**
     * Sends v8::String to output stream. This will first convert it to a UTF-8 string.
     *
@@ -115,21 +119,24 @@ namespace Osmium {
     * @param string A v8::String.
     * @param os A reference to an output stream.
     */
-    inline void v8_String_to_ostream(v8::Local<v8::String> string, std::ostream &os) {
+    inline void v8_String_to_ostream(v8::Local<v8::String> string, std::ostream& os) {
         UErrorCode error_code = U_ZERO_ERROR;
         int length = 4 * (string->Length() + 1);
-        uint16_t *src = (uint16_t *) malloc(length);
+        uint16_t* src = static_cast<uint16_t*>(malloc(length));
         if (!src) {
             throw std::bad_alloc();
         }
-        char *buffer = (char *) malloc(length);
+        char* buffer = static_cast<char*>(malloc(length));
         if (!buffer) {
+            free(src);
             throw std::bad_alloc();
         }
         int32_t buffer_length;
         string->Write(src);
         u_strToUTF8(buffer, length, &buffer_length, src, string->Length(), &error_code);
         if (error_code != U_ZERO_ERROR) {
+            free(buffer);
+            free(src);
             throw UTF16_to_UTF8_Conversion_Error(error_code);
         }
         os << buffer;
@@ -139,4 +146,4 @@ namespace Osmium {
 
 } // namespace Osmium
 
-#endif // OSMIUM_UTILS_UNICODE_HPP
+#endif // OSMIUM_JAVASCRIPT_UNICODE_HPP
diff --git a/include/osmium/javascript/wrapper/export_csv.hpp b/include/osmium/javascript/wrapper/export_csv.hpp
new file mode 100644
index 0000000..a0aa72e
--- /dev/null
+++ b/include/osmium/javascript/wrapper/export_csv.hpp
@@ -0,0 +1,80 @@
+#ifndef OSMIUM_JAVASCRIPT_WRAPPER_EXPORT_CSV_HPP
+#define OSMIUM_JAVASCRIPT_WRAPPER_EXPORT_CSV_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <v8.h>
+
+#include <osmium/javascript/unicode.hpp>
+#include <osmium/javascript/template.hpp>
+#include <osmium/export/csv.hpp>
+
+namespace Osmium {
+
+    namespace Javascript {
+
+        namespace Wrapper {
+
+            struct ExportCSV : public Osmium::Javascript::Template {
+
+                static v8::Handle<v8::Value> open(const v8::Arguments& args) {
+                    if (args.Length() != 1) {
+                        return v8::Undefined();
+                    } else {
+                        v8::String::Utf8Value str(args[0]);
+                        Osmium::Export::CSV* oc = new Osmium::Export::CSV(*str);
+                        return Osmium::Javascript::Wrapper::ExportCSV::get<Osmium::Javascript::Wrapper::ExportCSV>().create_instance((void*)(oc));
+                    }
+                }
+
+                static v8::Handle<v8::Value> print(const v8::Arguments& args, Osmium::Export::CSV* csv) {
+                    for (int i = 0; i < args.Length(); i++) {
+                        if (i != 0) {
+                            csv->out << '\t';
+                        }
+                        Osmium::v8_String_to_ostream(args[i]->ToString(), csv->out);
+                    }
+                    csv->out << std::endl;
+                    return v8::Integer::New(1);
+                }
+
+                static v8::Handle<v8::Value> close(const v8::Arguments& /*args*/, Osmium::Export::CSV* csv) {
+                    csv->out.flush();
+                    csv->out.close();
+                    return v8::Undefined();
+                }
+
+                ExportCSV() :
+                    Osmium::Javascript::Template() {
+                    js_template->Set("print", v8::FunctionTemplate::New(function_template<Osmium::Export::CSV, print>));
+                    js_template->Set("close", v8::FunctionTemplate::New(function_template<Osmium::Export::CSV, close>));
+                }
+
+            }; // class ExportCSV
+
+        } // namespace Wrapper
+
+    } // namespace Javascript
+
+} // namespace Osmium
+
+#endif // OSMIUM_JAVASCRIPT_WRAPPER_EXPORT_CSV_HPP
diff --git a/include/osmium/javascript/wrapper/export_shapefile.hpp b/include/osmium/javascript/wrapper/export_shapefile.hpp
new file mode 100644
index 0000000..c961fa0
--- /dev/null
+++ b/include/osmium/javascript/wrapper/export_shapefile.hpp
@@ -0,0 +1,196 @@
+#ifndef OSMIUM_JAVASCRIPT_WRAPPER_EXPORT_SHAPEFILE_HPP
+#define OSMIUM_JAVASCRIPT_WRAPPER_EXPORT_SHAPEFILE_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <v8.h>
+
+#include <osmium/javascript/unicode.hpp>
+#include <osmium/javascript/template.hpp>
+#include <osmium/export/shapefile.hpp>
+
+namespace Osmium {
+
+    namespace Javascript {
+
+        namespace Wrapper {
+
+            struct ExportShapefile : public Osmium::Javascript::Template {
+
+                static v8::Handle<v8::Value> open(const v8::Arguments& args) {
+                    if (args.Length() != 2) {
+                        return v8::Undefined();
+                    } else {
+                        v8::String::Utf8Value str(args[0]);
+                        v8::String::AsciiValue type(args[1]);
+                        std::string filename(*str);
+                        Osmium::Export::Shapefile* oc;
+                        if (!strcmp(*type, "point")) {
+                            oc = new Osmium::Export::PointShapefile(filename);
+                        } else if (!strcmp(*type, "line")) {
+                            oc = new Osmium::Export::LineStringShapefile(filename);
+                        } else if (!strcmp(*type, "polygon")) {
+                            oc = new Osmium::Export::PolygonShapefile(filename);
+                        } else {
+                            throw std::runtime_error("unkown shapefile type");
+                        }
+
+                        return Osmium::Javascript::Wrapper::ExportShapefile::get<Osmium::Javascript::Wrapper::ExportShapefile>().create_instance(static_cast<void*>(oc));
+                    }
+                }
+
+                static void add_string_attribute(Osmium::Export::Shapefile* shapefile, int n, v8::Local<v8::Value> value) {
+                    uint16_t source[(Osmium::Export::Shapefile::max_dbf_field_length+2)*2];
+                    char dest[(Osmium::Export::Shapefile::max_dbf_field_length+1)*4];
+                    memset(source, 0, (Osmium::Export::Shapefile::max_dbf_field_length+2)*4);
+                    memset(dest, 0, (Osmium::Export::Shapefile::max_dbf_field_length+1)*4);
+                    int32_t dest_length;
+                    UErrorCode error_code = U_ZERO_ERROR;
+                    value->ToString()->Write(source, 0, Osmium::Export::Shapefile::max_dbf_field_length+1);
+                    u_strToUTF8(dest, shapefile->field(n).width(), &dest_length, source, std::min(Osmium::Export::Shapefile::max_dbf_field_length+1, value->ToString()->Length()), &error_code);
+                    if (error_code == U_BUFFER_OVERFLOW_ERROR) {
+                        // thats ok, it just means we clip the text at that point
+                    } else if (U_FAILURE(error_code)) {
+                        throw std::runtime_error("UTF-16 to UTF-8 conversion failed");
+                    }
+                    shapefile->add_attribute(n, dest);
+                }
+
+                static void add_logical_attribute(Osmium::Export::Shapefile* shapefile, int n, v8::Local<v8::Value> value) {
+                    v8::String::Utf8Value str(value);
+
+                    if (atoi(*str) == 1 || !strncasecmp(*str, "T", 1) || !strncasecmp(*str, "Y", 1)) {
+                        shapefile->add_attribute(n, true);
+                    } else if ((!strcmp(*str, "0")) || !strncasecmp(*str, "F", 1) || !strncasecmp(*str, "N", 1)) {
+                        shapefile->add_attribute(n, false);
+                    } else {
+                        shapefile->add_attribute(n);
+                    }
+                }
+
+                /**
+                * Add a geometry to the shapefile.
+                *
+                * @param shapefile shapefile the geometry is being added to
+                * @param geometry geometry that should be added to the shapefile
+                * @param attributes a %Javascript object (hash) with the attributes
+                */
+                static bool add(Osmium::Export::Shapefile* shapefile,
+                                Osmium::Geometry::Geometry* geometry,
+                                v8::Local<v8::Object> attributes) {
+
+                    try {
+                        shapefile->add_geometry(Osmium::Geometry::create_shp_object(*geometry));
+                    } catch (Osmium::Geometry::IllegalGeometry) {
+                        return false;
+                    }
+
+                    for (size_t n=0; n < shapefile->fields().size(); ++n) {
+                        v8::Local<v8::String> key = v8::String::New(shapefile->field(n).name().c_str());
+                        if (attributes->HasRealNamedProperty(key)) {
+                            v8::Local<v8::Value> value = attributes->GetRealNamedProperty(key);
+                            if (value->IsUndefined() || value->IsNull()) {
+                                shapefile->add_attribute(n);
+                            } else {
+                                switch (shapefile->field(n).type()) {
+                                    case FTString:
+                                        add_string_attribute(shapefile, n, value);
+                                        break;
+                                    case FTInteger:
+                                        shapefile->add_attribute(n, value->Int32Value());
+                                        break;
+                                    case FTDouble:
+                                        throw std::runtime_error("fields of type double not implemented");
+                                        break;
+                                    case FTLogical:
+                                        add_logical_attribute(shapefile, n, value);
+                                        break;
+                                    default:
+                                        // should never be here
+                                        break;
+                                }
+                            }
+                        } else {
+                            shapefile->add_attribute(n);
+                        }
+                    }
+                    return true;
+                }
+
+                static v8::Handle<v8::Value> add_field(const v8::Arguments& args, Osmium::Export::Shapefile* shapefile) {
+                    if (args.Length() < 3 || args.Length() > 4) {
+                        throw std::runtime_error("Wrong number of arguments to add_field method.");
+                    }
+
+                    v8::String::Utf8Value name(args[0]);
+                    std::string sname(*name);
+
+                    v8::String::Utf8Value type(args[1]);
+                    std::string stype(*type);
+
+                    int width = args[2]->Int32Value();
+                    int decimals = (args.Length() == 4) ? args[3]->Int32Value() : 0;
+
+                    shapefile->add_field(sname, stype, width, decimals);
+
+                    return v8::Integer::New(1);
+                }
+
+                static v8::Handle<v8::Value> add(const v8::Arguments& args, Osmium::Export::Shapefile* shapefile) {
+                    if (args.Length() != 2) {
+                        throw std::runtime_error("Wrong number of arguments to add method.");
+                    }
+
+                    v8::Local<v8::Object> xxx = v8::Local<v8::Object>::Cast(args[0]);
+                    Osmium::Geometry::Geometry* geometry = static_cast<Osmium::Geometry::Geometry*>(v8::Local<v8::External>::Cast(xxx->GetInternalField(0))->Value());
+
+                    try {
+                        add(shapefile, geometry, v8::Local<v8::Object>::Cast(args[1]));
+                    } catch (Osmium::Geometry::IllegalGeometry) {
+                        std::cerr << "Ignoring object with illegal geometry." << std::endl;
+                        return v8::Integer::New(0);
+                    }
+
+                    return v8::Integer::New(1);
+                }
+
+                static v8::Handle<v8::Value> close(const v8::Arguments& /*args*/, Osmium::Export::Shapefile* shapefile) {
+                    shapefile->close();
+                    return v8::Undefined();
+                }
+
+                ExportShapefile() :
+                    Osmium::Javascript::Template() {
+                    js_template->Set("add_field", v8::FunctionTemplate::New(function_template<Osmium::Export::Shapefile, add_field>));
+                    js_template->Set("add",       v8::FunctionTemplate::New(function_template<Osmium::Export::Shapefile, add>));
+                    js_template->Set("close",     v8::FunctionTemplate::New(function_template<Osmium::Export::Shapefile, close>));
+                }
+
+            }; // class ExportShapefile
+
+        } // namespace Wrapper
+
+    } // namespace Javascript
+
+} // namespace Osmium
+
+#endif // OSMIUM_JAVASCRIPT_WRAPPER_EXPORT_SHAPEFILE_HPP
diff --git a/include/osmium/javascript/wrapper/geometry.hpp b/include/osmium/javascript/wrapper/geometry.hpp
new file mode 100644
index 0000000..505e4c3
--- /dev/null
+++ b/include/osmium/javascript/wrapper/geometry.hpp
@@ -0,0 +1,224 @@
+#ifndef OSMIUM_JAVASCRIPT_WRAPPER_GEOMETRY_HPP
+#define OSMIUM_JAVASCRIPT_WRAPPER_GEOMETRY_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <sstream>
+#include <v8.h>
+
+#include <geos/geom/Geometry.h>
+#include <geos/geom/LineString.h>
+#include <geos/geom/Polygon.h>
+
+#include <osmium/javascript/unicode.hpp>
+#include <osmium/javascript/template.hpp>
+#include <osmium/javascript/wrapper/position.hpp>
+#include <osmium/geometry/null.hpp>
+#include <osmium/geometry/point.hpp>
+#include <osmium/geometry/linestring.hpp>
+#include <osmium/geometry/polygon.hpp>
+#include <osmium/geometry/multipolygon.hpp>
+
+namespace Osmium {
+
+    namespace Javascript {
+
+        namespace Wrapper {
+
+            struct Geometry : public Osmium::Javascript::Template {
+
+                static v8::Handle<v8::Value> to_wkt(const v8::Arguments& args, Osmium::Geometry::Geometry* geometry) {
+                    std::ostringstream oss;
+                    bool with_srid = false;
+                    if (args.Length() >= 1) {
+                        with_srid = args[0]->ToBoolean()->Value();
+                    }
+                    oss << geometry->as_WKT(with_srid);
+                    return v8::String::New(oss.str().c_str());
+                }
+
+                static v8::Handle<v8::Value> to_wkb(const v8::Arguments& args, Osmium::Geometry::Geometry* geometry) {
+                    std::ostringstream oss;
+                    bool with_srid = false;
+                    if (args.Length() >= 1) {
+                        with_srid = args[0]->ToBoolean()->Value();
+                    }
+                    oss << geometry->as_WKB(with_srid);
+                    return v8::String::New(oss.str().c_str());
+                }
+
+                static v8::Handle<v8::Value> to_hexwkb(const v8::Arguments& args, Osmium::Geometry::Geometry* geometry) {
+                    std::ostringstream oss;
+                    bool with_srid = false;
+                    if (args.Length() >= 1) {
+                        with_srid = args[0]->ToBoolean()->Value();
+                    }
+                    oss << geometry->as_HexWKB(with_srid);
+                    return v8::String::New(oss.str().c_str());
+                }
+
+                Geometry() :
+                    Osmium::Javascript::Template() {
+                    js_template->Set("toWKT",    v8::FunctionTemplate::New(function_template<Osmium::Geometry::Geometry, to_wkt>));
+                    js_template->Set("toWKB",    v8::FunctionTemplate::New(function_template<Osmium::Geometry::Geometry, to_wkb>));
+                    js_template->Set("toHexWKB", v8::FunctionTemplate::New(function_template<Osmium::Geometry::Geometry, to_hexwkb>));
+                }
+
+            };
+
+            struct GeometryNull : public Osmium::Javascript::Template {
+
+                GeometryNull() :
+                    Osmium::Javascript::Template() {
+                    js_template->Set("toWKT",    v8::FunctionTemplate::New(undefined));
+                    js_template->Set("toWKB",    v8::FunctionTemplate::New(undefined));
+                    js_template->Set("toHexWKB", v8::FunctionTemplate::New(undefined));
+                    js_template->Set("toArray",  v8::FunctionTemplate::New(undefined));
+                }
+
+            };
+
+            struct GeometryPoint : public Geometry {
+
+                static v8::Handle<v8::Value> lon(Osmium::Geometry::Point* point) {
+                    return v8::Number::New(point->lon());
+                }
+
+                static v8::Handle<v8::Value> lat(Osmium::Geometry::Point* point) {
+                    return v8::Number::New(point->lat());
+                }
+
+                static v8::Handle<v8::Value> to_array(const v8::Arguments& /*args*/, Osmium::Geometry::Point* point) {
+                    return OSMPosition::to_array(point->position());
+                }
+
+                GeometryPoint() :
+                    Geometry() {
+                    js_template->SetAccessor(v8::String::NewSymbol("lon"), accessor_getter<Osmium::Geometry::Point, lon>);
+                    js_template->SetAccessor(v8::String::NewSymbol("lat"), accessor_getter<Osmium::Geometry::Point, lat>);
+                    js_template->Set("toArray", v8::FunctionTemplate::New(function_template<Osmium::Geometry::Point, to_array>));
+                }
+
+            };
+
+            struct GeometryLineString : public Geometry {
+
+                static v8::Handle<v8::Value> to_array(const v8::Arguments& /*args*/, Osmium::Geometry::LineString* ls) {
+                    v8::HandleScope scope;
+                    v8::Local<v8::Array> linestring = v8::Array::New(ls->nodes().size());
+                    unsigned int max = ls->nodes().size() - 1;
+                    const Osmium::OSM::WayNodeList& wnl = ls->nodes();
+                    if (ls->reverse()) {
+                        for (unsigned int i=0; i <= max; ++i) {
+                            linestring->Set(max - i, OSMPosition::to_array(wnl[i].position()));
+                        }
+                    } else {
+                        for (unsigned int i=0; i <= max; ++i) {
+                            linestring->Set(i, OSMPosition::to_array(wnl[i].position()));
+                        }
+                    }
+                    return scope.Close(linestring);
+                }
+
+                GeometryLineString() :
+                    Geometry() {
+                    js_template->Set("toArray", v8::FunctionTemplate::New(function_template<Osmium::Geometry::LineString, to_array>));
+                }
+
+            };
+
+            struct GeometryPolygon : public Geometry {
+
+                static v8::Handle<v8::Value> to_array(const v8::Arguments& /*args*/, Osmium::Geometry::Polygon* p) {
+                    v8::HandleScope scope;
+                    v8::Local<v8::Array> polygon = v8::Array::New(1);
+                    v8::Local<v8::Array> linear_ring = v8::Array::New(p->nodes().size());
+                    polygon->Set(0, linear_ring);
+                    unsigned int max = p->nodes().size() - 1;
+                    const Osmium::OSM::WayNodeList& wnl = p->nodes();
+                    if (p->reverse()) {
+                        for (unsigned int i=0; i <= max; ++i) {
+                            linear_ring->Set(max - i, OSMPosition::to_array(wnl[i].position()));
+                        }
+                    } else {
+                        for (unsigned int i=0; i <= max; ++i) {
+                            linear_ring->Set(i, OSMPosition::to_array(wnl[i].position()));
+                        }
+                    }
+                    return scope.Close(polygon);
+                }
+
+                GeometryPolygon() :
+                    Geometry() {
+                    js_template->Set("toArray", v8::FunctionTemplate::New(function_template<Osmium::Geometry::Polygon, to_array>));
+                }
+
+            };
+
+            struct GeometryMultiPolygon : public Geometry {
+
+                static v8::Handle<v8::Array> ring_as_array(const geos::geom::LineString* ring) {
+                    v8::HandleScope scope;
+                    const geos::geom::CoordinateSequence* cs = ring->getCoordinatesRO();
+                    v8::Local<v8::Array> ring_array = v8::Array::New(cs->getSize());
+                    for (size_t i = 0; i < cs->getSize(); ++i) {
+                        v8::Local<v8::Array> coord = v8::Array::New(2);
+                        coord->Set(0, v8::Number::New(cs->getX(i)));
+                        coord->Set(1, v8::Number::New(cs->getY(i)));
+                        ring_array->Set(i, coord);
+                    }
+
+                    return scope.Close(ring_array);
+                }
+
+                static v8::Handle<v8::Value> to_array(const v8::Arguments& /*args*/, Osmium::Geometry::MultiPolygon* multipolygon) {
+                    v8::HandleScope scope;
+                    const geos::geom::MultiPolygon* geos_multipolygon = multipolygon->borrow_geos_geometry();
+
+                    v8::Local<v8::Array> multipolygon_array = v8::Array::New(geos_multipolygon->getNumGeometries());
+
+                    for (size_t i=0; i < geos_multipolygon->getNumGeometries(); ++i) {
+                        const geos::geom::Polygon* polygon = dynamic_cast<const geos::geom::Polygon*>(geos_multipolygon->getGeometryN(i));
+                        v8::Local<v8::Array> polygon_array = v8::Array::New(polygon->getNumInteriorRing());
+                        multipolygon_array->Set(i, polygon_array);
+                        polygon_array->Set(0, ring_as_array(polygon->getExteriorRing()));
+                        for (size_t j=0; j < polygon->getNumInteriorRing(); ++j) {
+                            polygon_array->Set(j+1, ring_as_array(polygon->getInteriorRingN(j)));
+                        }
+                    }
+                    return scope.Close(multipolygon_array);
+                }
+
+                GeometryMultiPolygon() :
+                    Geometry() {
+                    js_template->Set("toArray", v8::FunctionTemplate::New(function_template<Osmium::Geometry::MultiPolygon, to_array>));
+                }
+
+            };
+
+        } // namespace Wrapper
+
+    } // namespace Javascript
+
+} // namespace Osmium
+
+#endif // OSMIUM_JAVASCRIPT_WRAPPER_GEOMETRY_HPP
diff --git a/include/osmium/javascript/wrapper/osm.hpp b/include/osmium/javascript/wrapper/osm.hpp
new file mode 100644
index 0000000..bd72f24
--- /dev/null
+++ b/include/osmium/javascript/wrapper/osm.hpp
@@ -0,0 +1,323 @@
+#ifndef OSMIUM_JAVASCRIPT_WRAPPER_OSM_HPP
+#define OSMIUM_JAVASCRIPT_WRAPPER_OSM_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+#include <v8.h>
+
+#include <osmium/javascript/unicode.hpp>
+#include <osmium/javascript/template.hpp>
+#include <osmium/javascript/wrapper/geometry.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/area.hpp>
+
+namespace Osmium {
+
+    namespace Javascript {
+
+        /**
+         * @brief Functions wrapping %Osmium objects for use from %Javascript
+         */
+        namespace Wrapper {
+
+            struct OSMTagList : public Osmium::Javascript::Template {
+
+                static v8::Handle<v8::Value> get_value_by_key(v8::Local<v8::String> property, Osmium::OSM::TagList* tag_list) {
+                    const char* key = Osmium::v8_String_to_utf8<Osmium::OSM::Tag::max_utf16_length_key>(property);
+                    const char* value = tag_list->get_value_by_key(key);
+                    if (value) {
+                        return Osmium::utf8_to_v8_String<Osmium::OSM::Tag::max_utf16_length_value>(value);
+                    }
+                    return v8::Undefined();
+                }
+
+                static v8::Handle<v8::Array> enumerate_tag_keys(Osmium::OSM::TagList* tag_list) {
+                    v8::HandleScope scope;
+                    v8::Local<v8::Array> array = v8::Array::New(tag_list->size());
+
+                    Osmium::OSM::TagList::const_iterator end = tag_list->end();
+                    int i = 0;
+                    for (Osmium::OSM::TagList::const_iterator it = tag_list->begin(); it != end; ++it) {
+                        array->Set(i++, Osmium::utf8_to_v8_String<Osmium::OSM::Tag::max_utf16_length_key>(it->key()));
+                    }
+
+                    return scope.Close(array);
+                }
+
+                OSMTagList() :
+                    Osmium::Javascript::Template() {
+                    js_template->SetNamedPropertyHandler(
+                        named_property_getter<Osmium::OSM::TagList, get_value_by_key>,
+                        0,
+                        0,
+                        0,
+                        property_enumerator<Osmium::OSM::TagList, enumerate_tag_keys>
+                    );
+                }
+
+            };
+
+            struct OSMWayNodeList : public Osmium::Javascript::Template {
+
+                static v8::Handle<v8::Value> length(Osmium::OSM::WayNodeList* wnl) {
+                    return v8::Number::New(wnl->size());
+                }
+
+                static v8::Handle<v8::Value> get_node_id(uint32_t index, Osmium::OSM::WayNodeList* wnl) {
+                    return v8::Number::New((*wnl)[index].ref());
+                }
+
+                static v8::Handle<v8::Array> enumerate_nodes(Osmium::OSM::WayNodeList* wnl) {
+                    v8::HandleScope scope;
+                    v8::Local<v8::Array> array = v8::Array::New(wnl->size());
+
+                    for (unsigned int i=0; i < wnl->size(); ++i) {
+                        array->Set(i, v8::Integer::New(i));
+                    }
+
+                    return scope.Close(array);
+                }
+
+                OSMWayNodeList() :
+                    Osmium::Javascript::Template() {
+                    js_template->SetAccessor(v8::String::NewSymbol("length"), accessor_getter<Osmium::OSM::WayNodeList, length>);
+                    js_template->SetIndexedPropertyHandler(
+                        indexed_property_getter<Osmium::OSM::WayNodeList, get_node_id>,
+                        0,
+                        0,
+                        0,
+                        property_enumerator<Osmium::OSM::WayNodeList, enumerate_nodes>
+                    );
+                }
+
+            };
+
+            struct OSMRelationMember : public Osmium::Javascript::Template {
+
+                static v8::Handle<v8::Value> ref(Osmium::OSM::RelationMember* member) {
+                    return v8::Number::New(member->ref());
+                }
+
+                static v8::Handle<v8::Value> type(Osmium::OSM::RelationMember* member) {
+                    char t[2];
+                    t[0] = member->type();
+                    t[1] = 0;
+                    return v8::String::NewSymbol(t);
+                }
+
+                static v8::Handle<v8::Value> role(Osmium::OSM::RelationMember* member) {
+                    return Osmium::utf8_to_v8_String<Osmium::OSM::RelationMember::max_utf16_length_role>(member->role());
+                }
+
+                OSMRelationMember() :
+                    Osmium::Javascript::Template() {
+                    js_template->SetAccessor(v8::String::NewSymbol("type"), accessor_getter<Osmium::OSM::RelationMember, type>);
+                    js_template->SetAccessor(v8::String::NewSymbol("ref"),  accessor_getter<Osmium::OSM::RelationMember, ref>);
+                    js_template->SetAccessor(v8::String::NewSymbol("role"), accessor_getter<Osmium::OSM::RelationMember, role>);
+                }
+
+            };
+
+            struct OSMRelationMemberList : public Osmium::Javascript::Template {
+
+                static v8::Handle<v8::Value> get_member(uint32_t index, Osmium::OSM::RelationMemberList* rml) {
+                    return OSMRelationMember::get<OSMRelationMember>().create_instance((void*)&((*rml)[index]));
+                }
+
+                static v8::Handle<v8::Array> enumerate_members(Osmium::OSM::RelationMemberList* rml) {
+                    v8::HandleScope scope;
+                    v8::Local<v8::Array> array = v8::Array::New(rml->size());
+
+                    for (unsigned int i=0; i < rml->size(); ++i) {
+                        array->Set(i, v8::Integer::New(i));
+                    }
+
+                    return scope.Close(array);
+                }
+
+                static v8::Handle<v8::Value> length(Osmium::OSM::RelationMemberList* rml) {
+                    return v8::Number::New(rml->size());
+                }
+
+                OSMRelationMemberList() :
+                    Osmium::Javascript::Template() {
+                    js_template->SetAccessor(v8::String::NewSymbol("length"), accessor_getter<Osmium::OSM::RelationMemberList, length>);
+                    js_template->SetIndexedPropertyHandler(
+                        indexed_property_getter<Osmium::OSM::RelationMemberList, get_member>,
+                        0,
+                        0,
+                        0,
+                        property_enumerator<Osmium::OSM::RelationMemberList, enumerate_members>
+                    );
+                }
+
+            };
+
+            struct OSMObject : public Osmium::Javascript::Template {
+
+                static v8::Handle<v8::Value> id(Osmium::OSM::Object* object) {
+                    return v8::Number::New(object->id());
+                }
+
+                static v8::Handle<v8::Value> version(Osmium::OSM::Object* object) {
+                    return v8::Integer::New(object->version());
+                }
+
+                static v8::Handle<v8::Value> timestamp_as_string(Osmium::OSM::Object* object) {
+                    return v8::String::New(object->timestamp_as_string().c_str());
+                }
+
+                static v8::Handle<v8::Value> uid(Osmium::OSM::Object* object) {
+                    return v8::Integer::New(object->uid());
+                }
+
+                static v8::Handle<v8::Value> user(Osmium::OSM::Object* object) {
+                    return Osmium::utf8_to_v8_String<Osmium::OSM::Object::max_utf16_length_username>(object->user());
+                }
+
+                static v8::Handle<v8::Value> changeset(Osmium::OSM::Object* object) {
+                    return v8::Number::New(object->changeset());
+                }
+
+                static v8::Handle<v8::Value> visible(Osmium::OSM::Object* object) {
+                    return v8::Boolean::New(object->visible());
+                }
+
+                static v8::Handle<v8::Value> tags(Osmium::OSM::Object* object) {
+                    return OSMTagList::get<OSMTagList>().create_instance((void*)&(object->tags()));
+                }
+
+                OSMObject() :
+                    Osmium::Javascript::Template() {
+                    js_template->SetAccessor(v8::String::NewSymbol("id"),        accessor_getter<Osmium::OSM::Object, id>);
+                    js_template->SetAccessor(v8::String::NewSymbol("version"),   accessor_getter<Osmium::OSM::Object, version>);
+                    js_template->SetAccessor(v8::String::NewSymbol("timestamp"), accessor_getter<Osmium::OSM::Object, timestamp_as_string>);
+                    js_template->SetAccessor(v8::String::NewSymbol("uid"),       accessor_getter<Osmium::OSM::Object, uid>);
+                    js_template->SetAccessor(v8::String::NewSymbol("user"),      accessor_getter<Osmium::OSM::Object, user>);
+                    js_template->SetAccessor(v8::String::NewSymbol("changeset"), accessor_getter<Osmium::OSM::Object, changeset>);
+                    js_template->SetAccessor(v8::String::NewSymbol("tags"),      accessor_getter<Osmium::OSM::Object, tags>);
+                    js_template->SetAccessor(v8::String::NewSymbol("visible"),   accessor_getter<Osmium::OSM::Object, visible>);
+                }
+
+            };
+
+            struct OSMNode : public OSMObject {
+
+                static v8::Handle<v8::Value> get_geom(Osmium::OSM::Node* node) {
+                    Osmium::Geometry::Point* geom = new Osmium::Geometry::Point(*node);
+                    return GeometryPoint::get<GeometryPoint>().create_persistent_instance<Osmium::Geometry::Point>(geom);
+                }
+
+                OSMNode() :
+                    OSMObject() {
+                    js_template->SetAccessor(v8::String::NewSymbol("geom"), accessor_getter<Osmium::OSM::Node, get_geom>);
+                }
+
+            };
+
+            struct OSMWay : public OSMObject {
+
+                static v8::Handle<v8::Value> nodes(Osmium::OSM::Way* way) {
+                    return OSMWayNodeList::get<OSMWayNodeList>().create_instance((void*)&(way->nodes()));
+                }
+
+                static v8::Handle<v8::Value> geom(Osmium::OSM::Way* way) {
+                    if (way->nodes().has_position()) {
+                        Osmium::Geometry::LineString* geom = new Osmium::Geometry::LineString(*way);
+                        return GeometryLineString::get<GeometryLineString>().create_persistent_instance<Osmium::Geometry::LineString>(geom);
+                    } else {
+                        Osmium::Geometry::Null* geom = new Osmium::Geometry::Null();
+                        return GeometryNull::get<GeometryNull>().create_persistent_instance<Osmium::Geometry::Null>(geom);
+                    }
+                }
+
+                static v8::Handle<v8::Value> reverse_geom(Osmium::OSM::Way* way) {
+                    if (way->nodes().has_position()) {
+                        Osmium::Geometry::LineString* geom = new Osmium::Geometry::LineString(*way, true);
+                        return Osmium::Javascript::Template::get<GeometryLineString>().create_persistent_instance<Osmium::Geometry::LineString>(geom);
+                    } else {
+                        Osmium::Geometry::Null* geom = new Osmium::Geometry::Null();
+                        return Osmium::Javascript::Template::get<GeometryNull>().create_persistent_instance<Osmium::Geometry::Null>(geom);
+                    }
+                }
+
+                static v8::Handle<v8::Value> polygon_geom(Osmium::OSM::Way* way) {
+                    if (way->nodes().has_position() && way->nodes().is_closed()) {
+                        Osmium::Geometry::Polygon* geom = new Osmium::Geometry::Polygon(*way);
+                        return Osmium::Javascript::Template::get<GeometryPolygon>().create_persistent_instance<Osmium::Geometry::Polygon>(geom);
+                    } else {
+                        Osmium::Geometry::Null* geom = new Osmium::Geometry::Null();
+                        return Osmium::Javascript::Template::get<GeometryNull>().create_persistent_instance<Osmium::Geometry::Null>(geom);
+                    }
+                }
+
+                OSMWay() :
+                    OSMObject() {
+                    js_template->SetAccessor(v8::String::NewSymbol("nodes"),        accessor_getter<Osmium::OSM::Way, nodes>);
+                    js_template->SetAccessor(v8::String::NewSymbol("geom"),         accessor_getter<Osmium::OSM::Way, geom>);
+                    js_template->SetAccessor(v8::String::NewSymbol("reverse_geom"), accessor_getter<Osmium::OSM::Way, reverse_geom>);
+                    js_template->SetAccessor(v8::String::NewSymbol("polygon_geom"), accessor_getter<Osmium::OSM::Way, polygon_geom>);
+                }
+
+            };
+
+            struct OSMRelation : public OSMObject {
+
+                static v8::Handle<v8::Value> members(Osmium::OSM::Relation* relation) {
+                    return OSMRelationMemberList::get<OSMRelationMemberList>().create_instance((void*)&(relation->members()));
+                }
+
+                OSMRelation() :
+                    OSMObject() {
+                    js_template->SetAccessor(v8::String::NewSymbol("members"), accessor_getter<Osmium::OSM::Relation, members>);
+                }
+
+            };
+
+            struct OSMArea : public OSMObject {
+
+                static v8::Handle<v8::Value> from(Osmium::OSM::Area* area) {
+                    const char* value = area->from_way() ? "way" : "relation";
+                    return v8::String::NewSymbol(value);
+                }
+
+                static v8::Handle<v8::Value> geom(Osmium::OSM::Area* area) {
+                    Osmium::Geometry::MultiPolygon* geom = new Osmium::Geometry::MultiPolygon(*area);
+                    return Osmium::Javascript::Template::get<GeometryMultiPolygon>().create_persistent_instance<Osmium::Geometry::MultiPolygon>(geom);
+                }
+
+                OSMArea() :
+                    OSMObject() {
+                    js_template->SetAccessor(v8::String::NewSymbol("from"), accessor_getter<Osmium::OSM::Area, from>);
+                    js_template->SetAccessor(v8::String::NewSymbol("geom"), accessor_getter<Osmium::OSM::Area, geom>);
+                }
+
+            };
+
+        } // namespace Wrapper
+
+    } // namespace Javascript
+
+} // namespace Osmium
+
+#endif // OSMIUM_JAVASCRIPT_WRAPPER_OSM_HPP
diff --git a/include/osmium/javascript/wrapper/position.hpp b/include/osmium/javascript/wrapper/position.hpp
new file mode 100644
index 0000000..957689c
--- /dev/null
+++ b/include/osmium/javascript/wrapper/position.hpp
@@ -0,0 +1,55 @@
+#ifndef OSMIUM_JAVASCRIPT_WRAPPER_POSITION_HPP
+#define OSMIUM_JAVASCRIPT_WRAPPER_POSITION_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <v8.h>
+
+#include <osmium/javascript/unicode.hpp>
+#include <osmium/javascript/template.hpp>
+#include <osmium/osm/position.hpp>
+
+namespace Osmium {
+
+    namespace Javascript {
+
+        namespace Wrapper {
+
+            struct OSMPosition {
+
+                static v8::Handle<v8::Array> to_array(const Osmium::OSM::Position& position) {
+                    v8::HandleScope scope;
+                    v8::Local<v8::Array> array = v8::Array::New(2);
+                    array->Set(0, v8::Number::New(position.lon()));
+                    array->Set(1, v8::Number::New(position.lat()));
+                    return scope.Close(array);
+                }
+
+            };
+
+        } // namespace Wrapper
+
+    } // namespace Javascript
+
+} // namespace Osmium
+
+#endif // OSMIUM_JAVASCRIPT_WRAPPER_POSITION_HPP
diff --git a/include/osmium/multipolygon/assembler.hpp b/include/osmium/multipolygon/assembler.hpp
new file mode 100644
index 0000000..55bb56b
--- /dev/null
+++ b/include/osmium/multipolygon/assembler.hpp
@@ -0,0 +1,128 @@
+#ifndef OSMIUM_MULTIPOLYGON_ASSEMBLER_HPP
+#define OSMIUM_MULTIPOLYGON_ASSEMBLER_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <osmium/smart_ptr.hpp>
+#include <osmium/debug.hpp>
+#include <osmium/osm/area.hpp>
+#include <osmium/relations/relation_info.hpp>
+#include <osmium/relations/assembler.hpp>
+#include <osmium/multipolygon/builder.hpp>
+
+namespace Osmium {
+
+    /**
+     * @brief Code related to the building of multipolygons from relations.
+     */
+    namespace MultiPolygon {
+
+        /**
+         * This class assembles MultiPolygons from relations tagged with
+         * type=multipolygon or type=boundary.
+         *
+         * @tparam THandler Chained handler class.
+         * @tparam TBuilder MultiPolygon Builder class.
+         */
+        template <class THandler, class TBuilder = Osmium::MultiPolygon::Builder>
+        class Assembler : public Osmium::Relations::Assembler<Osmium::MultiPolygon::Assembler<THandler>, Osmium::Relations::RelationInfo, false, true, false, THandler>, public Osmium::WithDebug {
+
+            typedef typename Osmium::Relations::Assembler<Osmium::MultiPolygon::Assembler<THandler>, Osmium::Relations::RelationInfo, false, true, false, THandler> AssemblerType;
+
+            bool m_attempt_repair;
+
+        public:
+
+            Assembler(THandler& handler, bool attempt_repair) :
+                AssemblerType(handler),
+                m_attempt_repair(attempt_repair) {
+            }
+
+            void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
+                const char* type = relation->tags().get_value_by_key("type");
+
+                // ignore relations without "type" tag
+                if (!type) {
+                    return;
+                }
+
+                if ((!strcmp(type, "multipolygon")) || (!strcmp(type, "boundary"))) {
+                    AssemblerType::add_relation(Osmium::Relations::RelationInfo(relation));
+                }
+            }
+
+            /**
+             * We are only interested in members of type way.
+             *
+             * Overwritten from the Assembler class.
+             */
+            bool keep_member(Osmium::Relations::RelationInfo& relation_info, const Osmium::OSM::RelationMember& member) {
+                if (member.type() == 'w') {
+                    return true;
+                }
+                if (debug && has_debug_level(1)) {
+                    std::cout << "Ignored non-way member of multipolygon/boundary relation " << relation_info.relation()->id() << "\n";
+                }
+                return false;
+            }
+
+            void way_not_in_any_relation(const shared_ptr<Osmium::OSM::Way const>& way) {
+                if (way->is_closed() && way->nodes().size() >= 4) { // way is closed and has enough nodes, build simple multipolygon
+                    if (debug && has_debug_level(2)) {
+                        std::cout << "MultiPolygon from way " << way->id() << "\n";
+                    }
+                    AssemblerType::nested_handler().area(make_shared<Osmium::OSM::Area>(*way));
+                }
+            }
+
+            void complete_relation(Osmium::Relations::RelationInfo& relation_info) {
+                if (debug && has_debug_level(2)) {
+                    std::cout << "MultiPolygon from relation " << relation_info.relation()->id() << "\n";
+                }
+
+                TBuilder builder(relation_info, m_attempt_repair);
+
+                BOOST_FOREACH(shared_ptr<Osmium::OSM::Area>& area, builder.build()) {
+                    AssemblerType::nested_handler().area(area);
+                }
+            }
+
+            void all_members_available() {
+                if (debug && has_debug_level(1)) {
+                    AssemblerType::clean_assembled_relations();
+                    if (! AssemblerType::relations().empty()) {
+                        std::cout << "Warning! Some member ways missing for these multipolygon relations:";
+                        BOOST_FOREACH(const Osmium::Relations::RelationInfo& relation_info, AssemblerType::relations()) {
+                            std::cout << " " << relation_info.relation()->id();
+                        }
+                        std::cout << "\n";
+                    }
+                }
+            }
+
+        }; // class Assembler
+
+    } // namespace MultiPolygon
+
+} // namespace Osmium
+
+#endif // OSMIUM_MULTIPOLYGON_ASSEMBLER_HPP
diff --git a/include/osmium/multipolygon/builder.hpp b/include/osmium/multipolygon/builder.hpp
new file mode 100644
index 0000000..12ce7c1
--- /dev/null
+++ b/include/osmium/multipolygon/builder.hpp
@@ -0,0 +1,980 @@
+#ifndef OSMIUM_MULTIPOLYGON_BUILDER_HPP
+#define OSMIUM_MULTIPOLYGON_BUILDER_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <cassert>
+#include <map>
+#include <stdexcept>
+#include <vector>
+
+#include <boost/foreach.hpp>
+#include <boost/dynamic_bitset.hpp>
+
+#include <geos/geom/Geometry.h>
+#include <geos/geom/Point.h>
+#include <geos/geom/LineString.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/CoordinateSequence.h>
+#include <geos/geom/CoordinateArraySequenceFactory.h>
+#include <geos/geom/LinearRing.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/MultiPolygon.h>
+#include <geos/geom/MultiLineString.h>
+#include <geos/geom/prep/PreparedPolygon.h>
+#include <geos/util/GEOSException.h>
+#include <geos/operation/polygonize/Polygonizer.h>
+#include <geos/algorithm/CGAlgorithms.h>
+
+#include <osmium/smart_ptr.hpp>
+#include <osmium/osm.hpp>
+#include <osmium/geometry.hpp>
+#include <osmium/geometry/geos.hpp>
+#include <osmium/geometry/haversine.hpp>
+#include <osmium/relations/relation_info.hpp>
+
+namespace Osmium {
+
+    namespace MultiPolygon {
+
+        struct BuildError : public std::runtime_error {
+            BuildError(const std::string& what) :
+                std::runtime_error(what) {
+            }
+        };
+
+        struct DanglingEnds : public BuildError {
+            DanglingEnds(const std::string& what) :
+                BuildError(what) {
+            }
+        };
+
+        struct NoRings : public BuildError {
+            NoRings(const std::string& what) :
+                BuildError(what) {
+            }
+        };
+
+        struct InvalidRing : public BuildError {
+            InvalidRing(const std::string& what) :
+                BuildError(what) {
+            }
+        };
+
+        struct InvalidMultiPolygon : public BuildError {
+            InvalidMultiPolygon(const std::string& what) :
+                BuildError(what) {
+            }
+        };
+
+        enum innerouter_t { UNSET, INNER, OUTER };
+        enum direction_t { NO_DIRECTION, CLOCKWISE, COUNTERCLOCKWISE };
+
+        class WayInfo {
+
+        public:
+
+            const shared_ptr<Osmium::OSM::Way const> way;
+            int used;
+            int sequence;
+            bool invert;
+            innerouter_t innerouter;
+
+            WayInfo(const shared_ptr<Osmium::OSM::Way const>& w) :
+                way(w),
+                used(-1),
+                sequence(0),
+                invert(false),
+                innerouter(UNSET) {
+            }
+
+            osm_object_id_t firstnode() const {
+                return way->nodes().front().ref();
+            }
+
+            osm_object_id_t lastnode() const {
+                return way->nodes().back().ref();
+            }
+
+        private:
+
+            // objects of this class can't be copied
+            WayInfo(const WayInfo&);
+            WayInfo& operator=(const WayInfo&);
+
+        }; // class WayInfo
+
+        class RingInfo {
+
+        public:
+
+            geos::geom::Polygon* polygon;
+            direction_t direction;
+            std::vector< shared_ptr<WayInfo> > ways;
+            std::vector< shared_ptr<RingInfo> > inner_rings;
+            weak_ptr<RingInfo> contained_by;
+
+            RingInfo(geos::geom::Polygon* p, direction_t dir) :
+                polygon(p),
+                direction(dir),
+                ways(),
+                inner_rings(),
+                contained_by() {
+            }
+
+            ~RingInfo() {
+                delete polygon;
+            }
+
+            /// Is this an inner ring?
+            bool is_inner() const {
+                return !contained_by.expired();
+            }
+
+            /**
+             * Return a copy of the outer ring of the polygon geometry in the
+             * given direction.
+             *
+             * Caller takes ownership.
+             */
+            geos::geom::LinearRing* ring_in_direction(direction_t dir) const {
+                if (direction == dir) {
+                    return dynamic_cast<geos::geom::LinearRing*>(polygon->getExteriorRing()->clone());
+                } else {
+                    geos::geom::LineString* tmp = dynamic_cast<geos::geom::LineString*>(polygon->getExteriorRing()->reverse());
+                    assert(tmp);
+                    geos::geom::LinearRing* reversed_ring = Osmium::Geometry::geos_geometry_factory()->createLinearRing(tmp->getCoordinates());
+                    delete tmp;
+                    return reversed_ring;
+                }
+            }
+
+        private:
+
+            // objects of this class can't be copied
+            RingInfo(const RingInfo&);
+            RingInfo& operator=(const RingInfo&);
+
+        }; // class RingInfo
+
+        /**
+         *
+         */
+        class Builder {
+
+            /// Relation information (including members) to build the area from.
+            const Osmium::Relations::RelationInfo& m_relation_info;
+
+            /// All areas generated will end up in this vector.
+            std::vector< shared_ptr<Osmium::OSM::Area> > m_areas;
+
+            /// Do we want to attempt repair of a broken geometry?
+            const bool m_attempt_repair;
+
+            /// This is the new area we are building.
+            shared_ptr<Osmium::OSM::Area> m_new_area;
+
+            std::vector< shared_ptr<RingInfo> > m_ringlist;
+
+            /**
+             * Return true if the given tag key is in a fixed list of keys we are
+             * not interested in.
+             */
+            bool ignore_tag(const std::string& key) const {
+                if (key == "type") return true;
+                if (key == "created_by") return true;
+                if (key == "source") return true;
+                if (key == "note") return true;
+                return false;
+            }
+
+            /**
+             * Compare tags on two OSM objects ignoring tags with certain keys
+             * defined in the ignore_tag() method.
+             *
+             * @returns true if all tags are the same, false otherwise.
+             */
+            bool same_tags(const Osmium::OSM::Object* a, const Osmium::OSM::Object* b) const {
+                if ((a == NULL) || (b == NULL)) return false;
+
+                std::map<std::string, std::string> tag_map;
+
+                BOOST_FOREACH(const Osmium::OSM::Tag& tag, a->tags()) {
+                    if (!ignore_tag(tag.key())) {
+                        tag_map[tag.key()] = tag.value();
+                    }
+                }
+
+                BOOST_FOREACH(const Osmium::OSM::Tag& tag, b->tags()) {
+                    if (!ignore_tag(tag.key())) {
+                        if (tag_map[tag.key()] != tag.value()) return false;
+                        tag_map.erase(tag.key());
+                    }
+                }
+
+                if (!tag_map.empty()) return false;
+
+                return true;
+            }
+
+            /**
+             * Check if the object is without tags ignoring tags with certain
+             * keys defined in the ignore_tag() method.
+             *
+             * @returns true if this object has no tags, false otherwise
+             */
+            bool untagged(const Osmium::OSM::Object* object) const {
+                if (object == NULL) return true;
+
+                BOOST_FOREACH(const Osmium::OSM::Tag& tag, object->tags()) {
+                    if (!ignore_tag(tag.key())) {
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+
+            /**
+             * Merge tags from way into the new area.
+             *
+             * @returns false if there was a collision, true otherwise
+             */
+            bool merge_tags(const Osmium::OSM::Way* way) {
+                bool rv = true;
+
+                std::map<std::string, std::string> tag_map;
+
+                BOOST_FOREACH(const Osmium::OSM::Tag& tag, m_new_area->tags()) {
+                    if (!ignore_tag(tag.key())) {
+                        tag_map[tag.key()] = tag.value();
+                    }
+                }
+
+                BOOST_FOREACH(const Osmium::OSM::Tag& tag, way->tags()) {
+                    if (ignore_tag(tag.key())) continue;
+
+                    if (tag_map.find(tag.key()) != tag_map.end()) {
+                        if (tag_map[tag.key()] != tag.value()) rv = false;
+                    } else {
+                        m_new_area->tags().add(tag.key(), tag.value());
+                        tag_map[tag.key()] = tag.value();
+                    }
+                }
+
+                return rv;
+            }
+
+            // objects of this class can't be copied
+            Builder(const Builder&);
+            Builder& operator=(const Builder&);
+
+        public:
+
+            Builder(const Osmium::Relations::RelationInfo& relation_info, bool attempt_repair) :
+                m_relation_info(relation_info),
+                m_areas(),
+                m_attempt_repair(attempt_repair),
+                m_new_area(make_shared<Osmium::OSM::Area>(*relation_info.relation())),
+                m_ringlist() {
+            }
+
+            /**
+             * Build the area object(s) and return it/them.
+             *
+             * @returns All areas built. This is a reference to a vector
+             *          internal to the Builder object. Do not use it after
+             *          the Builder object is gone.
+             */
+            std::vector< shared_ptr<Osmium::OSM::Area> >& build() {
+                try {
+                    {
+                        std::vector< shared_ptr<WayInfo> > ways;
+                        assemble_ways(ways);
+                        make_rings(ways);
+                    }
+
+                    determine_inner_outer_rings();
+
+                    build_multipolygon();
+                    m_areas.push_back(m_new_area);
+                } catch (BuildError& error) {
+                    std::cerr << "Building multipolygon based on relation " << m_relation_info.relation()->id() << " failed: " << error.what() << "\n";
+                }
+                return m_areas;
+            }
+
+        private:
+
+            /**
+            * This helper gets called when we find a ring that is not valid -
+            * usually because it self-intersects. The method tries to salvage
+            * as much of the ring as possible, using binary search to find the
+            * bit that needs to be cut out.
+            *
+            * @returns A valid LinearRing, or NULL if none can be built.
+            *
+            * Caller takes ownership.
+            *
+            * There is massive potential for improvement here. The biggest
+            * limitation is that this method does not deliver results for
+            * linear rings with more than one self-intersection.
+            */
+            geos::geom::LinearRing* create_non_intersecting_linear_ring(geos::geom::CoordinateSequence* orig_cs) const {
+                const std::vector<geos::geom::Coordinate>* coords = orig_cs->toVector();
+                int inv = coords->size();
+                int val = 0;
+                int current = (inv + val) / 2;
+                bool simple;
+
+                // find the longest non-intersecting stretch from the beginning
+                // of the way.
+                while (true) {
+                    std::vector<geos::geom::Coordinate>* vcoords = new std::vector<geos::geom::Coordinate>(coords->begin(), coords->begin() + current);
+                    geos::geom::CoordinateSequence* coordinate_sequence = geos::geom::CoordinateArraySequenceFactory::instance()->create(vcoords);
+                    geos::geom::LineString* linestring = Osmium::Geometry::geos_geometry_factory()->createLineString(coordinate_sequence);
+                    if (!(simple = linestring->isSimple())) {
+                        inv = current;
+                    } else {
+                        val = current;
+                    }
+                    delete linestring;
+                    if (current == (inv+val)/2) break;
+                    current = (inv + val) / 2;
+                }
+
+                if (!simple) --current;
+
+                unsigned int cutoutstart = current;
+
+                inv = 0;
+                val = coords->size();
+                current = (inv + val) / 2;
+
+                // find the longest non-intersecting stretch from the end
+                // of the way. Note that this is likely to overlap with the
+                // stretch found above - assume a 10-node way where nodes 3
+                // and 7 are identical, then we will find the sequence 0..6
+                // above, and 4..9 here!
+
+                while (true) {
+                    std::vector<geos::geom::Coordinate>* vcoords = new std::vector<geos::geom::Coordinate>(coords->begin() + current, coords->end());
+                    geos::geom::CoordinateSequence* coordinate_sequence = geos::geom::CoordinateArraySequenceFactory::instance()->create(vcoords);
+                    geos::geom::LineString* linestring = Osmium::Geometry::geos_geometry_factory()->createLineString(coordinate_sequence);
+                    if (!(simple = linestring->isSimple())) {
+                        inv = current;
+                    } else {
+                        val = current;
+                    }
+                    delete linestring;
+                    if (current == (inv+val)/2) break;
+                    current = (inv + val) / 2;
+                }
+
+                if (!simple) ++current;
+
+                unsigned int cutoutend = current;
+
+                // assemble a new linear ring by cutting out the problematic bit.
+                // if the "problematic bit" however is longer than half the way,
+                // then try using the "problematic bit" by itself.
+
+                if (cutoutstart < cutoutend) {
+                    std::swap(cutoutstart, cutoutend);
+                }
+
+                std::vector<geos::geom::Coordinate>* vv = new std::vector<geos::geom::Coordinate>();
+                if (cutoutstart-cutoutend > coords->size() / 2) {
+                    vv->insert(vv->end(), coords->begin() + cutoutend, coords->begin() + cutoutstart);
+                    vv->insert(vv->end(), vv->at(0));
+                } else {
+                    vv->insert(vv->end(), coords->begin(), coords->begin() + cutoutend);
+                    vv->insert(vv->end(), coords->begin() + cutoutstart, coords->end());
+                }
+                geos::geom::CoordinateSequence* cs = geos::geom::CoordinateArraySequenceFactory::instance()->create(vv);
+                geos::geom::LinearRing* linear_ring = Osmium::Geometry::geos_geometry_factory()->createLinearRing(cs);
+
+                if (linear_ring->isValid()) {
+                    return linear_ring;
+                }
+
+                delete linear_ring;
+                return NULL;
+            }
+
+            /**
+             * Create CoordinateSequence from Ways in a vector of WayInfos.
+             *
+             * Caller takes ownership.
+             */
+            geos::geom::CoordinateSequence* create_ring_coordinate_sequence(std::vector< shared_ptr<WayInfo> >& ways) const {
+                geos::geom::CoordinateSequence* coordinates = Osmium::Geometry::geos_geometry_factory()->getCoordinateSequenceFactory()->create(static_cast<size_t>(0), 2);
+
+                BOOST_FOREACH(const shared_ptr<WayInfo>& way_info, ways) {
+                    if (way_info->invert) {
+                        BOOST_REVERSE_FOREACH(const Osmium::OSM::WayNode& wn, way_info->way->nodes()) {
+                            coordinates->add(Osmium::Geometry::create_geos_coordinate(wn.position()), false);
+                        }
+                    } else {
+                        BOOST_FOREACH(const Osmium::OSM::WayNode& wn, way_info->way->nodes()) {
+                            coordinates->add(Osmium::Geometry::create_geos_coordinate(wn.position()), false);
+                        }
+                    }
+                }
+
+                return coordinates;
+            }
+
+            /**
+             * This method is called when a complete ring was created from one or more ways.
+             */
+            shared_ptr<RingInfo> ring_is_complete(std::vector< shared_ptr<WayInfo> >& ways, int ringcount, int num_ways_in_ring) const {
+                std::vector< shared_ptr<WayInfo> > sorted_ways(num_ways_in_ring);
+                for (unsigned int i=0; i < ways.size(); ++i) {
+                    if (ways[i]->used == ringcount) {
+                        sorted_ways[ways[i]->sequence] = ways[i];
+                    }
+                }
+
+                try {
+                    geos::geom::LinearRing* linear_ring = Osmium::Geometry::geos_geometry_factory()->createLinearRing(create_ring_coordinate_sequence(sorted_ways));
+
+                    if (!linear_ring->isSimple() || !linear_ring->isValid()) {
+                        delete linear_ring;
+                        linear_ring = NULL;
+                        if (m_attempt_repair) {
+                            scoped_ptr<geos::geom::CoordinateSequence> cs(create_ring_coordinate_sequence(sorted_ways));
+                            linear_ring = create_non_intersecting_linear_ring(cs.get());
+                            if (linear_ring) {
+                                std::cerr << "Successfully repaired an invalid ring" << std::endl;
+                            }
+                        }
+                        if (!linear_ring) return shared_ptr<RingInfo>();
+                    }
+                    bool ccw = geos::algorithm::CGAlgorithms::isCCW(linear_ring->getCoordinatesRO());
+                    return make_shared<RingInfo>(Osmium::Geometry::geos_geometry_factory()->createPolygon(linear_ring, NULL), ccw ? COUNTERCLOCKWISE : CLOCKWISE);
+                } catch (const geos::util::GEOSException& exc) {
+                    std::cerr << "Exception: " << exc.what() << std::endl;
+                    return shared_ptr<RingInfo>();
+                }
+            }
+
+            /**
+             * Try extending a proto-ring recursively until it is complete.
+             */
+            shared_ptr<RingInfo> complete_ring(std::vector< shared_ptr<WayInfo> >& ways, osm_object_id_t first, osm_object_id_t last, int ringcount, int sequence) const {
+
+                // is the ring closed already?
+                if (first == last) {
+                    return ring_is_complete(ways, ringcount, sequence);
+                }
+
+                // try extending our current line at the rear end
+                for (unsigned int i=0; i<ways.size(); ++i) {
+                    // ignore used ways
+                    if (ways[i]->used >= 0) continue;
+
+                    // remember old used state in case we have to backtrack
+                    int old_used = ways[i]->used;
+
+                    if (ways[i]->firstnode() == last) {
+                        // add way to end
+                        ways[i]->used = ringcount;
+                        ways[i]->sequence = sequence;
+                        ways[i]->invert = false;
+                        shared_ptr<RingInfo> result = complete_ring(ways, first, ways[i]->lastnode(), ringcount, sequence+1);
+                        if (result) {
+                            result->ways.push_back(ways[i]);
+                            return result;
+                        }
+                        ways[i]->used = old_used;
+                    } else if (ways[i]->lastnode() == last) {
+                        // add way to end, but turn it around
+                        ways[i]->used = ringcount;
+                        ways[i]->sequence = sequence;
+                        ways[i]->invert = true;
+                        shared_ptr<RingInfo> result = complete_ring(ways, first, ways[i]->firstnode(), ringcount, sequence+1);
+                        if (result) {
+                            result->ways.push_back(ways[i]);
+                            return result;
+                        }
+                        ways[i]->used = old_used;
+                    }
+                }
+
+                // we have exhausted all combinations
+                return shared_ptr<RingInfo>();
+            }
+
+            /**
+             * Start with the first available way and build a ring containing it.
+             *
+             * @returns true if a ring could be built, false otherwise
+             */
+            bool make_one_ring(std::vector< shared_ptr<WayInfo> >& ways) {
+                for (unsigned int i=0; i<ways.size(); ++i) {
+                    if (ways[i]->used != -1) continue;
+                    ways[i]->used = m_ringlist.size();
+                    ways[i]->sequence = 0;
+                    ways[i]->invert = false;
+                    const shared_ptr<RingInfo>& rl = complete_ring(ways, ways[i]->firstnode(), ways[i]->lastnode(), m_ringlist.size(), 1);
+                    if (rl) {
+                        rl->ways.push_back(ways[i]);
+                        m_ringlist.push_back(rl);
+                        return true;
+                    }
+                    ways[i]->used = -2;
+                    break;
+                }
+                return false;
+            }
+
+            /**
+            * Checks if there are any dangling ends, and connects them to the
+            * nearest other dangling end with a straight line. This could
+            * conceivably introduce intersections, but it's the best we can
+            * do.
+            *
+            * Returns true on success.
+            *
+            * (This implementation always succeeds because it is impossible for
+            * there to be only one dangling end in a collection of lines.)
+            */
+            bool find_and_repair_holes_in_rings(std::vector< shared_ptr<WayInfo> >& ways) const {
+
+                typedef std::vector<Osmium::OSM::WayNode> wnv_t;
+
+                wnv_t dangling_nodes;
+
+                {
+                    wnv_t end_nodes;
+
+                    // fill end_nodes vector with all end nodes of all unused ways
+                    // and reset way_infos in the process
+                    BOOST_FOREACH(shared_ptr<WayInfo>& way_info, ways) {
+                        if (way_info->used < 0) {
+                            way_info->innerouter = UNSET;
+                            way_info->used = -1;
+                            end_nodes.push_back(way_info->way->nodes().front());
+                            end_nodes.push_back(way_info->way->nodes().back());
+                        }
+                    }
+
+                    // the env_nodes vector now contains all nodes that are not
+                    // open ends twice, sort it so that those nodes are next to
+                    // each other
+                    std::sort(end_nodes.begin(), end_nodes.end());
+
+                    // find nodes that are not doubled up after the sort and add
+                    // them to the dangling_nodes vector
+                    for (wnv_t::const_iterator it = end_nodes.begin(); it != end_nodes.end(); ++it) {
+                        if ((it+1 == end_nodes.end()) || (*it != *(it+1))) {
+                            dangling_nodes.push_back(*it);
+                        } else {
+                            ++it;
+                        }
+                    }
+                }
+
+                assert(dangling_nodes.size() % 2 == 0);
+
+                // if there are dangling nodes but aren't repairing we return
+                // false
+                if (!m_attempt_repair && !dangling_nodes.empty()) {
+                    return false;
+                }
+
+                // while there are any dangling nodes, take the last
+                // one and compare the distance to each of the other
+                // nodes and find the closest one
+                while (!dangling_nodes.empty()) {
+                    Osmium::OSM::WayNode wn = dangling_nodes.back();
+                    dangling_nodes.pop_back();
+
+                    wnv_t::iterator closest = dangling_nodes.begin();
+
+                    // XXX we probably don't need a haversine here, a simpler distance formula should be ok
+                    double min_distance = Osmium::Geometry::Haversine::distance(wn.position(), closest->position());
+
+                    for (wnv_t::iterator it = closest+1; it != dangling_nodes.end(); ++it) {
+                        double distance = Osmium::Geometry::Haversine::distance(wn.position(), it->position());
+                        if (distance < min_distance) {
+                            min_distance = distance;
+                            closest = it;
+                        }
+                    }
+
+                    // create pseudo-way closing the gap
+                    shared_ptr<Osmium::OSM::Way> way = make_shared<Osmium::OSM::Way>();
+                    way->nodes().push_back(*closest);
+                    way->nodes().push_back(wn);
+                    ways.push_back(make_shared<WayInfo>(way));
+                    std::cerr << "fill gap between nodes " << closest->ref() << " and " << wn.ref() << std::endl;
+
+                    dangling_nodes.erase(closest);
+                }
+
+                return true;
+            }
+
+            /**
+             * Assemble all ways which are members of this relation into a
+             * vector of WayInfo elements. this holds room for the way pointer
+             * and some extra flags.
+             */
+            void assemble_ways(std::vector< shared_ptr<WayInfo> >& way_infos) {
+                std::map<osm_object_id_t, bool> added_ways;
+
+                BOOST_FOREACH(const shared_ptr<Osmium::OSM::Object const>& object, m_relation_info.members()) {
+                    const shared_ptr<Osmium::OSM::Way const> way = static_pointer_cast<Osmium::OSM::Way const>(object);
+
+                    // ignore members that are not ways and ways without nodes
+                    if (way && !way->nodes().empty() && (!m_attempt_repair || !added_ways[way->id()])) {
+                        if (way->timestamp() > m_new_area->timestamp()) {
+                            m_new_area->timestamp(way->timestamp());
+                        }
+                        added_ways[way->id()] = true;
+                        way_infos.push_back(make_shared<WayInfo>(way));
+                        // TODO maybe add INNER/OUTER instead of UNSET to enable later warnings on role mismatch
+                    }
+                }
+            }
+
+            /**
+             * Try and create as many closed rings as possible from the assortment
+             * of ways. make_one_ring will automatically flag those that have been
+             * used so they are not used again.
+             */
+            void make_rings(std::vector< shared_ptr<WayInfo> >& ways) {
+                while (make_one_ring(ways)) {
+                };
+
+                if (m_ringlist.empty()) {
+                    // FIXME throw NoRings("no rings");
+                }
+
+                if (!find_and_repair_holes_in_rings(ways)) {
+                    throw DanglingEnds("un-connectable dangling ends");
+                }
+
+                // re-run ring building, taking into account the newly created "repair" bits.
+                // (in case there were no dangling bits, make_one_ring terminates quickly.)
+                while (make_one_ring(ways)) {
+                };
+
+                if (m_ringlist.empty()) {
+                    throw NoRings("no rings");
+                }
+            }
+
+            /**
+             * Find out which ring contains which other ring, so we know
+             * which are inner rings and which outer. Don't trust the "role"
+             * specifications.
+             */
+            void determine_inner_outer_rings() {
+                typedef boost::dynamic_bitset<> bs_t;
+
+                std::vector<bs_t> contains(m_ringlist.size(), bs_t(m_ringlist.size()));
+
+                bs_t contained_by_even_number(m_ringlist.size());
+                contained_by_even_number.set();
+
+                // build contains relationships.
+                // we use contained_by_even_number as a helper for us to determine
+                // whether something is an inner (false) or outer (true) ring.
+
+                for (unsigned int i=0; i < m_ringlist.size(); ++i) {
+                    const scoped_ptr<geos::geom::prep::PreparedPolygon> pp(new geos::geom::prep::PreparedPolygon(m_ringlist[i]->polygon));
+                    for (unsigned int j=0; j < m_ringlist.size(); ++j) {
+                        if (i==j) continue;
+                        if (contains[j][i]) continue;
+                        contains[i][j] = pp->contains(m_ringlist[j]->polygon);
+                        contained_by_even_number[j] ^= contains[i][j];
+                    }
+                }
+
+                // we now have an array that has a true value whenever something is
+                // contained by something else; if a contains b and b contains c, then
+                // our array says that a contains b, b contains c, and a contains c.
+                // thin out the array so that only direct relationships remain (and
+                // the "a contains c" is dropped).
+
+                for (unsigned int i=0; i < m_ringlist.size(); ++i) {
+                    for (unsigned j=0; j < m_ringlist.size(); ++j) {
+                        if (contains[i][j]) {
+                            // see if there is an intermediary relationship
+                            for (unsigned int k=0; k < m_ringlist.size(); ++k) {
+                                if (k==i) continue;
+                                if (k==j) continue;
+                                if (contains[i][k] && contains[k][j]) {
+                                    // intermediary relationship exists; break this
+                                    // one up.
+                                    contains[i][j] = false;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // populate the "inner_rings" list and the "contained_by" pointer
+                // in the ring list based on the data collected. the "contains"
+                // array can be thrown away afterwards.
+
+                for (unsigned int i=0; i < m_ringlist.size(); ++i) {
+                    for (unsigned int j=0; j < m_ringlist.size(); ++j) {
+                        if (contains[i][j] && !contained_by_even_number[j]) {
+                            m_ringlist[j]->contained_by = m_ringlist[i];
+                            m_ringlist[i]->inner_rings.push_back(m_ringlist[j]);
+                        }
+                    }
+                }
+            }
+
+            void warning(const std::string& /*text*/) const {
+//                std::cerr << text << "\n";
+            }
+
+            void handle_one_way_inner_ring(RingInfo& ring_info) {
+                if (ring_info.ways.size() != 1 || untagged(ring_info.ways[0]->way.get())) {
+                    return;
+                }
+
+                if (same_tags(ring_info.ways[0]->way.get(), m_new_area.get())) {
+                    warning("duplicate_tags_on_inner");
+                    return;
+                }
+
+                if (ring_info.contained_by.lock()->ways.size() == 1 && same_tags(ring_info.ways[0]->way.get(), ring_info.contained_by.lock()->ways[0]->way.get())) {
+                    warning("duplicate_tags_on_inner");
+                    return;
+                }
+
+                std::vector<geos::geom::Geometry*>* geometries = new std::vector<geos::geom::Geometry*>;
+
+                geometries->push_back(Osmium::Geometry::geos_geometry_factory()->createPolygon(ring_info.ring_in_direction(CLOCKWISE), NULL));
+
+                shared_ptr<Osmium::OSM::Area> internal_area = make_shared<Osmium::OSM::Area>(*(ring_info.ways[0]->way));
+                internal_area->geos_geometry(Osmium::Geometry::geos_geometry_factory()->createMultiPolygon(geometries));
+                m_areas.push_back(internal_area);
+            }
+
+            /**
+             * now look at all enclosed (inner) rings that consist of only one way.
+             * if such an inner ring has way tags, do the following:
+             * - emit an extra polygon for the inner ring if the tags are different
+             *   from the relation's
+             * - emit a warning, and ignore the inner ring, if the tags are the same
+             *   as for the relation
+             **/
+            int handle_one_way_inner_rings() {
+                int outer_ring_count = 0;
+
+                for (unsigned int i=0; i < m_ringlist.size(); ++i) {
+                    if (m_ringlist[i]->is_inner()) {
+                        handle_one_way_inner_ring(*m_ringlist[i]);
+                    } else {
+                        ++outer_ring_count;
+                    }
+                }
+
+                return outer_ring_count;
+            }
+
+            void check_touching_inner_rings(const std::vector< shared_ptr<RingInfo> >& inner_rings) const {
+                if (inner_rings.empty()) {
+                    return;
+                }
+
+                for (unsigned int j=0; j < inner_rings.size()-1; ++j) {
+                    if (!inner_rings[j]->polygon) continue;
+                    const geos::geom::Geometry* ring1_geom = inner_rings[j]->polygon->getExteriorRing();
+
+                    // check if some of the rings touch another ring.
+                    for (unsigned int k=j + 1; k < inner_rings.size(); ++k) {
+                        if (!inner_rings[k]->polygon) continue;
+                        const geos::geom::Geometry* ring2_geom = inner_rings[k]->polygon->getExteriorRing();
+                        geos::geom::Geometry* inter = NULL;
+                        try {
+                            if (!ring1_geom->intersects(ring2_geom)) continue;
+                            inter = ring1_geom->intersection(ring2_geom);
+                        } catch (const geos::util::GEOSException& exc) {
+                            std::cerr << "Exception while checking intersection of rings\n";
+                            // nop;
+                        }
+
+                        if (inter) {
+                            geos::geom::GeometryTypeId type = inter->getGeometryTypeId();
+                            delete inter;
+
+                            if (type == geos::geom::GEOS_LINESTRING) {
+                                // touching inner rings
+                                // this is allowed, but we must fix them up into a valid
+                                // geometry
+                                geos::geom::Geometry* diff = ring1_geom->symDifference(ring2_geom);
+                                const scoped_ptr<geos::operation::polygonize::Polygonizer> polygonizer(new geos::operation::polygonize::Polygonizer());
+                                polygonizer->add(diff);
+                                std::vector<geos::geom::Polygon*>* polys = polygonizer->getPolygons();
+                                if (polys) {
+                                    if (polys->size() == 1) {
+                                        delete inner_rings[j]->polygon;
+                                        inner_rings[j]->polygon = (*polys)[0];
+                                        bool ccw = geos::algorithm::CGAlgorithms::isCCW((*polys)[0]->getExteriorRing()->getCoordinatesRO());
+                                        inner_rings[j]->direction = ccw ? COUNTERCLOCKWISE : CLOCKWISE;
+
+                                        delete inner_rings[k]->polygon;
+                                        inner_rings[k]->polygon = NULL;
+
+                                        delete polys;
+                                        check_touching_inner_rings(inner_rings);
+                                    } else {
+                                        BOOST_FOREACH(geos::geom::Polygon* p, *polys) {
+                                            delete p;
+                                        }
+                                        delete polys;
+                                    }
+                                    return;
+                                }
+                            } else {
+                                // other kind of intersect between inner rings; this is
+                                // not allwoed and will lead to an exception later when
+                                // building the MP
+                            }
+                        }
+                    }
+                }
+            }
+
+            /**
+            * Tries to build a multipolygon.
+            */
+            void build_multipolygon() {
+                int outer_ring_count = handle_one_way_inner_rings();
+
+                // for all non-enclosed rings, assemble holes and build polygon.
+
+                std::vector<geos::geom::Geometry*>* polygons = new std::vector<geos::geom::Geometry*>();
+
+                for (unsigned int i=0; i < m_ringlist.size(); ++i) {
+                    // look only at outer, i.e. non-contained rings. each ends up as one polygon.
+                    if (!m_ringlist[i]) continue; // can happen if ring has been deleted
+                    if (m_ringlist[i]->is_inner()) continue;
+
+                    check_touching_inner_rings(m_ringlist[i]->inner_rings);
+
+                    std::vector<geos::geom::Geometry*>* holes = new std::vector<geos::geom::Geometry*>(); // ownership is later transferred to polygon
+
+                    for (unsigned int j=0; j < m_ringlist[i]->inner_rings.size(); ++j) {
+                        if (!m_ringlist[i]->inner_rings[j]->polygon) continue;
+                        holes->push_back(m_ringlist[i]->inner_rings[j]->ring_in_direction(COUNTERCLOCKWISE));
+                    }
+
+                    geos::geom::LinearRing* ring = m_ringlist[i]->ring_in_direction(CLOCKWISE);
+
+                    geos::geom::Polygon* p = NULL;
+                    bool valid = false;
+
+                    try {
+                        p = Osmium::Geometry::geos_geometry_factory()->createPolygon(ring, holes);
+                        if (p) valid = p->isValid();
+                    } catch (const geos::util::GEOSException& exc) {
+                        // nop
+                        std::cerr << "Exception during creation of polygon for relation #" << m_relation_info.relation()->id() << ": " << exc.what() << " (treating as invalid polygon)" << std::endl;
+                    }
+                    if (!valid) {
+                        // polygon is invalid.
+                        if (p) {
+                            delete p;
+                        } else {
+                            BOOST_FOREACH(geos::geom::Geometry* g, *holes) {
+                                delete g;
+                            }
+                            delete holes;
+                            delete ring;
+                        }
+                        BOOST_FOREACH(geos::geom::Geometry* p, *polygons) {
+                            delete p;
+                        }
+                        delete polygons;
+                        throw InvalidRing("invalid ring");
+                    } else {
+                        polygons->push_back(p);
+                        for (unsigned int k=0; k < m_ringlist[i]->ways.size(); ++k) {
+                            shared_ptr<WayInfo>& wi = m_ringlist[i]->ways[k];
+                            // may have "hole filler" ways in there, not backed by
+                            // proper way and thus no tags:
+                            if (wi->way == NULL) continue;
+                            if (untagged(wi->way.get())) {
+                                // way not tagged - ok
+                            } else if (same_tags(m_new_area.get(), wi->way.get())) {
+                                // way tagged the same as relation/previous ways, ok
+                            } else if (untagged(m_new_area.get())) {
+                                // relation untagged; use tags from way; ok
+                                merge_tags(wi->way.get());
+                            } else {
+                                // this is grey-area terrain in OSM - we have tags on
+                                // the relation and a different set of tags on the outer
+                                // way(s). Use tags from outer ring only if there is
+                                // only one outer ring and it has only one way.
+                                if (outer_ring_count == 1 && m_ringlist[i]->ways.size() == 1) {
+                                    merge_tags(wi->way.get());
+                                }
+                            }
+
+                            wi->innerouter = OUTER;
+                        }
+                    }
+                }
+
+                if (polygons->empty()) {
+                    delete polygons;
+                    throw NoRings("no rings");
+                }
+
+                geos::geom::MultiPolygon* mp = NULL;
+                try {
+                    mp = Osmium::Geometry::geos_geometry_factory()->createMultiPolygon(polygons);
+                } catch (const geos::util::GEOSException& exc) {
+                    BOOST_FOREACH(geos::geom::Geometry* p, *polygons) {
+                        delete p;
+                    }
+                    delete polygons;
+                    throw InvalidMultiPolygon("multipolygon invalid");
+                };
+
+                if (mp->isValid()) {
+                    m_new_area->geos_geometry(mp);
+                    return;
+                }
+
+                delete mp;
+                throw InvalidMultiPolygon("multipolygon invalid");
+            }
+
+        }; // class Builder
+
+    } // namespace MultiPolygon
+
+} // namespace Osmium
+
+#endif // OSMIUM_MULTIPOLYGON_BUILDER_HPP
diff --git a/include/osmium/osm.hpp b/include/osmium/osm.hpp
index 236ecb0..7e7ff92 100644
--- a/include/osmium/osm.hpp
+++ b/include/osmium/osm.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp
index 251b6bf..77945cb 100644
--- a/include/osmium/osm/area.hpp
+++ b/include/osmium/osm/area.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,1096 +22,132 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <sys/types.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <vector>
-#include <sstream>
-#include <iomanip>
-#include <map>
+#include <boost/operators.hpp>
 
-#ifdef OSMIUM_WITH_MULTIPOLYGON_PROFILING
-# include <osmium/utils/timer.h>
-# define START_TIMER(x) x##_timer.start();
-# define STOP_TIMER(x) x##_timer.stop();
-#else
-# define START_TIMER(x)
-# define STOP_TIMER(x)
-#endif // OSMIUM_WITH_MULTIPOLYGON_PROFILING
-
-#ifdef OSMIUM_WITH_GEOS
-# include <geos/version.h>
-# include <geos/geom/Geometry.h>
-# include <geos/geom/Point.h>
-# include <geos/geom/LineString.h>
-# include <geos/geom/Polygon.h>
-# include <geos/geom/PrecisionModel.h>
-# include <geos/geom/CoordinateSequence.h>
-# include <geos/geom/CoordinateArraySequenceFactory.h>
-# include <geos/geom/LinearRing.h>
-# include <geos/geom/Polygon.h>
-# include <geos/geom/MultiPolygon.h>
-# include <geos/geom/MultiLineString.h>
-# include <geos/geom/prep/PreparedPolygon.h>
-# include <geos/io/WKTWriter.h>
-# include <geos/util/GEOSException.h>
-# include <geos/opLinemerge.h>
-# include <geos/operation/polygonize/Polygonizer.h>
-# include <geos/operation/distance/DistanceOp.h>
-# include <geos/opPolygonize.h>
-# include <geos/algorithm/LineIntersector.h>
-# include <geos/geomgraph/GeometryGraph.h>
-# include <geos/geomgraph/index/SegmentIntersector.h>
-
-// this should come from /usr/include/geos/algorithm, but its missing there in some Ubuntu versions
-# include "../CGAlgorithms.h"
-#endif // OSMIUM_WITH_GEOS
-
-#ifdef OSMIUM_WITH_SHPLIB
-# include <shapefil.h>
-#endif // OSMIUM_WITH_SHPLIB
+#include <geos/geom/MultiPolygon.h>
 
+#include <osmium/osm/object.hpp>
 #include <osmium/osm/way.hpp>
 #include <osmium/osm/relation.hpp>
-#include <osmium/geometry.hpp>
 
 namespace Osmium {
 
-    namespace OSM {
-
-        enum innerouter_t { UNSET, INNER, OUTER };
-        enum direction_t { NO_DIRECTION, CLOCKWISE, COUNTERCLOCKWISE };
-
-#ifdef OSMIUM_WITH_GEOS
-        class WayInfo {
-
-            friend class AreaFromRelation;
-
-            Osmium::OSM::Way *way;
-            int used;
-            int sequence;
-            bool invert;
-            bool duplicate;
-            std::string errorhint;
-            innerouter_t innerouter;
-            innerouter_t orig_innerouter;
-            geos::geom::Geometry *way_geom;
-            int firstnode;
-            int lastnode;
-            bool tried;
-
-            WayInfo() {
-                way = NULL;
-                used = -1;
-                innerouter = UNSET;
-                orig_innerouter = UNSET;
-                sequence = 0;
-                invert = false;
-                duplicate = false;
-                way_geom = NULL;
-                firstnode = -1;
-                lastnode = -1;
-                tried = false;
-            }
-
-            WayInfo(Osmium::OSM::Way *w, innerouter_t io) {
-                way = w;
-                way_geom = w->create_geos_geometry();
-                orig_innerouter = io;
-                used = -1;
-                innerouter = UNSET;
-                sequence = 0;
-                invert = false;
-                duplicate = false;
-                tried = false;
-                firstnode = w->get_first_node_id();
-                lastnode = w->get_last_node_id();
-            }
-
-            /** Special version with a synthetic way, not backed by real way object. */
-            WayInfo(geos::geom::Geometry *g, int first, int last, innerouter_t io) {
-                way = NULL;
-                way_geom = g;
-                orig_innerouter = io;
-                used = -1;
-                innerouter = UNSET;
-                sequence = 0;
-                invert = false;
-                duplicate = false;
-                tried = false;
-                firstnode = first;
-                lastnode = last;
-            }
-
-            ~WayInfo() {
-                delete way_geom;
-            }
-
-            geos::geom::Point *get_firstnode_geom() {
-                return (way ? way->get_first_node_geometry() : NULL);
-            }
-
-            geos::geom::Point *get_lastnode_geom() {
-                return (way ? way->get_last_node_geometry() : NULL);
-            }
-
-        };
-
-        class RingInfo {
-
-            friend class AreaFromRelation;
-
-            geos::geom::Polygon *polygon;
-            direction_t direction;
-            std::vector<WayInfo *> ways;
-            std::vector<RingInfo *> inner_rings;
-            bool nested;
-            RingInfo *contained_by;
-            int ring_id;
-
-            RingInfo() {
-                polygon = NULL;
-                direction = NO_DIRECTION;
-                nested = false;
-                contained_by = NULL;
-                ring_id = -1;
-            }
-        };
-#endif // OSMIUM_WITH_GEOS
-
-        /// virtual parent class for AreaFromWay and AreaFromRelation
-        class Area : public Object {
-
-        protected:
-
-#ifdef OSMIUM_WITH_GEOS
-            geos::geom::Geometry *geometry;
-
-            Area(geos::geom::Geometry *geom = NULL) : geometry(geom) {
-#else
-            Area() {
-#endif // OSMIUM_WITH_GEOS
-# ifdef OSMIUM_WITH_JAVASCRIPT
-                v8::HandleScope handle_scope;
-                js_object_instance = v8::Persistent<v8::Object>::New(JavascriptTemplate::get<JavascriptTemplate>().create_instance(this));
-# endif // OSMIUM_WITH_JAVASCRIPT
-            }
-
-            ~Area() {
-#ifdef OSMIUM_WITH_GEOS
-                delete(geometry);
-#endif // OSMIUM_WITH_GEOS
-            }
-
-        public:
-
-#ifdef OSMIUM_WITH_GEOS
-            geos::geom::Geometry* get_geometry() const {
-                return geometry;
-            }
-#endif // OSMIUM_WITH_GEOS
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Handle<v8::Value> js_from() const {
-                const char *value = (get_type() == AREA_FROM_WAY) ? "way" : "relation";
-                return v8::String::NewSymbol(value);
-            }
-
-            v8::Handle<v8::Value> js_geom() const;
+    namespace Geometry {
+        class MultiPolygon;
+    }
 
-            struct JavascriptTemplate : public Osmium::OSM::Object::JavascriptTemplate {
+    namespace {
 
-                JavascriptTemplate() : Osmium::OSM::Object::JavascriptTemplate() {
-                    js_template->SetAccessor(v8::String::NewSymbol("from"), accessor_getter<Area, &Area::js_from>);
-                    js_template->SetAccessor(v8::String::NewSymbol("geom"), accessor_getter<Area, &Area::js_geom>);
-                }
+        template <typename T>
+        int sgn(T val) {
+            return (T(0) < val) - (val < T(0));
+        }
 
-            };
+    }
 
-#endif // OSMIUM_WITH_JAVASCRIPT
-
-        }; // class Area
+    namespace OSM {
 
-        /***
-        * Area created from a way.
-        * The way pointer given to the constructor will not be stored, all
-        * needed attributes are copied.
-        */
-        class AreaFromWay : public Area {
+        /**
+         * Area objects are pseudo OSM objects created from Ways or
+         * Relations of type=multipolygon (or type=boundary).
+         *
+         * Area IDs are unique regardless of whether the Area was created
+         * from a Way or from a Relation.
+         */
+        class Area : public Object, boost::less_than_comparable<Area> {
 
-        public:
+            friend class Osmium::Geometry::MultiPolygon;
 
             WayNodeList m_node_list;
+            mutable geos::geom::MultiPolygon* m_geos_geometry;
 
-#ifdef OSMIUM_WITH_GEOS
-            AreaFromWay(Way* way, geos::geom::Geometry* geom) : Area(geom) {
-#else
-            AreaFromWay(Way* way) : Area() {
-#endif // OSMIUM_WITH_GEOS
-                id(way->id());
-                version(way->version());
-                changeset(way->changeset());
-                timestamp(way->timestamp());
-                uid(way->uid());
-                user(way->user());
-
-                tags(way->tags());
-                m_node_list = way->nodes();
-            }
-
-            ~AreaFromWay() {
-            }
-
-            const WayNodeList& nodes() const {
-                return m_node_list;
-            }
-
-            WayNodeList& nodes() {
-                return m_node_list;
-            }
-
-            osm_object_type_t get_type() const {
-                return AREA_FROM_WAY;
-            }
-
-        }; // class AreaFromWay
-
-        /***
-        * Area created from a relation with tag type=multipolygon or type=boundary
-        */
-        class AreaFromRelation : public Area {
-
-            bool boundary; ///< was this area created from relation with tag type=boundary?
-
-            /// the relation this area was build from
-            Relation *relation;
-
-            /// the member ways of this area
-            std::vector<Osmium::OSM::Way> member_ways;
-
-            /// number of ways in this area
-            int num_ways;
-
-            /// how many ways are missing before we can build the multipolygon
-            int missing_ways;
-
-            std::string geometry_error_message;
-
-            /// callback we should call when a multipolygon was completed
-            void (*callback)(Osmium::OSM::Area*);
-
-            /// whether we want to repair a broken geometry
-            bool attempt_repair;
-
-            bool ignore_tag(const std::string &s) {
-                if (s=="type") return true;
-                if (s=="created_by") return true;
-                if (s=="source") return true;
-                if (s=="note") return true;
-                return false;
-            }
-
-            bool same_tags(const Object* a, const Object* b) {
-                if ((a == NULL) || (b == NULL)) return false;
-                const TagList& at = a->tags();
-                const TagList& bt = b->tags();
-                std::map<std::string, std::string> aTags;
-
-                TagList::const_iterator end = at.end();
-                for (TagList::const_iterator it = at.begin(); it != end; ++it) {
-                    if (ignore_tag(it->key())) continue;
-                    aTags[it->key()] = it->value();
-                }
-                end = bt.end();
-                for (TagList::const_iterator it = bt.begin(); it != end; ++it) {
-                    if (ignore_tag(it->key())) continue;
-                    if (aTags[it->key()] != it->value()) return false;
-                    aTags.erase(it->key());
-                }
-                if (!aTags.empty()) return false;
-                return true;
-            }
-
-            /** returns false if there was a collision, true otherwise */
-            bool merge_tags(Object* a, const Object* b) {
-                bool rv = true;
-                TagList& at = a->tags();
-                const TagList& bt = b->tags();
-                std::map<std::string, std::string> aTags;
-                TagList::const_iterator end = at.end();
-                for (TagList::const_iterator it = at.begin(); it != end; ++it) {
-                    if (ignore_tag(it->key())) continue;
-                    aTags[it->key()] = it->value();
-                }
-                end = bt.end();
-                for (TagList::const_iterator it = bt.begin(); it != end; ++it) {
-                    if (ignore_tag(it->key())) continue;
-                    if (aTags.find(it->key()) != aTags.end()) {
-                        if (aTags[it->key()] != it->value()) rv = false;
-                    } else {
-                        at.add(it->key(), it->value());
-                        aTags[it->key()] = it->value();
-                    }
-                }
-                return rv;
-            }
-
-            bool untagged(const Object* r) {
-                if (r == NULL) return true;
-                const TagList& tags = r->tags();
-                if (tags.empty()) return true;
-                TagList::const_iterator end = tags.end();
-                for (TagList::const_iterator it = tags.begin(); it != end; ++it) {
-                    if (! ignore_tag(it->key()) ) {
-                        return false;
-                    }
-                }
-                return true;
+            const geos::geom::MultiPolygon* geos_geometry() const {
+                return m_geos_geometry;
             }
 
         public:
 
-            AreaFromRelation(Relation* r, bool b, int n, void (*callback)(Osmium::OSM::Area*), bool repair) : Area(), boundary(b), relation(r), callback(callback) {
-                num_ways = n;
-                missing_ways = n;
-#ifdef OSMIUM_WITH_GEOS
-                geometry = NULL;
-#endif // OSMIUM_WITH_GEOS
-                id(r->id());
-                attempt_repair = repair;
+            /// Construct an Area object from a Relation object.
+            Area(const Relation& relation) :
+                Object(relation),
+                m_node_list(),
+                m_geos_geometry() {
+                id((id() * 2) + sgn(id()));
             }
 
-#ifdef OSMIUM_WITH_MULTIPOLYGON_PROFILING
-            static std::vector<std::pair<std::string, timer *> > timers;
-
-            static timer write_complex_poly_timer;
-            static timer assemble_ways_timer;
-            static timer assemble_nodes_timer;
-            static timer make_one_ring_timer;
-            static timer mor_polygonizer_timer;
-            static timer mor_union_timer;
-            static timer contains_timer;
-            static timer extra_polygons_timer;
-            static timer polygon_build_timer;
-            static timer inner_ring_touch_timer;
-            static timer polygon_intersection_timer;
-            static timer multipolygon_build_timer;
-            static timer multipolygon_write_timer;
-            static timer error_write_timer;
-
-            static void init_timings() {
-                timers.push_back(std::pair<std::string, timer *> ("   thereof assemble_ways", &assemble_ways_timer));
-                timers.push_back(std::pair<std::string, timer *> ("   thereof make_one_ring", &make_one_ring_timer));
-                timers.push_back(std::pair<std::string, timer *> ("      thereof union", &mor_union_timer));
-                timers.push_back(std::pair<std::string, timer *> ("      thereof polygonizer", &mor_polygonizer_timer));
-                timers.push_back(std::pair<std::string, timer *> ("   thereof contains", &contains_timer));
-                timers.push_back(std::pair<std::string, timer *> ("   thereof extra_polygons", &extra_polygons_timer));
-                timers.push_back(std::pair<std::string, timer *> ("   thereof polygon_build", &polygon_build_timer));
-                timers.push_back(std::pair<std::string, timer *> ("      thereof inner_ring_touch", &inner_ring_touch_timer));
-                timers.push_back(std::pair<std::string, timer *> ("      thereof intersections", &polygon_intersection_timer));
-                timers.push_back(std::pair<std::string, timer *> ("   thereof multipolygon_build", &multipolygon_build_timer));
-                timers.push_back(std::pair<std::string, timer *> ("   thereof multipolygon_write", &multipolygon_write_timer));
-                timers.push_back(std::pair<std::string, timer *> ("   thereof error_write", &error_write_timer));
+            /// Construct an Area object from a Way object.
+            Area(const Way& way) :
+                Object(way),
+                m_node_list(way.nodes()),
+                m_geos_geometry() {
+                id(id() * 2);
             }
 
-            static void print_timings() {
-                for (unsigned int i=0; i<timers.size(); i++) {
-                    std::cerr << timers[i].first << ": " << *(timers[i].second) << std::endl;
-                }
+            Area(const Area& area) :
+                Object(area),
+                m_node_list(area.m_node_list),
+                m_geos_geometry(dynamic_cast<geos::geom::MultiPolygon*>(area.m_geos_geometry->clone())) {
             }
-#endif // OSMIUM_WITH_MULTIPOLYGON_PROFILING
 
-            ~AreaFromRelation() {
-                delete relation;
-                member_ways.erase(member_ways.begin(), member_ways.end());
+            Area& operator=(const Area& area) {
+                id(area.id());
+                version(area.version());
+                changeset(area.changeset());
+                timestamp(area.timestamp());
+                endtime(area.endtime());
+                uid(area.uid());
+                user(area.user());
+                visible(area.visible());
+                tags(area.tags());
+                m_node_list = area.m_node_list;
+                m_geos_geometry = dynamic_cast<geos::geom::MultiPolygon*>(area.m_geos_geometry->clone());
+                return *this;
             }
 
-            osm_object_type_t get_type() const {
-                return AREA_FROM_RELATION;
-            }
-
-            /// Add way to list of member ways. This will create a copy of the way.
-            void add_member_way(Osmium::OSM::Way *way) {
-                member_ways.push_back(*way);
-                missing_ways--;
+            ~Area() {
+                delete m_geos_geometry;
             }
 
-            /// Do we have all the ways we need to build the multipolygon?
-            bool is_complete() {
-                return missing_ways == 0;
+            osm_object_type_t type() const {
+                return AREA;
             }
 
-            void handle_complete_multipolygon() {
-#ifdef OSMIUM_WITH_GEOS
-                if (! build_geometry()) {
-                    std::cerr << "  geom build error: " << geometry_error_message << "\n";
-                }
-#endif // OSMIUM_WITH_GEOS
-                callback(this);
+            /// Was this Area created from a Way or Relation?
+            bool from_way() const {
+                return (id() % 2) == 0;
             }
 
-        private:
-
-            /**
-            * This helper gets called when we find a ring that is not valid -
-            * usually because it self-intersects. The method tries to salvage
-            * as much of the ring as possible, using binary search to find the
-            * bit that needs to be cut out. It then returns a valid LinearRing,
-            * or NULL if none can be built.
-            *
-            * There is massive potential for improvement here. The biggest
-            * limitation is that this method does not deliver results for
-            * linear rings with more than one self-intersection.
-            */
-#ifdef OSMIUM_WITH_GEOS
-            geos::geom::LinearRing *create_non_intersecting_linear_ring(geos::geom::CoordinateSequence *orig_cs) {
-                const std::vector<geos::geom::Coordinate>* coords = orig_cs->toVector();
-                int inv = coords->size();
-                int val = 0;
-                int current = (inv + val) / 2;
-                bool simple;
-
-                // find the longest non-intersecting stretch from the beginning
-                // of the way.
-                while (1) {
-                    std::vector<geos::geom::Coordinate> *vv = new std::vector<geos::geom::Coordinate>(coords->begin(), coords->begin() + current);
-                    geos::geom::CoordinateSequence *cs = geos::geom::CoordinateArraySequenceFactory::instance()->create(vv);
-                    geos::geom::LineString *a = Osmium::Geometry::geos_geometry_factory()->createLineString(cs);
-                    if (!(simple = a->isSimple())) {
-                        inv = current;
-                    } else {
-                        val = current;
-                    }
-                    delete a;
-                    if (current == (inv+val)/2) break;
-                    current = (inv + val) / 2;
-                }
-
-                if (!simple) current--;
-
-                unsigned int cutoutstart = current;
-
-                inv = 0;
-                val = coords->size();
-                current = (inv + val) / 2;
-
-                // find the longest non-intersecting stretch from the end
-                // of the way. Note that this is likely to overlap with the
-                // stretch found above - assume a 10-node way where nodes 3
-                // and 7 are identical, then we will find the sequence 0..6
-                // above, and 4..9 here!
-
-                while (1) {
-                    std::vector<geos::geom::Coordinate> *vv = new std::vector<geos::geom::Coordinate>(coords->begin() + current, coords->end());
-                    geos::geom::CoordinateSequence *cs = geos::geom::CoordinateArraySequenceFactory::instance()->create(vv);
-                    geos::geom::LineString *a = Osmium::Geometry::geos_geometry_factory()->createLineString(cs);
-                    if (!(simple = a->isSimple())) {
-                        inv = current;
-                    } else {
-                        val = current;
-                    }
-                    delete a;
-                    if (current == (inv+val)/2) break;
-                    current = (inv + val) / 2;
-                }
-                if (!simple) current++;
-                unsigned int cutoutend = current;
-
-                // assemble a new linear ring by cutting out the problematic bit.
-                // if the "problematic bit" however is longer than half the way,
-                // then try using the "problematic bit" by itself.
-
-                std::vector<geos::geom::Coordinate> *vv = new std::vector<geos::geom::Coordinate>();
-                if (cutoutstart < cutoutend) {
-                    unsigned int t = cutoutstart;
-                    cutoutstart = cutoutend;
-                    cutoutend = t;
-                }
-                if (cutoutstart-cutoutend > coords->size() / 2) {
-                    vv->insert(vv->end(), coords->begin() + cutoutend, coords->begin() + cutoutstart);
-                    vv->insert(vv->end(), vv->at(0));
-                } else {
-                    vv->insert(vv->end(), coords->begin(), coords->begin() + cutoutend);
-                    vv->insert(vv->end(), coords->begin() + cutoutstart, coords->end());
-                }
-                geos::geom::CoordinateSequence *cs = geos::geom::CoordinateArraySequenceFactory::instance()->create(vv);
-                geos::geom::LinearRing *a = Osmium::Geometry::geos_geometry_factory()->createLinearRing(cs);
-
-                // if this results in a valid ring, return it; else return NULL.
-
-                if (!a->isValid()) return NULL;
-                geos::geom::LinearRing *b = dynamic_cast<geos::geom::LinearRing *>(a->clone());
-                //delete a;
-                return b;
-            }
-
-            /**
-            * Tries to collect 1...n ways from the n ways in the given list so that
-            * they form a closed ring. If this is possible, flag those as being used
-            * by ring #ringcount in the way list and return the geometry. (The method
-            * may be called again to find further rings.) If this is not possible,
-            * return NULL.
-            */
-            RingInfo *make_one_ring(std::vector<WayInfo *> &ways, osm_object_id_t first, osm_object_id_t last, int ringcount, int sequence) {
-
-                // have we found a loop already?
-                if (first && first == last) {
-                    geos::geom::CoordinateSequence *cs = geos::geom::CoordinateArraySequenceFactory::instance()->create((size_t)0, (size_t)0);
-                    geos::geom::LinearRing *lr = NULL;
-                    try {
-                        START_TIMER(mor_polygonizer);
-                        WayInfo **sorted_ways = new WayInfo*[sequence];
-                        for (unsigned int i=0; i<ways.size(); i++) {
-                            if (ways[i]->used == ringcount) {
-                                sorted_ways[ways[i]->sequence] = ways[i];
-                            }
-                        }
-                        for (int i=0; i<sequence; i++) {
-                            cs->add(dynamic_cast<geos::geom::LineString *>(sorted_ways[i]->way_geom)->getCoordinatesRO(), false, !sorted_ways[i]->invert);
-                        }
-                        delete[] sorted_ways;
-                        lr = Osmium::Geometry::geos_geometry_factory()->createLinearRing(cs);
-                        STOP_TIMER(mor_polygonizer);
-                        if (!lr->isSimple() || !lr->isValid()) {
-                            //delete lr;
-                            lr = NULL;
-                            if (attempt_repair) {
-                                lr = create_non_intersecting_linear_ring(cs);
-                                if (lr) {
-                                    if (Osmium::debug())
-                                        std::cerr << "successfully repaired an invalid ring" << std::endl;
-                                }
-                            }
-                            if (!lr) return NULL;
-                        }
-                        bool ccw = geos::algorithm::CGAlgorithms::isCCW(lr->getCoordinatesRO());
-                        RingInfo *rl = new RingInfo();
-                        rl->direction = ccw ? COUNTERCLOCKWISE : CLOCKWISE;
-                        rl->polygon = Osmium::Geometry::geos_geometry_factory()->createPolygon(lr, NULL);
-                        return rl;
-                    } catch (const geos::util::GEOSException& exc) {
-                        if (Osmium::debug())
-                            std::cerr << "Exception: " << exc.what() << std::endl;
-                        return NULL;
-                    }
-                }
-
-                // have we not allocated anything yet, then simply start with first available way,
-                // or return NULL if all are taken.
-                if (!first) {
-                    for (unsigned int i=0; i<ways.size(); i++) {
-                        if (ways[i]->used != -1) continue;
-                        ways[i]->used = ringcount;
-                        ways[i]->sequence = 0;
-                        ways[i]->invert = false;
-                        RingInfo *rl = make_one_ring(ways, ways[i]->firstnode, ways[i]->lastnode, ringcount, 1);
-                        if (rl) {
-                            rl->ways.push_back(ways[i]);
-                            return rl;
-                        }
-                        ways[i]->used = -2;
-                        break;
-                    }
-                    return NULL;
-                }
-
-                // try extending our current line at the rear end
-                // since we are looking for a LOOP, no sense to try extending it at both ends
-                // as we'll eventually get there anyway!
-
-                for (unsigned int i=0; i<ways.size(); i++) {
-                    if (ways[i]->used < 0) ways[i]->tried = false;
-                }
-
-                for (unsigned int i=0; i<ways.size(); i++) {
-                    // ignore used ways
-                    if (ways[i]->used >= 0) continue;
-                    if (ways[i]->tried) continue;
-                    ways[i]->tried = true;
-
-                    int old_used = ways[i]->used;
-                    if (ways[i]->firstnode == last) {
-                        // add way to end
-                        ways[i]->used = ringcount;
-                        ways[i]->sequence = sequence;
-                        ways[i]->invert = false;
-                        RingInfo *result = make_one_ring(ways, first, ways[i]->lastnode, ringcount, sequence+1);
-                        if (result) {
-                            result->ways.push_back(ways[i]);
-                            return result;
-                        }
-                        ways[i]->used = old_used;
-                    } else if (ways[i]->lastnode == last) {
-                        // add way to end, but turn it around
-                        ways[i]->used = ringcount;
-                        ways[i]->sequence = sequence;
-                        ways[i]->invert = true;
-                        RingInfo *result = make_one_ring(ways, first, ways[i]->firstnode, ringcount, sequence+1);
-                        if (result) {
-                            result->ways.push_back(ways[i]);
-                            return result;
-                        }
-                        ways[i]->used = old_used;
-                    }
-                }
-                // we have exhausted all combinations.
-                return NULL;
+            /// ID of the Way or Relation objects this Area was created from.
+            osm_object_id_t orig_id() const {
+                return id() / 2;
             }
 
-            /**
-            * Checks if there are any dangling ends, and connects them to the
-            * nearest other dangling end with a straight line. This could
-            * conceivably introduce intersections, but it's the best we can
-            * do.
-            *
-            * Returns true on success.
-            *
-            * (This implementation always succeeds because it is impossible for
-            * there to be only one dangling end in a collection of lines.)
-            */
-            bool find_and_repair_holes_in_rings(std::vector<WayInfo *> *ways) {
-                // collect the remaining debris (=unused ways) and find dangling nodes.
-
-                std::map<int,geos::geom::Point *> dangling_node_map;
-                for (std::vector<WayInfo *>::iterator i(ways->begin()); i != ways->end(); i++) {
-                    if ((*i)->used < 0) {
-                        (*i)->innerouter = UNSET;
-                        (*i)->used = -1;
-                        for (int j=0; j<2; j++) {
-                            int nid = j ? (*i)->firstnode : (*i)->lastnode;
-                            if (dangling_node_map[nid]) {
-                                delete dangling_node_map[nid];
-                                dangling_node_map[nid] = NULL;
-                            } else {
-                                dangling_node_map[nid] = j ? (*i)->get_firstnode_geom() : (*i)->get_lastnode_geom();
-                            }
-                        }
-                    }
-                }
-
-                do {
-                    int mindist_id = 0;
-                    double mindist = -1;
-                    int node1_id = 0;
-                    geos::geom::Point *node1 = NULL;
-                    geos::geom::Point *node2 = NULL;
-
-                    // find one pair consisting of a random node from the list (node1)
-                    // plus the node that lies closest to it.
-                    for (std::map<int,geos::geom::Point *>::iterator i(dangling_node_map.begin()); i!= dangling_node_map.end(); i++) {
-                        if (!i->second) continue;
-                        if (node1 == NULL) {
-                            node1 = i->second;
-                            node1_id = i->first;
-                            i->second = NULL;
-                            mindist = -1;
-                        } else {
-# if GEOS_VERSION_MAJOR < 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 2)
-                            double dist = geos::operation::distance::DistanceOp::distance(node1, (i->second)); // deprecated in newer version of GEOS
-# else
-                            double dist = geos::operation::distance::DistanceOp::distance(*node1, *(i->second));
-# endif
-                            if ((dist < mindist) || (mindist < 0)) {
-                                mindist = dist;
-                                mindist_id = i->first;
-                            }
-                        }
-                    }
-
-                    // if such a pair has been found, synthesize a connecting way.
-                    if (node1 && mindist > -1) {
-                        // if we find that there are dangling nodes but aren't
-                        // repairing - break out.
-                        if (!attempt_repair) return false;
-
-                        // drop node2 from dangling map
-                        node2 = dangling_node_map[mindist_id];
-                        dangling_node_map[mindist_id] = NULL;
-
-                        std::vector<geos::geom::Coordinate> *c = new std::vector<geos::geom::Coordinate>;
-                        c->push_back(*(node1->getCoordinate()));
-                        c->push_back(*(node2->getCoordinate()));
-                        geos::geom::CoordinateSequence *cs = Osmium::Geometry::geos_geometry_factory()->getCoordinateSequenceFactory()->create(c);
-                        geos::geom::Geometry *geometry = (geos::geom::Geometry *) Osmium::Geometry::geos_geometry_factory()->createLineString(cs);
-                        ways->push_back(new WayInfo(geometry, node1_id, mindist_id, UNSET));
-                        if (Osmium::debug())
-                            std::cerr << "fill gap between nodes " << node1_id << " and " << mindist_id << std::endl;
-                    } else {
-                        break;
-                    }
-                } while (1);
-
-                return true;
+            const Area& geos_geometry(geos::geom::MultiPolygon* multipolygon) const {
+                m_geos_geometry = multipolygon;
+                return *this;
             }
 
+        }; // class Area
 
-            /**
-            * Tries to build a multipolygon from the given relation.
-            *
-            */
-            bool build_geometry() {
-                std::vector<WayInfo *> ways;
-
-                // the timestamp of the multipolygon will be the maximum of the timestamp from the relation and from all member ways
-                timestamp(relation->timestamp());
-
-                // assemble all ways which are members of this relation into a
-                // vector of WayInfo elements. this holds room for the way pointer
-                // and some extra flags.
-
-                START_TIMER(assemble_ways);
-                for (std::vector<Way>::iterator i(member_ways.begin()); i != member_ways.end(); i++) {
-                    if (i->timestamp() > timestamp()) {
-                        timestamp(i->timestamp());
-                    }
-                    WayInfo *wi = new WayInfo(&(*i), UNSET);
-                    if (wi->way_geom) {
-                        geos::io::WKTWriter wkt;
-                    } else {
-                        delete wi;
-                        return geometry_error("invalid way geometry in multipolygon relation member");
-                    }
-                    ways.push_back(wi);
-                    // TODO drop duplicate ways automatically in repair mode?
-                    // TODO maybe add INNER/OUTER instead of UNSET to enable later warnings on role mismatch
-                }
-                STOP_TIMER(assemble_ways);
-
-                std::vector<RingInfo *> ringlist;
-
-                // convenience defines to aid in clearing up on error return.
-#define clear_ringlist() \
-                for (std::vector<RingInfo *>::const_iterator rli(ringlist.begin()); rli != ringlist.end(); rli++) delete *rli;
-#define clear_wayinfo() \
-                for (std::vector<WayInfo *>::const_iterator win(ways.begin()); win != ways.end(); win++) delete *win;
-
-                // try and create as many closed rings as possible from the assortment
-                // of ways. make_one_ring will automatically flag those that have been
-                // used so they are not used again.
-
-                do {
-                    START_TIMER(make_one_ring);
-                    RingInfo *r = make_one_ring(ways, 0, 0, ringlist.size(), 0);
-                    STOP_TIMER(make_one_ring);
-                    if (r == NULL) break;
-                    r->ring_id = ringlist.size();
-                    ringlist.push_back(r);
-                } while (1);
-
-                if (ringlist.empty()) {
-                    // FIXME return geometry_error("no rings");
-                }
-
-                if (!find_and_repair_holes_in_rings(&ways)) {
-                    clear_ringlist();
-                    clear_wayinfo();
-                    return geometry_error("un-connectable dangling ends");
-                }
-
-                // re-run ring building, taking into account the newly created "repair" bits.
-                // (in case there were no dangling bits, make_one_ring terminates quickly.)
-                do {
-                    START_TIMER(make_one_ring);
-                    RingInfo *r = make_one_ring(ways, 0, 0, ringlist.size(), 0);
-                    STOP_TIMER(make_one_ring);
-                    if (r == NULL) break;
-                    r->ring_id = ringlist.size();
-                    ringlist.push_back(r);
-                } while (1);
-
-                if (ringlist.empty()) {
-                    clear_ringlist();
-                    clear_wayinfo();
-                    return geometry_error("no rings");
-                }
-
-                std::vector<geos::geom::Geometry *> *polygons = new std::vector<geos::geom::Geometry *>();
-
-                geos::geom::MultiPolygon *mp = NULL;
-
-                // find out which ring contains which other ring, so we know
-                // which are inner rings and which outer. don't trust the "role"
-                // specifications.
-
-                START_TIMER(contains);
-
-                bool **contains = new bool*[ringlist.size()];
-                bool *contained_by_even_number = new bool[ringlist.size()];
-
-                // reset array
-                for (unsigned int i=0; i<ringlist.size(); i++) {
-                    contains[i] = new bool[ringlist.size()];
-                    contained_by_even_number[i] = true;
-                    for (unsigned int j=0; j<ringlist.size(); j++) {
-                        contains[i][j] = false;
-                    }
-                }
-
-                // build contains relationships.
-                // we use contained_by_even_number as a helper for us to determine
-                // whether something is an inner (false) or outer (true) ring.
-
-                for (unsigned int i=0; i<ringlist.size(); i++) {
-                    const geos::geom::prep::PreparedPolygon *pp = new geos::geom::prep::PreparedPolygon(ringlist[i]->polygon);
-                    for (unsigned int j=0; j<ringlist.size(); j++) {
-                        if (i==j) continue;
-                        if (contains[j][i]) continue;
-                        contains[i][j] = pp->contains(ringlist[j]->polygon);
-                        contained_by_even_number[j] ^= contains[i][j];
-                    }
-                    delete pp;
-                }
-
-                // we now have an array that has a true value whenever something is
-                // contained by something else; if a contains b and b contains c, then
-                // our array says that a contains b, b contains c, and a contains c.
-                // thin out the array so that only direct relationships remain (and
-                // the "a contains c" is dropped).
-
-                for (unsigned int i=0; i<ringlist.size(); i++) {
-                    for (unsigned j=0; j<ringlist.size(); j++) {
-                        if (contains[i][j]) {
-                            // see if there is an intermediary relationship
-                            for (unsigned int k=0; k<ringlist.size(); k++) {
-                                if (k==i) continue;
-                                if (k==j) continue;
-                                if (contains[i][k] && contains[k][j]) {
-                                    // intermediary relationship exists; break this
-                                    // one up.
-                                    contains[i][j] = false;
-                                    ringlist[j]->nested = true;
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                }
-
-                // populate the "inner_rings" list and the "contained_by" pointer
-                // in the ring list based on the data collected. the "contains"
-                // array can be thrown away afterwards.
-
-                for (unsigned int i=0; i<ringlist.size(); i++) {
-                    for (unsigned int j=0; j<ringlist.size(); j++) {
-                        if (contains[i][j] && !contained_by_even_number[j]) {
-                            ringlist[j]->contained_by = ringlist[i];
-                            ringlist[i]->inner_rings.push_back(ringlist[j]);
-                        }
-                    }
-                    delete[] contains[i];
-                }
-
-                delete[] contains;
-                delete[] contained_by_even_number;
-                STOP_TIMER(contains);
-
-                // now look at all enclosed (inner) rings that consist of only one way.
-                // if such an inner ring has way tags, do the following:
-                // * emit an extra polygon for the inner ring if the tags are different
-                //   from the relation's
-                // * emit a warning, and ignore the inner ring, if the tags are the same
-                //   as for the relation
-
-                START_TIMER(extra_polygons);
-                for (unsigned int i=0; i<ringlist.size(); i++) {
-                    if (ringlist[i]->contained_by) {
-                        if (ringlist[i]->ways.size() == 1 && !untagged(ringlist[i]->ways[0]->way)) {
-                            std::vector<geos::geom::Geometry *> *g = new std::vector<geos::geom::Geometry *>;
-                            if (ringlist[i]->direction == CLOCKWISE) {
-                                g->push_back(ringlist[i]->polygon->clone());
-                            } else {
-                                geos::geom::LineString *tmp = dynamic_cast<geos::geom::LineString *>(ringlist[i]->polygon->getExteriorRing()->reverse());
-                                geos::geom::LinearRing *reversed_ring =
-                                    Osmium::Geometry::geos_geometry_factory()->createLinearRing(tmp->getCoordinates());
-                                delete tmp;
-                                g->push_back(Osmium::Geometry::geos_geometry_factory()->createPolygon(reversed_ring, NULL));
-                            }
-
-                            geos::geom::MultiPolygon *special_mp = Osmium::Geometry::geos_geometry_factory()->createMultiPolygon(g);
-
-                            if (same_tags(ringlist[i]->ways[0]->way, relation)) {
-                                // warning
-                                // warnings.insert("duplicate_tags_on_inner");
-                            } else if (ringlist[i]->contained_by->ways.size() == 1 && same_tags(ringlist[i]->ways[0]->way, ringlist[i]->contained_by->ways[0]->way)) {
-                                // warning
-                                // warnings.insert("duplicate_tags_on_inner");
-                            } else {
-                                Osmium::OSM::AreaFromWay *internal_mp =
-                                    new Osmium::OSM::AreaFromWay(ringlist[i]->ways[0]->way, special_mp);
-                                callback(internal_mp);
-                                delete internal_mp;
-                                // AreaFromWay destructor deletes the
-                                // geometry, so avoid to delete it again.
-                                special_mp = NULL;
-                            }
-                            delete special_mp;
-                        }
-                    }
-                }
-                STOP_TIMER(extra_polygons);
-
-                // for all non-enclosed rings, assemble holes and build polygon.
-
-                START_TIMER(polygon_build)
-                for (unsigned int i=0; i<ringlist.size(); i++) {
-                    // look only at outer, i.e. non-contained rings. each ends up as one polygon.
-                    if (ringlist[i] == NULL) continue; // can happen if ring has been deleted
-                    if (ringlist[i]->contained_by) continue;
-
-                    std::vector<geos::geom::Geometry *> *holes = new std::vector<geos::geom::Geometry *>(); // ownership is later transferred to polygon
-
-                    START_TIMER(inner_ring_touch)
-                    for (int j=0; j<((int)ringlist[i]->inner_rings.size()-1); j++) {
-                        if (!ringlist[i]->inner_rings[j]->polygon) continue;
-                        geos::geom::LinearRing *ring = (geos::geom::LinearRing *) ringlist[i]->inner_rings[j]->polygon->getExteriorRing();
-
-                        // check if some of the rings touch another ring.
-
-                        for (unsigned int k=j + 1; k<ringlist[i]->inner_rings.size(); k++) {
-                            if (!ringlist[i]->inner_rings[k]->polygon) continue;
-                            const geos::geom::Geometry *compare = ringlist[i]->inner_rings[k]->polygon->getExteriorRing();
-                            geos::geom::Geometry *inter = NULL;
-                            try {
-                                if (!ring->intersects(compare)) continue;
-                                inter = ring->intersection(compare);
-                            } catch (const geos::util::GEOSException& exc) {
-                                // nop;
-                            }
-                            if (inter && (inter->getGeometryTypeId() == geos::geom::GEOS_LINESTRING || inter->getGeometryTypeId() == geos::geom::GEOS_MULTILINESTRING)) {
-                                // touching inner rings
-                                // this is allowed, but we must fix them up into a valid
-                                // geometry
-                                geos::geom::Geometry *diff = ring->symDifference(compare);
-                                geos::operation::polygonize::Polygonizer *p = new geos::operation::polygonize::Polygonizer();
-                                p->add(diff);
-                                std::vector<geos::geom::Polygon*>* polys = p->getPolygons();
-                                if (polys && polys->size() == 1) {
-                                    ringlist[i]->inner_rings[j]->polygon = polys->at(0);
-                                    bool ccw = geos::algorithm::CGAlgorithms::isCCW(polys->at(0)->getExteriorRing()->getCoordinatesRO());
-                                    ringlist[i]->inner_rings[j]->direction = ccw ? COUNTERCLOCKWISE : CLOCKWISE;
-                                    ringlist[i]->inner_rings[k]->polygon = NULL;
-                                    j=-1;
-                                    break;
-                                }
-                            } else {
-                                // other kind of intersect between inner rings; this is
-                                // not allwoed and will lead to an exception later when
-                                // building the MP
-                            }
-                        }
-                    }
-                    STOP_TIMER(inner_ring_touch)
-
-                    for (unsigned int j=0; j<ringlist[i]->inner_rings.size(); j++) {
-                        if (!ringlist[i]->inner_rings[j]->polygon) continue;
-                        geos::geom::LinearRing *ring = (geos::geom::LinearRing *) ringlist[i]->inner_rings[j]->polygon->getExteriorRing();
-
-                        if (ringlist[i]->inner_rings[j]->direction == CLOCKWISE) {
-                            // reverse ring
-                            geos::geom::LineString *tmp = dynamic_cast<geos::geom::LineString *>(ring->reverse());
-                            geos::geom::LinearRing *reversed_ring =
-                                Osmium::Geometry::geos_geometry_factory()->createLinearRing(tmp->getCoordinates());
-                            delete tmp;
-                            holes->push_back(reversed_ring);
-                        } else {
-                            holes->push_back(ring);
-                        }
-                    }
-
-                    geos::geom::LinearRing *ring = (geos::geom::LinearRing *) ringlist[i]->polygon->getExteriorRing();
-                    if (ringlist[i]->direction == COUNTERCLOCKWISE) {
-                        geos::geom::LineString *tmp = dynamic_cast<geos::geom::LineString *>(ring->reverse());
-                        geos::geom::LinearRing *reversed_ring = Osmium::Geometry::geos_geometry_factory()->createLinearRing(tmp->getCoordinates());
-                        ring = reversed_ring;
-                        delete tmp;
-                    } else {
-                        ring = dynamic_cast<geos::geom::LinearRing *>(ring->clone());
-                    }
-                    delete ringlist[i]->polygon;
-                    ringlist[i]->polygon = NULL;
-                    geos::geom::Polygon *p = NULL;
-                    bool valid = false;
-
-                    try {
-                        p = Osmium::Geometry::geos_geometry_factory()->createPolygon(ring, holes);
-                        if (p) valid = p->isValid();
-                    } catch (const geos::util::GEOSException& exc) {
-                        // nop
-                        if (Osmium::debug())
-                            std::cerr << "Exception during creation of polygon for relation #" << relation->id() << ": " << exc.what() << " (treating as invalid polygon)" << std::endl;
-                    }
-                    if (!valid) {
-                        // polygon is invalid.
-                        clear_ringlist();
-                        clear_wayinfo();
-                        if (p) delete p;
-                        else delete ring;
-                        return geometry_error("invalid ring");
-                    } else {
-                        polygons->push_back(p);
-                        for (unsigned int k=0; k<ringlist[i]->ways.size(); k++) {
-                            WayInfo *wi = ringlist[i]->ways[k];
-                            // may have "hole filler" ways in there, not backed by
-                            // proper way and thus no tags:
-                            if (wi->way == NULL) continue;
-                            if (untagged(wi->way)) {
-                                // way not tagged - ok
-                            } else if (same_tags(relation, wi->way)) {
-                                // way tagged the same as relation/previous ways, ok
-                            } else if (untagged(relation)) {
-                                // relation untagged; use tags from way; ok
-                                merge_tags(relation, wi->way);
-                            }
-
-                            wi->innerouter = OUTER;
-                            if (wi->orig_innerouter == INNER && wi->errorhint.empty()) {
-                                // warning: inner/outer mismatch
-                            }
-                        }
-                        // copy tags from relation into area
-                        tags(relation->tags());
-                    }
-                    // later delete ringlist[i];
-                    // ringlist[i] = NULL;
-                }
-                STOP_TIMER(polygon_build);
-
-                clear_ringlist();
-                clear_wayinfo();
-                if (polygons->empty()) {
-                    return geometry_error("no rings");
-                }
-
-                START_TIMER(multipolygon_build);
-                bool valid = false;
-                try {
-                    mp = Osmium::Geometry::geos_geometry_factory()->createMultiPolygon(polygons);
-                    valid = mp->isValid();
-                } catch (const geos::util::GEOSException& exc) {
-                    // nop
-                };
-                STOP_TIMER(multipolygon_build);
-                if (valid) {
-                    geometry = mp;
-                    return true;
-                }
-                return geometry_error("multipolygon invalid");
-            }
-
-            bool geometry_error(const char *message) {
-                geometry_error_message = message;
-                if (Osmium::debug()) {
-                    std::cerr << "building mp failed: " << geometry_error_message << std::endl;
-                }
-                geometry = NULL;
-                return false;
-            }
-
-#endif // OSMIUM_WITH_GEOS
-
-        }; // class AreaFromRelation
+        /**
+         * Areas can be ordered by id and version.
+         * Note that we use the absolute value of the id for a
+         * better ordering of objects with negative id.
+         */
+        inline bool operator<(const Area& lhs, const Area& rhs) {
+            if (lhs.id() == rhs.id()) {
+                return lhs.version() < rhs.version();
+            } else {
+                return abs(lhs.id()) < abs(rhs.id());
+            }
+        }
+
+        /**
+         * Ordering for shared_ptrs of Areas.
+         */
+        inline bool operator<(const shared_ptr<Area const>& lhs, const shared_ptr<Area const>& rhs) {
+            return *lhs < *rhs;
+        }
 
     } // namespace OSM
 
diff --git a/include/osmium/osm/bounds.hpp b/include/osmium/osm/bounds.hpp
index afc88cd..da69a42 100644
--- a/include/osmium/osm/bounds.hpp
+++ b/include/osmium/osm/bounds.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,8 +22,6 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <limits>
-
 #include <osmium/osm/position.hpp>
 
 namespace Osmium {
@@ -34,54 +32,55 @@ namespace Osmium {
 
         public:
 
-            Bounds()
-                : m_min_x(std::numeric_limits<int32_t>::max()),
-                  m_max_x(std::numeric_limits<int32_t>::min()),
-                  m_min_y(std::numeric_limits<int32_t>::max()),
-                  m_max_y(std::numeric_limits<int32_t>::min()) {
+            Bounds() :
+                m_bottom_left(),
+                m_top_right() {
             }
 
             Bounds& extend(const Position& position) {
-                if (position.x() < m_min_x) m_min_x = position.x();
-                if (position.x() > m_max_x) m_max_x = position.x();
-                if (position.y() < m_min_y) m_min_y = position.y();
-                if (position.y() > m_max_y) m_max_y = position.y();
+                if (m_bottom_left.defined()) {
+                    if (position.x() < m_bottom_left.x()) m_bottom_left.x(position.x());
+                    if (position.x() > m_top_right.x()  ) m_top_right.x(position.x());
+                    if (position.y() < m_bottom_left.y()) m_bottom_left.y(position.y());
+                    if (position.y() > m_top_right.y()  ) m_top_right.y(position.y());
+                } else {
+                    m_bottom_left = position;
+                    m_top_right = position;
+                }
                 return *this;
             }
 
             bool defined() const {
-                return m_min_x != std::numeric_limits<int32_t>::max();
+                return m_bottom_left.defined();
             }
 
             /**
              * Bottom-left position.
              */
-            Position bl() const {
-                return Position(m_min_x, m_min_y);
+            Position bottom_left() const {
+                return m_bottom_left;
             }
 
             /**
              * Top-right position.
              */
-            Position tr() const {
-                return Position(m_max_x, m_max_y);
-            }
-
-            friend std::ostream& operator<<(std::ostream& out, const Bounds& bounds) {
-                out << '(' << bounds.bl().lon() << ',' << bounds.bl().lat() << ','
-                    << bounds.tr().lon() << ',' << bounds.tr().lat() << ')';
-                return out;
+            Position top_right() const {
+                return m_top_right;
             }
 
         private:
 
-            int32_t m_min_x;
-            int32_t m_max_x;
-            int32_t m_min_y;
-            int32_t m_max_y;
+            Osmium::OSM::Position m_bottom_left;
+            Osmium::OSM::Position m_top_right;
 
         }; // class Bounds
 
+        inline std::ostream& operator<<(std::ostream& out, const Bounds& bounds) {
+            out << '(' << bounds.bottom_left().lon() << ',' << bounds.bottom_left().lat() << ','
+                << bounds.top_right().lon() << ',' << bounds.top_right().lat() << ')';
+            return out;
+        }
+
     } // namespace OSM
 
 } // namespace Osmium
diff --git a/include/osmium/osm/meta.hpp b/include/osmium/osm/meta.hpp
index 5f8d4e3..b667bb6 100644
--- a/include/osmium/osm/meta.hpp
+++ b/include/osmium/osm/meta.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -35,10 +35,16 @@ namespace Osmium {
 
         public:
 
-            Meta() : m_bounds(), m_has_multiple_object_versions(false) {
+            Meta() :
+                m_bounds(),
+                m_has_multiple_object_versions(false),
+                m_generator() {
             }
 
-            Meta(const Bounds& bounds) : m_bounds(bounds), m_has_multiple_object_versions(false) {
+            Meta(const Bounds& bounds) :
+                m_bounds(bounds),
+                m_has_multiple_object_versions(false),
+                m_generator() {
             }
 
             Bounds& bounds() {
@@ -58,6 +64,15 @@ namespace Osmium {
                 return *this;
             }
 
+            const std::string& generator() const {
+                return m_generator;
+            }
+
+            Meta& generator(const std::string& generator) {
+                m_generator = generator;
+                return *this;
+            }
+
         private:
 
             Bounds m_bounds;
@@ -68,6 +83,9 @@ namespace Osmium {
              */
             bool m_has_multiple_object_versions;
 
+            /// Program that generated this file.
+            std::string m_generator;
+
         }; // class Meta
 
     } // namespace OSM
diff --git a/include/osmium/osm/node.hpp b/include/osmium/osm/node.hpp
index bd45ad3..3e42058 100644
--- a/include/osmium/osm/node.hpp
+++ b/include/osmium/osm/node.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,14 +22,13 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <boost/operators.hpp>
-
-#include <osmium/osm/position.hpp>
-
 /** @file
 *   @brief Contains the Osmium::OSM::Node class.
 */
 
+#include <boost/operators.hpp>
+
+#include <osmium/osm/position.hpp>
 #include <osmium/osm/object.hpp>
 
 namespace Osmium {
@@ -44,75 +43,61 @@ namespace Osmium {
 
         public:
 
-            Node() : Object(), m_position() {
-#ifdef OSMIUM_WITH_JAVASCRIPT
-                v8::HandleScope handle_scope;
-                js_object_instance = v8::Persistent<v8::Object>::New(JavascriptTemplate::get<JavascriptTemplate>().create_instance(this));
-#endif // OSMIUM_WITH_JAVASCRIPT
+            Node() :
+                Object(),
+                m_position() {
             }
 
             const Position position() const {
                 return m_position;
             }
 
-            Node& position(Position position) {
+            Node& position(const Position& position) {
                 m_position = position;
                 return *this;
             }
 
-            osm_object_type_t get_type() const {
+            osm_object_type_t type() const {
                 return NODE;
             }
 
-            void set_x(double x) {
+            void lon(double x) {
                 m_position.lon(x);
             }
 
-            void set_y(double y) {
+            void lat(double y) {
                 m_position.lat(y);
             }
 
-            double get_lon() const {
+            double lon() const {
                 return m_position.lon();
             }
 
-            double get_lat() const {
+            double lat() const {
                 return m_position.lat();
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Handle<v8::Value> js_get_geom() const;
-
-            struct JavascriptTemplate : public Osmium::OSM::Object::JavascriptTemplate {
-
-                JavascriptTemplate() : Osmium::OSM::Object::JavascriptTemplate() {
-                    js_template->SetAccessor(v8::String::NewSymbol("geom"), accessor_getter<Node, &Node::js_get_geom>);
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
-            /**
-             * Nodes can be ordered by id and version.
-             * Note that we use the absolute value of the id for a
-             * better ordering of objects with negative id.
-             */
-            friend bool operator<(const Node& lhs, const Node& rhs) {
-                if (lhs.id() == rhs.id()) {
-                    return lhs.version() < rhs.version();
-                } else {
-                    return abs(lhs.id()) < abs(rhs.id());
-                }
-            }
+        }; // class Node
 
-            /**
-             * Ordering for shared_ptrs of Nodes.
-             */
-            friend bool operator<(const shared_ptr<Node const>& lhs, const shared_ptr<Node const>& rhs) {
-                return *lhs < *rhs;
+        /**
+         * Nodes can be ordered by id and version.
+         * Note that we use the absolute value of the id for a
+         * better ordering of objects with negative id.
+         */
+        inline bool operator<(const Node& lhs, const Node& rhs) {
+            if (lhs.id() == rhs.id()) {
+                return lhs.version() < rhs.version();
+            } else {
+                return abs(lhs.id()) < abs(rhs.id());
             }
-
-        }; // class Node
+        }
+
+        /**
+         * Ordering for shared_ptrs of Nodes.
+         */
+        inline bool operator<(const shared_ptr<Node const>& lhs, const shared_ptr<Node const>& rhs) {
+            return *lhs < *rhs;
+        }
 
     } // namespace OSM
 
diff --git a/include/osmium/osm/object.hpp b/include/osmium/osm/object.hpp
index 1647a4b..512e579 100644
--- a/include/osmium/osm/object.hpp
+++ b/include/osmium/osm/object.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -26,17 +26,13 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 *   @brief Contains the Osmium::OSM::Object class.
 */
 
+#include <cassert>
 #include <cstdlib>
+#include <ctime>
 #include <stdexcept>
-#include <assert.h>
-#include <time.h>
-#include <boost/tr1/memory.hpp>
-using std::tr1::shared_ptr;
-
-#ifdef OSMIUM_WITH_SHPLIB
-# include <shapefil.h>
-#endif // OSMIUM_WITH_SHPLIB
+#include <string>
 
+#include <osmium/smart_ptr.hpp>
 #include <osmium/osm/types.hpp>
 #include <osmium/osm/tag_list.hpp>
 #include <osmium/utils/timestamp.hpp>
@@ -52,10 +48,10 @@ namespace Osmium {
 
         public:
 
-            static const int max_characters_username = 255;
-            static const int max_utf16_length_username = 2 * (max_characters_username + 1); ///< maximum number of UTF-16 units
+            static const unsigned int max_characters_username = 255;
+            static const unsigned int max_utf16_length_username = 2 * (max_characters_username + 1); ///< maximum number of UTF-16 units
 
-            static const int max_length_username = 255 * 4 + 1; ///< maximum length of OSM user name (255 UTF-8 characters + null byte)
+            static const unsigned int max_length_username = 255 * 4 + 1; ///< maximum length of OSM user name (255 UTF-8 characters + null byte)
 
             osm_object_id_t id() const {
                 return m_id;
@@ -67,7 +63,7 @@ namespace Osmium {
             }
 
             Object& id(const char* id) {
-                m_id = atol(id);
+                m_id = Osmium::string_to_osm_object_id_t(id);
                 return *this;
             }
 
@@ -81,7 +77,7 @@ namespace Osmium {
             }
 
             Object& version(const char* version) {
-                m_version = atoi(version);
+                m_version = Osmium::string_to_osm_version_t(version);
                 return *this;
             }
 
@@ -95,7 +91,7 @@ namespace Osmium {
             }
 
             Object& changeset(const char* changeset) {
-                m_changeset = atol(changeset);
+                m_changeset = Osmium::string_to_osm_changeset_id_t(changeset);
                 return *this;
             }
 
@@ -109,10 +105,14 @@ namespace Osmium {
             }
 
             Object& uid(const char* uid) {
-                m_uid = atol(uid);
+                m_uid = Osmium::string_to_osm_user_id_t(uid);
                 return *this;
             }
 
+            bool user_is_anonymous() const {
+                return m_uid == -1;
+            }
+
             time_t timestamp() const {
                 return m_timestamp;
             }
@@ -125,16 +125,16 @@ namespace Osmium {
              * Get the timestamp when this object last changed.
              * @return Timestamp as a string in ISO format (yyyy-mm-ddThh:mm:ssZ). Empty string if unset.
              */
-            std::string timestamp_as_string() const {
-                return Osmium::Utils::Timestamp::to_iso(m_timestamp);
+            const std::string timestamp_as_string() const {
+                return Osmium::Timestamp::to_iso(m_timestamp);
             }
 
             /**
              * Get the timestamp until which this object is valid.
              * @return Timestamp as a string in ISO format (yyyy-mm-ddThh:mm:ssZ). Empty string if unset.
              */
-            std::string endtime_as_string() const {
-                return Osmium::Utils::Timestamp::to_iso(m_endtime);
+            const std::string endtime_as_string() const {
+                return Osmium::Timestamp::to_iso(m_endtime);
             }
 
             /**
@@ -167,7 +167,7 @@ namespace Osmium {
              *   unchanged in this case.
              */
             Object& timestamp(const char* timestamp) {
-                m_timestamp = Osmium::Utils::Timestamp::parse_iso(timestamp);
+                m_timestamp = Osmium::Timestamp::parse_iso(timestamp);
                 return *this;
             }
 
@@ -176,7 +176,7 @@ namespace Osmium {
              * @return Pointer to internal buffer with user name.
              */
             const char* user() const {
-                return m_user;
+                return m_user.c_str();
             }
 
             /**
@@ -185,10 +185,10 @@ namespace Osmium {
              * @exception std::length_error Thrown when the username contains more than max_characters_username (255 UTF-8 characters). When the exception is thrown the username is set to "".
              */
             Object& user(const char* user) {
-                if (! memccpy(m_user, user, 0, max_length_username)) {
-                    m_user[0] = '\0';
+                if (strlen(user) > max_length_username) {
                     throw std::length_error("user name too long");
                 }
+                m_user = user;
                 return *this;
             }
 
@@ -219,11 +219,13 @@ namespace Osmium {
             Object& visible(const char* visible) {
                 if (!strcmp(visible, "false")) {
                     m_visible = false;
+                } else {
+                    m_visible = true;
                 }
                 return *this;
             }
 
-            virtual osm_object_type_t get_type() const = 0;
+            virtual osm_object_type_t type() const = 0;
 
             /**
              * Set named attribute.
@@ -256,65 +258,10 @@ namespace Osmium {
                 return m_tags;
             }
 
-            void tags(TagList& tags) {
+            void tags(const TagList& tags) {
                 m_tags = tags;
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Persistent<v8::Object> js_object_instance;
-
-            v8::Persistent<v8::Object> get_instance() const {
-                return js_object_instance;
-            }
-
-            v8::Handle<v8::Value> js_id() const {
-                return v8::Number::New(id());
-            }
-
-            v8::Handle<v8::Value> js_version() const {
-                return v8::Integer::New(version());
-            }
-
-            v8::Handle<v8::Value> js_timestamp_as_string() const {
-                return v8::String::New(timestamp_as_string().c_str());
-            }
-
-            v8::Handle<v8::Value> js_uid() const {
-                return v8::Integer::New(uid());
-            }
-
-            v8::Handle<v8::Value> js_user() const {
-                return Osmium::utf8_to_v8_String<max_utf16_length_username>(user());
-            }
-
-            v8::Handle<v8::Value> js_changeset() const {
-                return v8::Number::New(changeset());
-            }
-
-            v8::Handle<v8::Value> js_visible() const {
-                return v8::Boolean::New(visible());
-            }
-
-            v8::Handle<v8::Value> js_tags() const {
-                return tags().js_instance();
-            }
-
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->SetAccessor(v8::String::NewSymbol("id"),        accessor_getter<Object, &Object::js_id>);
-                    js_template->SetAccessor(v8::String::NewSymbol("version"),   accessor_getter<Object, &Object::js_version>);
-                    js_template->SetAccessor(v8::String::NewSymbol("timestamp"), accessor_getter<Object, &Object::js_timestamp_as_string>);
-                    js_template->SetAccessor(v8::String::NewSymbol("uid"),       accessor_getter<Object, &Object::js_uid>);
-                    js_template->SetAccessor(v8::String::NewSymbol("user"),      accessor_getter<Object, &Object::js_user>);
-                    js_template->SetAccessor(v8::String::NewSymbol("changeset"), accessor_getter<Object, &Object::js_changeset>);
-                    js_template->SetAccessor(v8::String::NewSymbol("tags"),      accessor_getter<Object, &Object::js_tags>);
-                    js_template->SetAccessor(v8::String::NewSymbol("visible"),   accessor_getter<Object, &Object::js_visible>);
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         protected:
 
             Object() :
@@ -324,27 +271,24 @@ namespace Osmium {
                 m_timestamp(0),
                 m_endtime(0),
                 m_uid(-1), // to be compatible with Osmosis we use -1 for unknown user id
+                m_user(),
                 m_visible(true),
                 m_tags() {
-                m_user[0] = '\0';
             }
 
-            Object(const Object &o) {
-                m_id        = o.m_id;
-                m_version   = o.m_version;
-                m_uid       = o.m_uid;
-                m_changeset = o.m_changeset;
-                m_timestamp = o.m_timestamp;
-                m_endtime   = o.m_endtime;
-                m_tags      = o.m_tags;
-                m_visible   = o.m_visible;
-                strncpy(m_user, o.m_user, max_length_username);
+            Object(const Object& o) :
+                m_id(o.m_id),
+                m_version(o.m_version),
+                m_changeset(o.m_changeset),
+                m_timestamp(o.m_timestamp),
+                m_endtime(o.m_endtime),
+                m_uid(o.m_uid),
+                m_user(o.m_user),
+                m_visible(o.m_visible),
+                m_tags(o.m_tags) {
             }
 
             virtual ~Object() {
-#ifdef OSMIUM_WITH_JAVASCRIPT
-                js_object_instance.Dispose();
-#endif // OSMIUM_WITH_JAVASCRIPT
             }
 
         private:
@@ -355,7 +299,7 @@ namespace Osmium {
             time_t             m_timestamp;   ///< when this object changed last
             time_t             m_endtime;     ///< when this object version was replaced by a new one
             osm_user_id_t      m_uid;         ///< user id of user who last changed this object
-            char m_user[max_length_username]; ///< name of user who last changed this object
+            std::string        m_user;        ///< name of user who last changed this object
             bool               m_visible;     ///< object visible (only when working with history data)
 
             TagList m_tags;
diff --git a/include/osmium/osm/position.hpp b/include/osmium/osm/position.hpp
index c385739..5dd8721 100644
--- a/include/osmium/osm/position.hpp
+++ b/include/osmium/osm/position.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,40 +22,75 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <stdint.h>
+#include <cmath>
 #include <ostream>
-#include <limits>
-#include <math.h>
-
-#ifdef OSMIUM_WITH_GEOS
-# include <geos/geom/Coordinate.h>
-#endif
+#include <stdint.h>
+#include <boost/operators.hpp>
+#include <boost/integer_traits.hpp>
 
 namespace Osmium {
 
     namespace OSM {
 
+        const int coordinate_precision = 10000000;
+
+        namespace {
+
+            inline int32_t double_to_fix(double c) {
+                return round(c * coordinate_precision);
+            }
+
+            inline double fix_to_double(int32_t c) {
+                return static_cast<double>(c) / coordinate_precision;
+            }
+
+        }
+
         /**
-        * Positions are stored in 32 bit integers for the x and y
-        * coordinates, respectively. This gives you an accuracy of a few
-        * centimeters, good enough for OSM use. (The main OSM database uses
-        * the same scheme.)
-        */
-        class Position {
+         * Positions define a place on earth.
+         *
+         * Positions are stored in 32 bit integers for the x and y
+         * coordinates, respectively. This gives you an accuracy of a few
+         * centimeters, good enough for %OSM use. (The main %OSM database
+         * uses the same scheme.)
+         *
+         * An undefined (invalid) Position can be created by calling the
+         * constructor without parameters.
+         *
+         * Coordinates are never checked whether they are inside bounds.
+         */
+        class Position : boost::totally_ordered<Position> {
 
         public:
 
-            explicit Position() : m_x(std::numeric_limits<int32_t>::max()), m_y(std::numeric_limits<int32_t>::max()) {
+            /// this value is used for a coordinate to mark it as invalid or undefined
+            static const int32_t invalid = boost::integer_traits<int32_t>::const_max;
+
+            /**
+             * Create undefined Position.
+             */
+            explicit Position() :
+                m_x(invalid),
+                m_y(invalid) {
+            }
+
+            explicit Position(int32_t x, int32_t y) :
+                m_x(x),
+                m_y(y) {
             }
 
-            explicit Position(int32_t x, int32_t y) : m_x(x), m_y(y) {
+            explicit Position(int64_t x, int64_t y) :
+                m_x(x),
+                m_y(y) {
             }
 
-            explicit Position(double lon, double lat) : m_x(double_to_fix(lon)), m_y(double_to_fix(lat)) {
+            explicit Position(double lon, double lat) :
+                m_x(double_to_fix(lon)),
+                m_y(double_to_fix(lat)) {
             }
 
             bool defined() const {
-                return m_x != std::numeric_limits<int32_t>::max() && m_x != std::numeric_limits<int32_t>::min();
+                return m_x != invalid && m_y != invalid;
             }
 
             int32_t x() const {
@@ -66,6 +101,16 @@ namespace Osmium {
                 return m_y;
             }
 
+            Position& x(int32_t x) {
+                m_x = x;
+                return *this;
+            }
+
+            Position& y(int32_t y) {
+                m_y = y;
+                return *this;
+            }
+
             double lon() const {
                 return fix_to_double(m_x);
             }
@@ -84,68 +129,37 @@ namespace Osmium {
                 return *this;
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Handle<v8::Array> js_to_array() const {
-                v8::HandleScope scope;
-                v8::Local<v8::Array> array = v8::Array::New(2);
-                array->Set(0, v8::Number::New(lon()));
-                array->Set(1, v8::Number::New(lat()));
-                return scope.Close(array);
-            }
-#endif // OSMIUM_WITH_JAVASCRIPT
-
-            friend bool operator==(const Position& p1, const Position& p2) {
-                return p1.m_x == p2.m_x && p1.m_y == p2.m_y;
-            }
-
-            friend bool operator!=(const Position& p1, const Position& p2) {
-                return !(p1 == p2);
-            }
-
-            friend std::ostream& operator<<(std::ostream& out, const Position& position) {
-                out << '(' << position.lon() << ',' << position.lat() << ')';
-                return out;
-            }
-
-            /// conversion to uint32_t
-            operator uint32_t() const {
-                int32_t x = 180 + m_x / precision;
-                int32_t y =  90 - m_y / precision;
-
-                if (x < 0) x = 0;
-                if (y < 0) y = 0;
-                if (x >= 360) x = 359;
-                if (y >= 180) y = 179;
-
-                return 360 * y + x;
-            }
-
-#ifdef OSMIUM_WITH_GEOS
-            /**
-             * Conversion of Position to GEOS Coordinate.
-             */
-            operator geos::geom::Coordinate() const {
-                geos::geom::Coordinate c(lon(), lat());
-                return c;
-            }
-#endif
-
         private:
 
-            static const int precision = 10000000;
-
             int32_t m_x;
             int32_t m_y;
 
-            static int32_t double_to_fix(double c) {
-                return round(c * precision);
-            }
+        };
 
-            static double fix_to_double(int32_t c) {
-                return static_cast<double>(c) / precision;
-            }
+        /**
+         * Positions are equal if both coordinates are equal.
+         */
+        inline bool operator==(const Position& p1, const Position& p2) {
+            return p1.x() == p2.x() && p1.y() == p2.y();
+        }
 
-        };
+        /**
+         * Compare two positions by comparing first the x and then the
+         * y coordinate.
+         * If the position is invalid the result is undefined.
+         */
+        inline bool operator<(const Position& p1, const Position& p2) {
+            if (p1.x() == p2.x()) {
+                return p1.y() < p2.y();
+            } else {
+                return p1.x() < p2.x();
+            }
+        }
+
+        inline std::ostream& operator<<(std::ostream& out, const Position& position) {
+            out << '(' << position.lon() << ',' << position.lat() << ')';
+            return out;
+        }
 
     } // namespace OSM
 
diff --git a/include/osmium/osm/relation.hpp b/include/osmium/osm/relation.hpp
index b053210..2211690 100644
--- a/include/osmium/osm/relation.hpp
+++ b/include/osmium/osm/relation.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -35,80 +35,61 @@ namespace Osmium {
 
         public:
 
-            Relation() : Object(), m_members() {
-#ifdef OSMIUM_WITH_JAVASCRIPT
-                v8::HandleScope handle_scope;
-                js_object_instance = v8::Persistent<v8::Object>::New(JavascriptTemplate::get<JavascriptTemplate>().create_instance(this));
-#endif // OSMIUM_WITH_JAVASCRIPT
+            Relation() :
+                Object(),
+                m_members() {
             }
 
-            Relation(const Relation &r) : Object(r) {
-                m_members = r.members();
-#ifdef OSMIUM_WITH_JAVASCRIPT
-                v8::HandleScope handle_scope;
-                js_object_instance = v8::Persistent<v8::Object>::New(JavascriptTemplate::get<JavascriptTemplate>().create_instance(this));
-#endif // OSMIUM_WITH_JAVASCRIPT
+            Relation(const Relation& relation) :
+                Object(relation),
+                m_members(relation.members()) {
             }
 
             const RelationMemberList& members() const {
                 return m_members;
             }
 
-            osm_object_type_t get_type() const {
+            osm_object_type_t type() const {
                 return RELATION;
             }
 
-            void add_member(const char type, osm_object_id_t ref, const char *role) {
+            void add_member(const char type, osm_object_id_t ref, const char* role) {
                 m_members.add_member(type, ref, role);
             }
 
-            const RelationMember *get_member(osm_sequence_id_t index) const {
+            const RelationMember* get_member(osm_sequence_id_t index) const {
                 if (index < m_members.size()) {
                     return &m_members[index];
                 }
                 return NULL;
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Handle<v8::Value> js_members() const {
-                return members().js_instance();
-            }
-
-            struct JavascriptTemplate : public Osmium::OSM::Object::JavascriptTemplate {
-
-                JavascriptTemplate() : Osmium::OSM::Object::JavascriptTemplate() {
-                    js_template->SetAccessor(v8::String::NewSymbol("members"), accessor_getter<Relation, &Relation::js_members>);
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
-            /**
-             * Relations can be ordered by id and version.
-             * Note that we use the absolute value of the id for a
-             * better ordering of objects with negative ids.
-             */
-            friend bool operator<(const Relation& lhs, const Relation& rhs) {
-                if (lhs.id() == rhs.id()) {
-                    return lhs.version() < rhs.version();
-                } else {
-                    return abs(lhs.id()) < abs(rhs.id());
-                }
-            }
-
-            /**
-             * Ordering for shared_ptrs of Relations.
-             */
-            friend bool operator<(const shared_ptr<Relation const>& lhs, const shared_ptr<Relation const>& rhs) {
-                return *lhs < *rhs;
-            }
-
         private:
 
             RelationMemberList m_members;
 
         }; // class Relation
 
+        /**
+         * Relations can be ordered by id and version.
+         * Note that we use the absolute value of the id for a
+         * better ordering of objects with negative ids.
+         */
+        inline bool operator<(const Relation& lhs, const Relation& rhs) {
+            if (lhs.id() == rhs.id()) {
+                return lhs.version() < rhs.version();
+            } else {
+                return abs(lhs.id()) < abs(rhs.id());
+            }
+        }
+
+        /**
+         * Ordering for shared_ptrs of Relations.
+         */
+        inline bool operator<(const shared_ptr<Relation const>& lhs, const shared_ptr<Relation const>& rhs) {
+            return *lhs < *rhs;
+        }
+
     } // namespace OSM
 
 } // namespace Osmium
diff --git a/include/osmium/osm/relation_member.hpp b/include/osmium/osm/relation_member.hpp
index 4614d75..271a619 100644
--- a/include/osmium/osm/relation_member.hpp
+++ b/include/osmium/osm/relation_member.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -24,6 +24,7 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 #include <cstring>
 #include <stdexcept>
+#include <string>
 
 #include <osmium/osm/types.hpp>
 
@@ -35,11 +36,17 @@ namespace Osmium {
 
         public:
 
-            static const int max_characters_role = 255;
+            RelationMember() :
+                m_ref(0),
+                m_type('x'),
+                m_role() {
+            }
+
+            static const unsigned int max_characters_role = 255;
 
-            static const int max_utf16_length_role = 2 * (max_characters_role + 1); ///< maximum number of UTF-16 units
+            static const unsigned int max_utf16_length_role = 2 * (max_characters_role + 1); ///< maximum number of UTF-16 units
 
-            static const int max_length_role = 255 * 4 + 1; /* 255 UTF-8 characters + null byte */
+            static const unsigned int max_length_role = 255 * 4 + 1; /* 255 UTF-8 characters + null byte */
 
             osm_object_id_t ref() const {
                 return m_ref;
@@ -54,7 +61,7 @@ namespace Osmium {
                 return m_type;
             }
 
-            const char *type_name() const {
+            const char* type_name() const {
                 switch (type()) {
                     case 'n':
                         return "node";
@@ -72,53 +79,23 @@ namespace Osmium {
                 return *this;
             }
 
-            const char *role() const {
-                return m_role;
+            const char* role() const {
+                return m_role.c_str();
             }
 
             RelationMember& role(const char* role) {
-                if (! memccpy(m_role, role, 0, max_length_role)) {
+                if (strlen(role) > max_length_role) {
                     throw std::length_error("role too long");
                 }
+                m_role = role;
                 return *this;
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void*)this);
-            }
-
-            v8::Handle<v8::Value> js_ref() const {
-                return v8::Number::New(ref());
-            }
-
-            v8::Handle<v8::Value> js_type() const {
-                char t[2];
-                t[0] = type();
-                t[1] = 0;
-                return v8::String::NewSymbol(t);
-            }
-
-            v8::Handle<v8::Value> js_role() const {
-                return Osmium::utf8_to_v8_String<max_utf16_length_role>(role());
-            }
-
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->SetAccessor(v8::String::NewSymbol("type"), accessor_getter<Osmium::OSM::RelationMember, &Osmium::OSM::RelationMember::js_type>);
-                    js_template->SetAccessor(v8::String::NewSymbol("ref"),  accessor_getter<Osmium::OSM::RelationMember, &Osmium::OSM::RelationMember::js_ref>);
-                    js_template->SetAccessor(v8::String::NewSymbol("role"), accessor_getter<Osmium::OSM::RelationMember, &Osmium::OSM::RelationMember::js_role>);
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         private:
 
             osm_object_id_t m_ref;
             char            m_type;
-            char            m_role[max_length_role];
+            std::string     m_role;
 
         }; // class RelationMember
 
diff --git a/include/osmium/osm/relation_member_list.hpp b/include/osmium/osm/relation_member_list.hpp
index 50c5dfb..df8c2be 100644
--- a/include/osmium/osm/relation_member_list.hpp
+++ b/include/osmium/osm/relation_member_list.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -34,7 +34,8 @@ namespace Osmium {
 
         public:
 
-            RelationMemberList() : m_list() {
+            RelationMemberList() :
+                m_list() {
             }
 
             osm_sequence_id_t size() const {
@@ -72,11 +73,11 @@ namespace Osmium {
                 return m_list.end();
             }
 
-            void add_member(const char type, osm_object_id_t ref, const char *role) {
+            void add_member(const char type, osm_object_id_t ref, const char* role) {
                 /* first we resize the vector... */
                 m_list.resize(m_list.size()+1);
                 /* ...and get an address for the new element... */
-                RelationMember *m = &m_list[m_list.size()-1];
+                RelationMember* m = &m_list[m_list.size()-1];
                 /* ...so that we can directly write into the memory and avoid
                 a second copy */
                 m->type(type);
@@ -84,46 +85,6 @@ namespace Osmium {
                 m->role(role);
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void *)this);
-            }
-
-            v8::Handle<v8::Value> js_get_member(uint32_t index) {
-                return m_list[index].js_instance();
-            }
-
-            v8::Handle<v8::Array> js_enumerate_members() const {
-                v8::HandleScope scope;
-                v8::Local<v8::Array> array = v8::Array::New(m_list.size());
-
-                for (unsigned int i=0; i < m_list.size(); i++) {
-                    array->Set(i, v8::Integer::New(i));
-                }
-
-                return scope.Close(array);
-            }
-
-            v8::Handle<v8::Value> js_length() const {
-                return v8::Number::New(m_list.size());
-            }
-
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->SetAccessor(v8::String::NewSymbol("length"), accessor_getter<RelationMemberList, &RelationMemberList::js_length>);
-                    js_template->SetIndexedPropertyHandler(
-                        indexed_property_getter<RelationMemberList, &RelationMemberList::js_get_member>,
-                        0,
-                        0,
-                        0,
-                        property_enumerator<RelationMemberList, &RelationMemberList::js_enumerate_members>
-                    );
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         private:
 
             std::vector<RelationMember> m_list;
diff --git a/include/osmium/osm/segment.hpp b/include/osmium/osm/segment.hpp
new file mode 100644
index 0000000..26c4dfd
--- /dev/null
+++ b/include/osmium/osm/segment.hpp
@@ -0,0 +1,75 @@
+#ifndef OSMIUM_OSM_SEGMENT_HPP
+#define OSMIUM_OSM_SEGMENT_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <osmium/osm/position.hpp>
+
+namespace Osmium {
+
+    namespace OSM {
+
+        /**
+         * A Segment is the directed connection between two Positions.
+         */
+        class Segment : boost::equality_comparable<Segment> {
+
+        public:
+
+            Segment(const Osmium::OSM::Position& p1, const Osmium::OSM::Position& p2) :
+                m_first(p1),
+                m_second(p2) {
+            }
+
+            /// Return first Position of Segment.
+            const Osmium::OSM::Position first() const {
+                return m_first;
+            }
+
+            /// Return second Position of Segment.
+            const Osmium::OSM::Position second() const {
+                return m_second;
+            }
+
+        protected:
+
+            void swap_positions() {
+                std::swap(m_first, m_second);
+            }
+
+        private:
+
+            Osmium::OSM::Position m_first;
+            Osmium::OSM::Position m_second;
+
+        }; // class Segment
+
+        /// Segments are equal if both their positions are equal
+        inline bool operator==(const Segment& lhs, const Segment& rhs) {
+            return lhs.first() == rhs.first() && lhs.second() == rhs.second();
+        }
+
+    } // namespace OSM
+
+} // namespace Osmium
+
+#endif // OSMIUM_OSM_SEGMENT_HPP
diff --git a/include/osmium/osm/tag.hpp b/include/osmium/osm/tag.hpp
index 561ce1c..049ed53 100644
--- a/include/osmium/osm/tag.hpp
+++ b/include/osmium/osm/tag.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,7 +22,6 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <cstring>
 #include <string>
 
 namespace Osmium {
@@ -42,7 +41,9 @@ namespace Osmium {
             static const int max_utf16_length_key   = 2 * (255 + 1); ///< maximum number of UTF-16 units
             static const int max_utf16_length_value = 2 * (255 + 1);
 
-            Tag(const char* key, const char* value) : m_key(key), m_value(value) {
+            Tag(const char* key, const char* value) :
+                m_key(key),
+                m_value(value) {
             }
 
             const char* key() const {
@@ -53,6 +54,10 @@ namespace Osmium {
                 return m_value.c_str();
             }
 
+            bool operator==(const Tag& other) const {
+                return this->m_key == other.m_key && this->m_value == other.m_value;
+            }
+
         private:
 
             std::string m_key;
@@ -60,6 +65,10 @@ namespace Osmium {
 
         };
 
+        inline bool operator!=(const Tag& lhs, const Tag& rhs) {
+            return !(lhs == rhs);
+        }
+
     } // namespace OSM
 
 } // namespace Osmium
diff --git a/include/osmium/osm/tag_list.hpp b/include/osmium/osm/tag_list.hpp
index 8eeccee..45dd486 100644
--- a/include/osmium/osm/tag_list.hpp
+++ b/include/osmium/osm/tag_list.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,9 +22,9 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <vector>
 #include <cstring>
 #include <stdexcept>
+#include <vector>
 
 #include <osmium/osm/tag.hpp>
 
@@ -42,7 +42,8 @@ namespace Osmium {
 
         public:
 
-            TagList() : m_tags() {
+            TagList() :
+                m_tags() {
             }
 
             /// Return the number of tags in this tag list.
@@ -91,7 +92,7 @@ namespace Osmium {
                 m_tags.push_back(Tag(key, value));
             }
 
-            const char* get_tag_by_key(const char* key) const {
+            const char* get_value_by_key(const char* key) const {
                 for (const_iterator it = begin(); it != end(); ++it) {
                     if (!strcmp(it->key(), key)) {
                         return it->value();
@@ -100,62 +101,6 @@ namespace Osmium {
                 return 0;
             }
 
-            const char* get_tag_key(unsigned int n) const {
-                if (n < m_tags.size()) {
-                    return m_tags[n].key();
-                }
-                throw std::range_error("no tag with this index");
-            }
-
-            const char* get_tag_value(unsigned int n) const {
-                if (n < m_tags.size()) {
-                    return m_tags[n].value();
-                }
-                throw std::range_error("no tag with this index");
-            }
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void*)this);
-            }
-
-            v8::Handle<v8::Value> js_get_tag_value_by_key(v8::Local<v8::String> property) const {
-                const char* key = Osmium::v8_String_to_utf8<Osmium::OSM::Tag::max_utf16_length_key>(property);
-                const char* value = get_tag_by_key(key);
-                if (value) {
-                    return Osmium::utf8_to_v8_String<Osmium::OSM::Tag::max_utf16_length_value>(value);
-                }
-                return v8::Undefined();
-            }
-
-            v8::Handle<v8::Array> js_enumerate_tag_keys() const {
-                v8::HandleScope scope;
-                v8::Local<v8::Array> array = v8::Array::New(m_tags.size());
-
-                const_iterator end = this->end();
-                int i = 0;
-                for (const_iterator it = begin(); it != end; ++it) {
-                    array->Set(i++, Osmium::utf8_to_v8_String<Osmium::OSM::Tag::max_utf16_length_key>(it->key()));
-                }
-
-                return scope.Close(array);
-            }
-
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->SetNamedPropertyHandler(
-                        named_property_getter<TagList, &TagList::js_get_tag_value_by_key>,
-                        0,
-                        0,
-                        0,
-                        property_enumerator<TagList, &TagList::js_enumerate_tag_keys>
-                    );
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         private:
 
             std::vector<Tag> m_tags;
diff --git a/include/osmium/exceptions.hpp b/include/osmium/osm/tag_ostream.hpp
similarity index 62%
rename from include/osmium/exceptions.hpp
rename to include/osmium/osm/tag_ostream.hpp
index 35619bb..110bf07 100644
--- a/include/osmium/exceptions.hpp
+++ b/include/osmium/osm/tag_ostream.hpp
@@ -1,9 +1,9 @@
-#ifndef OSMIUM_EXCEPTIONS_HPP
-#define OSMIUM_EXCEPTIONS_HPP
+#ifndef OSMIUM_OSM_TAG_OSTREAM_HPP
+#define OSMIUM_OSM_TAG_OSTREAM_HPP
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,24 +22,21 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <stdexcept>
+#include <ostream>
+
+#include <osmium/osm/tag.hpp>
 
 namespace Osmium {
 
-    /**
-     * @brief Exceptions used in different parts of %Osmium.
-     */
-    namespace Exception {
+    namespace OSM {
 
-        /**
-         * This exception is thrown when OSM data can't be assembled
-         * into proper geometries.
-         */
-        class IllegalGeometry {
-        };
+        inline std::ostream& operator<<(std::ostream& out, const Tag& tag) {
+            out << tag.key() << '=' << tag.value();
+            return out;
+        }
 
-    } // namespace Exception
+    } // namespace OSM
 
 } // namespace Osmium
 
-#endif // OSMIUM_EXCEPTIONS_HPP
+#endif // OSMIUM_OSM_TAG_OSTREAM_HPP
diff --git a/include/osmium/osm/types.hpp b/include/osmium/osm/types.hpp
index d281eb3..8ce1c60 100644
--- a/include/osmium/osm/types.hpp
+++ b/include/osmium/osm/types.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -23,25 +23,52 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 */
 
 #include <stdint.h>
+#include <stdlib.h>
+
+// These type definitions should have been in the Osmium namespace, but it is a
+// bit late to change this now...
 
 enum osm_object_type_t {
     UNKNOWN            = -1,
     NODE               = 0,
     WAY                = 1,
     RELATION           = 2,
-    AREA_FROM_WAY      = 3,
-    AREA_FROM_RELATION = 4
+    AREA               = 3
 };
 
 /*
 * The following typedefs are chosen so that they can represent all needed
 * numbers and still be reasonably space efficient. As the %OSM database is
-* growing rapidly, 64 bit IDs will be needed at some point!
+* growing rapidly, 64 bit IDs are used.
 */
-typedef int32_t  osm_object_id_t;    ///< type for %OSM object (node, way, or relation) IDs
+typedef int64_t  osm_object_id_t;    ///< type for %OSM object (node, way, or relation) IDs
 typedef uint32_t osm_version_t;      ///< type for %OSM object version number
 typedef int32_t  osm_changeset_id_t; ///< type for %OSM changeset IDs
 typedef int32_t  osm_user_id_t;      ///< type for %OSM user IDs
 typedef uint32_t osm_sequence_id_t;  ///< type for %OSM nodes and members sequence IDs
 
+#ifdef _MSC_VER
+# define atoll(x) (_atoi64(x))
+#endif
+
+namespace Osmium {
+
+    inline osm_object_id_t string_to_osm_object_id_t(const char* string) {
+        return atoll(string);
+    }
+
+    inline osm_version_t string_to_osm_version_t(const char* string) {
+        return atol(string);
+    }
+
+    inline osm_changeset_id_t string_to_osm_changeset_id_t(const char* string) {
+        return atol(string);
+    }
+
+    inline osm_user_id_t string_to_osm_user_id_t(const char* string) {
+        return atol(string);
+    }
+
+} // namespace Osmium
+
 #endif // OSMIUM_OSM_TYPES_HPP
diff --git a/include/osmium/osm/undirected_segment.hpp b/include/osmium/osm/undirected_segment.hpp
new file mode 100644
index 0000000..8c1310b
--- /dev/null
+++ b/include/osmium/osm/undirected_segment.hpp
@@ -0,0 +1,66 @@
+#ifndef OSMIUM_OSM_UNDIRECTED_SEGMENT_HPP
+#define OSMIUM_OSM_UNDIRECTED_SEGMENT_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <osmium/osm/segment.hpp>
+
+namespace Osmium {
+
+    namespace OSM {
+
+        /**
+         * Undirected connection between two Positions. The first Position is
+         * always equal or "smaller" than the second Position, ie to the left
+         * and down.
+         */
+        class UndirectedSegment : boost::less_than_comparable<UndirectedSegment>, public Segment {
+
+        public:
+
+            UndirectedSegment(const Osmium::OSM::Position& p1, const Osmium::OSM::Position& p2) :
+                Segment(p1, p2) {
+                if (p2 < p1) {
+                    swap_positions();
+                }
+            }
+
+        }; // class UndirectedSegment
+
+        /**
+        * UndirectedSegments are "smaller" if they are to the left and down of another
+        * segment. The first() position is checked first() and only if they have the
+        * same first() position the second() position is taken into account.
+        */
+        inline bool operator<(const UndirectedSegment& lhs, const UndirectedSegment& rhs) {
+            if (lhs.first() == rhs.first()) {
+                return lhs.second() < rhs.second();
+            } else {
+                return lhs.first() < rhs.first();
+            }
+        }
+
+    } // namespace OSM
+
+} // namespace Osmium
+
+#endif // OSMIUM_OSM_UNDIRECTED_SEGMENT_HPP
diff --git a/include/osmium/osm/way.hpp b/include/osmium/osm/way.hpp
index 26efb68..1e8e6a5 100644
--- a/include/osmium/osm/way.hpp
+++ b/include/osmium/osm/way.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,25 +22,14 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <stdexcept>
-#include <iostream>
-#include <boost/operators.hpp>
-
-#include <osmium/osm/object.hpp>
-#include <osmium/osm/way_node_list.hpp>
-
 /** @file
 *   @brief Contains the Osmium::OSM::Way class.
 */
 
-#ifdef OSMIUM_WITH_GEOS
-# include <geos/geom/Coordinate.h>
-# include <geos/geom/CoordinateSequenceFactory.h>
-# include <geos/geom/Geometry.h>
-# include <geos/geom/Point.h>
-# include <geos/util/GEOSException.h>
-#endif // OSMIUM_WITH_GEOS
+#include <boost/operators.hpp>
 
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/way_node_list.hpp>
 #include <osmium/geometry.hpp>
 
 namespace Osmium {
@@ -53,19 +42,19 @@ namespace Osmium {
 
         public:
 
-            /// Construct a Way object.
-            Way() : Object(), m_node_list() {
-                init();
+            Way() :
+                Object(),
+                m_node_list() {
             }
 
-            Way(int size_of_node_list) : Object(), m_node_list(size_of_node_list) {
-                init();
+            Way(int size_of_node_list) :
+                Object(),
+                m_node_list(size_of_node_list) {
             }
 
-            /// Copy a Way object.
-            Way(const Way& w) : Object(w) {
-                init();
-                m_node_list = w.m_node_list;
+            Way(const Way& way) :
+                Object(way),
+                m_node_list(way.m_node_list) {
             }
 
             const WayNodeList& nodes() const {
@@ -76,18 +65,7 @@ namespace Osmium {
                 return m_node_list;
             }
 
-        private:
-
-            void init() {
-#ifdef OSMIUM_WITH_JAVASCRIPT
-                v8::HandleScope handle_scope;
-                js_object_instance = v8::Persistent<v8::Object>::New(JavascriptTemplate::get<JavascriptTemplate>().create_instance(this));
-#endif // OSMIUM_WITH_JAVASCRIPT
-            }
-
-        public:
-
-            osm_object_type_t get_type() const {
+            osm_object_type_t type() const {
                 return WAY;
             }
 
@@ -95,14 +73,6 @@ namespace Osmium {
                 return m_node_list[n].ref();
             }
 
-            double get_lon(osm_sequence_id_t n) const {
-                return m_node_list[n].position().lon();
-            }
-
-            double get_lat(osm_sequence_id_t n) const {
-                return m_node_list[n].position().lat();
-            }
-
             /**
             * Add a node with the given id to the way.
             *
@@ -113,13 +83,6 @@ namespace Osmium {
             }
 
             /**
-            * Returns the number of nodes in this way.
-            */
-            osm_sequence_id_t node_count() const {
-                return m_node_list.size();
-            }
-
-            /**
              * Returns the id of the first node.
              */
             osm_object_id_t get_first_node_id() const {
@@ -140,93 +103,28 @@ namespace Osmium {
                 return m_node_list.is_closed();
             }
 
-#ifdef OSMIUM_WITH_GEOS
-            /**
-             * Returns the GEOS geometry of the first node.
-             * Caller takes ownership of the pointer.
-             */
-            geos::geom::Point* get_first_node_geometry() const {
-                if (!m_node_list.front().has_position()) {
-                    throw std::range_error("geometry for nodes not available");
-                }
-                return Osmium::Geometry::geos_geometry_factory()->createPoint(m_node_list.front().position());
-            }
-
-            /**
-             * Returns the GEOS geometry of the last node.
-             * Caller takes ownership of the pointer.
-             */
-            geos::geom::Point* get_last_node_geometry() const {
-                if (!m_node_list.back().has_position()) {
-                    throw std::range_error("geometry for nodes not available");
-                }
-                return Osmium::Geometry::geos_geometry_factory()->createPoint(m_node_list.back().position());
-            }
-
-            /**
-             * Returns the GEOS geometry of the way.
-             * Caller takes ownership of the pointer.
-             */
-            geos::geom::Geometry* create_geos_geometry() const {
-                try {
-                    std::vector<geos::geom::Coordinate>* c = new std::vector<geos::geom::Coordinate>;
-                    for (osm_sequence_id_t i=0; i < m_node_list.size(); ++i) {
-                        c->push_back(m_node_list[i].position());
-                    }
-                    geos::geom::CoordinateSequence* cs = Osmium::Geometry::geos_geometry_factory()->getCoordinateSequenceFactory()->create(c);
-                    return (geos::geom::Geometry*) Osmium::Geometry::geos_geometry_factory()->createLineString(cs);
-                } catch (const geos::util::GEOSException& exc) {
-                    std::cerr << "error building way geometry, leave it as NULL\n";
-                    return NULL;
-                }
-            }
-#endif // OSMIUM_WITH_GEOS
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Handle<v8::Value> js_nodes() const {
-                return m_node_list.js_instance();
-            }
-
-            v8::Handle<v8::Value> js_geom() const;
-
-            v8::Handle<v8::Value> js_reverse_geom() const;
-
-            v8::Handle<v8::Value> js_polygon_geom() const;
-
-            struct JavascriptTemplate : public Osmium::OSM::Object::JavascriptTemplate {
-
-                JavascriptTemplate() : Osmium::OSM::Object::JavascriptTemplate() {
-                    js_template->SetAccessor(v8::String::NewSymbol("nodes"),        accessor_getter<Way, &Way::js_nodes>);
-                    js_template->SetAccessor(v8::String::NewSymbol("geom"),         accessor_getter<Way, &Way::js_geom>);
-                    js_template->SetAccessor(v8::String::NewSymbol("reverse_geom"), accessor_getter<Way, &Way::js_reverse_geom>);
-                    js_template->SetAccessor(v8::String::NewSymbol("polygon_geom"), accessor_getter<Way, &Way::js_polygon_geom>);
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
-            /**
-             * Ways can be ordered by id and version.
-             * Note that we use the absolute value of the id for a
-             * better ordering of objects with negative ids.
-             */
-            friend bool operator<(const Way& lhs, const Way& rhs) {
-                if (lhs.id() == rhs.id()) {
-                    return lhs.version() < rhs.version();
-                } else {
-                    return abs(lhs.id()) < abs(rhs.id());
-                }
-            }
-
-            /**
-             * Ordering for shared_ptrs of Ways.
-             */
-            friend bool operator<(const shared_ptr<Way const>& lhs, const shared_ptr<Way const>& rhs) {
-                return *lhs < *rhs;
-            }
-
         }; // class Way
 
+        /**
+         * Ways can be ordered by id and version.
+         * Note that we use the absolute value of the id for a
+         * better ordering of objects with negative ids.
+         */
+        inline bool operator<(const Way& lhs, const Way& rhs) {
+            if (lhs.id() == rhs.id()) {
+                return lhs.version() < rhs.version();
+            } else {
+                return abs(lhs.id()) < abs(rhs.id());
+            }
+        }
+
+        /**
+         * Ordering for shared_ptrs of Ways.
+         */
+        inline bool operator<(const shared_ptr<Way const>& lhs, const shared_ptr<Way const>& rhs) {
+            return *lhs < *rhs;
+        }
+
     } // namespace OSM
 
 } // namespace Osmium
diff --git a/include/osmium/osm/way_node.hpp b/include/osmium/osm/way_node.hpp
index 5159bb7..6b2ef7e 100644
--- a/include/osmium/osm/way_node.hpp
+++ b/include/osmium/osm/way_node.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -33,20 +33,28 @@ namespace Osmium {
 
         public:
 
-            WayNode(osm_object_id_t ref=0) : m_ref(ref) {
-            }
-
-            WayNode(osm_object_id_t ref, const Position& position) : m_ref(ref), m_position(position) {
+            WayNode(osm_object_id_t ref=0, const Position& position=Position()) :
+                m_ref(ref),
+                m_position(position) {
             }
 
             osm_object_id_t ref() const {
                 return m_ref;
             }
 
+            WayNode& ref(osm_object_id_t ref) {
+                m_ref = ref;
+                return *this;
+            }
+
             const Position& position() const {
                 return m_position;
             }
 
+            Position& position() {
+                return m_position;
+            }
+
             WayNode& position(const Position& position) {
                 m_position = position;
                 return *this;
@@ -64,14 +72,6 @@ namespace Osmium {
                 return m_position.lat();
             }
 
-            friend bool operator==(const WayNode& wn1, const WayNode& wn2) {
-                return wn1.ref() == wn2.ref();
-            }
-
-            friend bool operator!=(const WayNode& wn1, const WayNode& wn2) {
-                return !(wn1 == wn2);
-            }
-
         private:
 
             osm_object_id_t m_ref;
@@ -79,6 +79,18 @@ namespace Osmium {
 
         }; // class WayNode
 
+        inline bool operator<(const WayNode& wn1, const WayNode& wn2) {
+            return wn1.ref() < wn2.ref();
+        }
+
+        inline bool operator==(const WayNode& wn1, const WayNode& wn2) {
+            return wn1.ref() == wn2.ref();
+        }
+
+        inline bool operator!=(const WayNode& wn1, const WayNode& wn2) {
+            return !(wn1 == wn2);
+        }
+
     } // namespace OSM
 
 } // namespace Osmium
diff --git a/include/osmium/osm/way_node_list.hpp b/include/osmium/osm/way_node_list.hpp
index bf11228..3d79e74 100644
--- a/include/osmium/osm/way_node_list.hpp
+++ b/include/osmium/osm/way_node_list.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -41,7 +41,8 @@ namespace Osmium {
              */
             static const int default_size = 500;
 
-            WayNodeList(unsigned int size=default_size) : m_list() {
+            WayNodeList(unsigned int size=default_size) :
+                m_list() {
                 m_list.reserve(size);
             }
 
@@ -49,6 +50,10 @@ namespace Osmium {
                 return m_list.size();
             }
 
+            bool empty() const {
+                return m_list.empty();
+            }
+
             void clear() {
                 m_list.clear();
             }
@@ -90,6 +95,11 @@ namespace Osmium {
                 return m_list.rend();
             }
 
+            template <class TInputIterator>
+            void insert(iterator position, TInputIterator first, TInputIterator last) {
+                m_list.insert(position, first, last);
+            }
+
             WayNode& operator[](int i) {
                 return m_list[i];
             }
@@ -102,10 +112,18 @@ namespace Osmium {
                 return m_list.front();
             }
 
+            WayNode& front() {
+                return m_list.front();
+            }
+
             const WayNode& back() const {
                 return m_list.back();
             }
 
+            WayNode& back() {
+                return m_list.back();
+            }
+
             bool is_closed() const {
                 return m_list.front().ref() == m_list.back().ref();
             }
@@ -118,64 +136,30 @@ namespace Osmium {
                 }
             }
 
-            WayNodeList& add(const WayNode& way_node) {
+            WayNodeList& push_back(const WayNode& way_node) {
                 m_list.push_back(way_node);
                 return *this;
             }
 
-            WayNodeList& add(osm_object_id_t ref) {
-                m_list.push_back(WayNode(ref));
+            WayNodeList& add(const WayNode& way_node) {
+                m_list.push_back(way_node);
                 return *this;
             }
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> js_instance() const {
-                return JavascriptTemplate::get<JavascriptTemplate>().create_instance((void *)this);
-            }
-
-            v8::Handle<v8::Value> js_length() const {
-                return v8::Number::New(m_list.size());
-            }
-
-            v8::Handle<v8::Value> js_get_node_id(uint32_t index) const {
-                return v8::Number::New(m_list[index].ref());
+            WayNodeList& push_back(osm_object_id_t ref) {
+                m_list.push_back(WayNode(ref));
+                return *this;
             }
 
-            v8::Handle<v8::Array> js_enumerate_nodes() const {
-                v8::HandleScope scope;
-                v8::Local<v8::Array> array = v8::Array::New(m_list.size());
-
-                for (unsigned int i=0; i < m_list.size(); ++i) {
-                    array->Set(i, v8::Integer::New(i));
-                }
-
-                return scope.Close(array);
+            WayNodeList& add(osm_object_id_t ref) {
+                m_list.push_back(WayNode(ref));
+                return *this;
             }
 
-            struct JavascriptTemplate : public Osmium::Javascript::Template {
-
-                JavascriptTemplate() : Osmium::Javascript::Template() {
-                    js_template->SetAccessor(v8::String::NewSymbol("length"), accessor_getter<WayNodeList, &WayNodeList::js_length>);
-                    js_template->SetIndexedPropertyHandler(
-                        indexed_property_getter<WayNodeList, &WayNodeList::js_get_node_id>,
-                        0,
-                        0,
-                        0,
-                        property_enumerator<WayNodeList, &WayNodeList::js_enumerate_nodes>
-                    );
-                }
-
-            };
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         private:
 
             std::vector<WayNode> m_list;
 
-#ifdef OSMIUM_WITH_JAVASCRIPT
-            v8::Local<v8::Object> m_js_instance;
-#endif // OSMIUM_WITH_JAVASCRIPT
-
         }; // class WayNodeList
 
     } // namespace OSM
diff --git a/include/osmium/osmfile.hpp b/include/osmium/osmfile.hpp
index 6fa7733..fb7f7fb 100644
--- a/include/osmium/osmfile.hpp
+++ b/include/osmium/osmfile.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,21 +22,17 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
+#include <cerrno>
 #include <fcntl.h>
-#include <errno.h>
 #include <stdexcept>
+#include <string>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
 #include <boost/utility.hpp>
 
 namespace Osmium {
 
-    // forward declaration
-    namespace Output {
-        class Base;
-    }
-
     /**
      * This class describes an %OSM file in one of several different formats.
      * It can be used as factory class for generating input and output OSM files.
@@ -59,17 +55,16 @@ namespace Osmium {
 
         public:
 
-            SystemError(const std::string& whatarg,
-                        int e)
-                : std::runtime_error(whatarg),
-                  m_errno(e) {
+            SystemError(const std::string& whatarg, int e) :
+                std::runtime_error(whatarg),
+                m_errno(e) {
             }
 
             /**
              * Get the system errno variable from the system call that caused
              * this exception.
              */
-            int system_errno() const throw() {
+            int system_errno() const {
                 return m_errno;
             }
 
@@ -88,10 +83,10 @@ namespace Osmium {
 
             IOError(const std::string& whatarg,
                     const std::string& filename,
-                    int e)
-                : std::runtime_error(whatarg),
-                  m_filename(filename),
-                  m_errno(e) {
+                    int e) :
+                std::runtime_error(whatarg),
+                m_filename(filename),
+                m_errno(e) {
             }
 
             ~IOError() throw() {
@@ -100,7 +95,7 @@ namespace Osmium {
             /**
              * Get the filename that caused this exception.
              */
-            const std::string& filename() const throw() {
+            const std::string& filename() const {
                 return m_filename;
             }
 
@@ -108,7 +103,7 @@ namespace Osmium {
              * Get the system errno variable from the system call that caused
              * this exception.
              */
-            int system_errno() const throw() {
+            int system_errno() const {
                 return m_errno;
             }
 
@@ -121,15 +116,15 @@ namespace Osmium {
         public:
 
             ArgumentError(const std::string& whatarg,
-                          const std::string& value="")
-                : std::runtime_error(whatarg),
-                  m_value(value) {
+                          const std::string& value="") :
+                std::runtime_error(whatarg),
+                m_value(value) {
             }
 
             ~ArgumentError() throw() {
             }
 
-            const std::string& value() const throw() {
+            const std::string& value() const {
                 return m_value;
             }
 
@@ -156,6 +151,9 @@ namespace Osmium {
         struct FileTypeHistoryExpected : public FileTypeError {
         };
 
+        class FileEncodingNotSupported {
+        };
+
         /**
          * Instances of this class describe different file types.
          *
@@ -167,7 +165,9 @@ namespace Osmium {
             std::string m_suffix;
             bool m_has_multiple_object_versions;
 
-            FileType(std::string suffix, bool has_multiple_object_versions) : m_suffix(suffix), m_has_multiple_object_versions(has_multiple_object_versions) {
+            FileType(std::string suffix, bool has_multiple_object_versions) :
+                m_suffix(suffix),
+                m_has_multiple_object_versions(has_multiple_object_versions) {
             }
 
         public:
@@ -215,25 +215,29 @@ namespace Osmium {
          */
         class FileEncoding : boost::noncopyable {
 
-            std::string m_suffix;
-            std::string m_compress;
-            std::string m_decompress;
-            bool m_pbf;
+            const std::string m_suffix;
+            const std::string m_compress;
+            const std::string m_decompress;
+            const bool m_pbf;
 
-            FileEncoding(std::string suffix, std::string compress, std::string decompress, bool pbf) : m_suffix(suffix), m_compress(compress), m_decompress(decompress), m_pbf(pbf) {
+            FileEncoding(const std::string& suffix, const std::string& compress, const std::string& decompress, bool pbf) :
+                m_suffix(suffix),
+                m_compress(compress),
+                m_decompress(decompress),
+                m_pbf(pbf) {
             }
 
         public:
 
-            std::string suffix() const {
+            const std::string& suffix() const {
                 return m_suffix;
             }
 
-            std::string compress() const {
+            const std::string& compress() const {
                 return m_compress;
             }
 
-            std::string decompress() const {
+            const std::string& decompress() const {
                 return m_decompress;
             }
 
@@ -261,7 +265,7 @@ namespace Osmium {
              * XML encoding, compressed with gzip.
              */
             static FileEncoding* XMLgz() {
-                static FileEncoding instance(".gz", "gzip", "gzcat", false);
+                static FileEncoding instance(".gz", "gzip", "zcat", false);
                 return &instance;
             }
 
@@ -307,7 +311,7 @@ namespace Osmium {
          * @return File descriptor of pipe in the parent.
          * @throws SystemError if a system call fails.
          */
-        int execute(std::string command, int input) {
+        int execute(const std::string& command, int input) {
             int pipefd[2];
             if (pipe(pipefd) < 0) {
                 throw SystemError("Can't create pipe", errno);
@@ -328,17 +332,17 @@ namespace Osmium {
                 }
 
                 if (input == 0) {
-                    open("/dev/null", O_RDONLY); // stdin
-                    open("/dev/null", O_WRONLY); // stderr
-                    if (execlp(command.c_str(), command.c_str(), m_filename.c_str(), NULL) < 0) {
+                    ::open("/dev/null", O_RDONLY); // stdin
+                    ::open("/dev/null", O_WRONLY); // stderr
+                    if (::execlp(command.c_str(), command.c_str(), m_filename.c_str(), NULL) < 0) {
                         exit(1);
                     }
                 } else {
-                    if (open(m_filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666) != 1) {
+                    if (::open(m_filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666) != 1) {
                         exit(1);
                     }
-                    open("/dev/null", O_WRONLY); // stderr
-                    if (execlp(command.c_str(), command.c_str(), 0, NULL) < 0) {
+                    ::open("/dev/null", O_WRONLY); // stderr
+                    if (::execlp(command.c_str(), command.c_str(), 0, NULL) < 0) {
                         exit(1);
                     }
                 }
@@ -359,7 +363,11 @@ namespace Osmium {
             if (m_filename == "") {
                 return 0; // stdin
             } else {
-                int fd = open(m_filename.c_str(), O_RDONLY);
+                int flags = O_RDONLY;
+#ifdef WIN32
+                flags |= O_BINARY;
+#endif
+                int fd = ::open(m_filename.c_str(), flags);
                 if (fd < 0) {
                     throw IOError("Open failed", m_filename, errno);
                 }
@@ -378,7 +386,11 @@ namespace Osmium {
             if (m_filename == "") {
                 return 1; // stdout
             } else {
-                int fd = open(m_filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
+                int flags = O_WRONLY | O_TRUNC | O_CREAT;
+#ifdef WIN32
+                flags |= O_BINARY;
+#endif
+                int fd = ::open(m_filename.c_str(), flags, 0666);
                 if (fd < 0) {
                     throw IOError("Open failed", m_filename, errno);
                 }
@@ -414,12 +426,12 @@ namespace Osmium {
          *                 of the file will be taken from the suffix.
          *                 An empty filename or "-" means stdin or stdout.
          */
-        OSMFile(const std::string& filename = "")
-            : m_type(FileType::OSM()),
-              m_encoding(FileEncoding::PBF()),
-              m_filename(filename),
-              m_fd(-1),
-              m_childpid(0) {
+        OSMFile(const std::string& filename = "") :
+            m_type(FileType::OSM()),
+            m_encoding(FileEncoding::PBF()),
+            m_filename(filename),
+            m_fd(-1),
+            m_childpid(0) {
 
             // stdin/stdout
             if (filename == "" || filename == "-") {
@@ -491,12 +503,12 @@ namespace Osmium {
          * Only attributes not related to the open file will be
          * copied.
          */
-        OSMFile(const OSMFile& orig) {
-            m_fd       = -1;
-            m_childpid = 0;
-            m_type     = orig.get_type();
-            m_encoding = orig.get_encoding();
-            m_filename = orig.get_filename();
+        OSMFile(const OSMFile& orig) :
+            m_type(orig.type()),
+            m_encoding(orig.encoding()),
+            m_filename(orig.filename()),
+            m_fd(-1),
+            m_childpid(0) {
         }
 
         /**
@@ -507,9 +519,9 @@ namespace Osmium {
         OSMFile& operator=(const OSMFile& orig) {
             m_fd       = -1;
             m_childpid = 0;
-            m_type     = orig.get_type();
-            m_encoding = orig.get_encoding();
-            m_filename = orig.get_filename();
+            m_type     = orig.type();
+            m_encoding = orig.encoding();
+            m_filename = orig.filename();
             return *this;
         }
 
@@ -531,7 +543,9 @@ namespace Osmium {
                 int status;
                 pid_t pid = waitpid(m_childpid, &status, 0);
                 if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-                    throw IOError("Subprocess returned error", "", errno);
+                    // global variable errno doesn't contain a valid value,
+                    // so we better set errno=0
+                    throw IOError("Subprocess returned error", m_filename, 0);
                 }
                 m_childpid = 0;
             }
@@ -567,20 +581,20 @@ namespace Osmium {
             m_encoding = FileEncoding::XML();
         }
 
-        int get_fd() const {
+        int fd() const {
             return m_fd;
         }
 
-        FileType* get_type() const {
+        FileType* type() const {
             return m_type;
         }
 
-        OSMFile& set_type(FileType* type) {
+        OSMFile& type(FileType* type) {
             m_type = type;
             return *this;
         }
 
-        OSMFile& set_type(std::string& type) {
+        OSMFile& type(const std::string& type) {
             if (type == "osm") {
                 m_type = FileType::OSM();
             } else if (type == "history" || type == "osh") {
@@ -597,16 +611,16 @@ namespace Osmium {
             return m_type->has_multiple_object_versions();
         }
 
-        FileEncoding* get_encoding() const {
+        FileEncoding* encoding() const {
             return m_encoding;
         }
 
-        OSMFile& set_encoding(FileEncoding* encoding) {
+        OSMFile& encoding(FileEncoding* encoding) {
             m_encoding = encoding;
             return *this;
         }
 
-        OSMFile& set_encoding(std::string& encoding) {
+        OSMFile& encoding(const std::string& encoding) {
             if (encoding == "pbf") {
                 m_encoding = FileEncoding::PBF();
             } else if (encoding == "xml") {
@@ -621,7 +635,7 @@ namespace Osmium {
             return *this;
         }
 
-        OSMFile& set_filename(std::string& filename) {
+        OSMFile& filename(const std::string& filename) {
             if (filename == "-") {
                 m_filename = "";
             } else {
@@ -630,16 +644,16 @@ namespace Osmium {
             return *this;
         }
 
-        std::string get_filename() const {
+        const std::string& filename() const {
             return m_filename;
         }
 
-        std::string get_filename_without_suffix() const {
-            return m_filename.substr(m_filename.find_first_of('.')+1);
+        std::string filename_without_suffix() const {
+            return m_filename.substr(0, m_filename.find_first_of('.'));
         }
 
-        std::string get_filename_with_default_suffix() const {
-            std::string filename = get_filename_without_suffix();
+        std::string filename_with_default_suffix() const {
+            std::string filename = filename_without_suffix();
             filename += m_type->suffix() + m_encoding->suffix();
             return filename;
         }
@@ -652,17 +666,7 @@ namespace Osmium {
             m_fd = m_encoding->compress() == "" ? open_output_file() : execute(m_encoding->compress(), 1);
         }
 
-        /**
-         * Read OSM file and call methods on handler object.
-         */
-        template <class T> void read(T& handler);
-
-        /**
-         * Create output file from OSMFile.
-         */
-        Osmium::Output::Base* create_output_file();
-
-    };
+    }; // class OSMFile
 
 } // namespace Osmium
 
diff --git a/include/osmium/osmfile_impl.hpp b/include/osmium/osmfile_impl.hpp
deleted file mode 100644
index b6b3cf1..0000000
--- a/include/osmium/osmfile_impl.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef OSMIUM_OSMFILE_IMPL_HPP
-#define OSMIUM_OSMFILE_IMPL_HPP
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#include <osmium/osmfile.hpp>
-#include <osmium/output.hpp>
-
-namespace Osmium {
-
-    template <class T>
-    void OSMFile::read(T& handler) {
-        Osmium::Input::Base<T>* input = m_encoding->is_pbf()
-                                        ? static_cast<Osmium::Input::Base<T>*>(new Osmium::Input::PBF<T>(*this, handler))
-                                        : static_cast<Osmium::Input::Base<T>*>(new Osmium::Input::XML<T>(*this, handler));
-        input->parse();
-        delete input;
-    }
-
-    Osmium::Output::Base *OSMFile::create_output_file() {
-        Osmium::Output::Base *output = NULL;
-
-        if (m_encoding->is_pbf()) {
-            output = new Osmium::Output::PBF(*this);
-        } else {
-#ifdef OSMIUM_WITH_OUTPUT_OSM_XML
-            output = new Osmium::Output::XML(*this);
-#endif
-        }
-
-        return output;
-    }
-
-} // namespace Osmium
-
-#endif // OSMIUM_OSMFILE_IMPL_HPP
diff --git a/include/osmium/output.hpp b/include/osmium/output.hpp
index 7b908eb..32721f5 100644
--- a/include/osmium/output.hpp
+++ b/include/osmium/output.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,6 +22,8 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#include <map>
+
 #include <osmium/osmfile.hpp>
 #include <osmium/handler.hpp>
 
@@ -37,14 +39,18 @@ namespace Osmium {
         protected:
 
             Osmium::OSMFile m_file;
+            std::string m_generator;
 
-            int get_fd() {
-                return m_file.get_fd();
+            int fd() {
+                return m_file.fd();
             }
 
         public:
 
-            Base(Osmium::OSMFile& file) : m_file(file) {
+            Base(const Osmium::OSMFile& file) :
+                Osmium::Handler::Base(),
+                m_file(file),
+                m_generator("Osmium (http://wiki.openstreetmap.org/wiki/Osmium)") {
                 m_file.open_for_output();
             }
 
@@ -57,15 +63,83 @@ namespace Osmium {
             virtual void relation(const shared_ptr<Osmium::OSM::Relation const>&) = 0;
             virtual void final() = 0;
 
+            void set_generator(const std::string& generator) {
+                m_generator = generator;
+            }
+
         }; // class Base
 
+        /**
+         * This factory class is used to register file output formats and open
+         * output files in these formats. You should not use this class directly.
+         * Instead use the Osmium::Output::open() function.
+         */
+        class Factory {
+
+        public:
+
+            typedef Osmium::Output::Base*(*create_output_t)(const Osmium::OSMFile&);
+
+        private:
+
+            typedef std::map<Osmium::OSMFile::FileEncoding*, create_output_t> encoding2create_t;
+
+            Factory() :
+                m_callbacks() {}
+
+        public:
+
+            static Factory& instance() {
+                static Factory factory;
+                return factory;
+            }
+
+            bool register_output_format(Osmium::OSMFile::FileEncoding* encoding, create_output_t create_function) {
+                return m_callbacks.insert(encoding2create_t::value_type(encoding, create_function)).second;
+            }
+
+            bool unregister_output_format(Osmium::OSMFile::FileEncoding* encoding) {
+                return m_callbacks.erase(encoding) == 1;
+            }
+
+            Osmium::Output::Base* create_output(const Osmium::OSMFile& file) {
+                encoding2create_t::iterator it = m_callbacks.find(file.encoding());
+
+                if (it != m_callbacks.end()) {
+                    return (it->second)(file);
+                }
+
+                throw Osmium::OSMFile::FileEncodingNotSupported();
+            }
+
+        private:
+
+            encoding2create_t m_callbacks;
+
+        }; // class Factory
+
+        /**
+         */
+        class Handler : public Osmium::Handler::Forward<Osmium::Output::Base> {
+
+        public:
+
+            Handler(const Osmium::OSMFile& file) :
+                Osmium::Handler::Forward<Osmium::Output::Base>(*Osmium::Output::Factory::instance().create_output(file)) {
+            }
+
+            ~Handler() {
+                delete &next_handler();
+            }
+
+            void set_generator(const std::string& generator) {
+                next_handler().set_generator(generator);
+            }
+
+        }; // Handler
+
     } // namespace Output
 
 } // namespace Osmium
 
-#include <osmium/output/pbf.hpp>
-#ifdef OSMIUM_WITH_OUTPUT_OSM_XML
-# include <osmium/output/xml.hpp>
-#endif
-
 #endif // OSMIUM_OUTPUT_HPP
diff --git a/include/osmium/output/pbf.hpp b/include/osmium/output/pbf.hpp
index 21b5f8a..9162f24 100644
--- a/include/osmium/output/pbf.hpp
+++ b/include/osmium/output/pbf.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,6 +22,8 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#define OSMIUM_LINK_WITH_LIBS_PBF -lz -lpthread -lprotobuf-lite -losmpbf
+
 /*
 
 About the .osm.pbf file format
@@ -84,23 +86,20 @@ More complete outlines of real .osm.pbf files can be created using the osmpbf-ou
  <https://github.com/MaZderMind/OSM-binary/tree/osmpbf-outline>
 */
 
-// netinet provides the network-byte-order conversion function
-#include <netinet/in.h>
-
-// the algorithm-lib contains the sort functions
 #include <algorithm>
-
-// math is used for round() in lonlat2int
-#include <math.h>
-
+#include <cmath>
+#include <osmpbf/osmpbf.h>
 #include <zlib.h>
 
-#include <osmpbf/osmpbf.h>
+#ifndef WIN32
+# include <netinet/in.h>
+#else
+# include <winsock2.h>
+#endif
 
-// StringTable management
 #include <osmium/utils/stringtable.hpp>
-
 #include <osmium/utils/delta.hpp>
+#include <osmium/output.hpp>
 
 namespace Osmium {
 
@@ -159,16 +158,16 @@ namespace Osmium {
             OSMPBF::PrimitiveGroup* pbf_relations;
 
             /**
-             * to flexibly handle multiple resolutions, the granularity, or
+             * To flexibly handle multiple resolutions, the granularity, or
              * resolution used for representing locations is adjustable in
              * multiples of 1 nanodegree. The default scaling factor is 100
              * nanodegrees, corresponding to about ~1cm at the equator.
-             * These is the current resolution of the OSM database.
+             * This is the current resolution of the OSM database.
              */
             int m_location_granularity;
 
             /**
-             * the granularity used for representing timestamps is also adjustable in
+             * The granularity used for representing timestamps is also adjustable in
              * multiples of 1 millisecond. The default scaling factor is 1000
              * milliseconds, which is the current resolution of the OSM database.
              */
@@ -245,18 +244,18 @@ namespace Osmium {
              * @param in String input.
              * @return Number of bytes after compression.
              */
-            size_t zlib_compress(std::string &in) {
+            size_t zlib_compress(const std::string& in) {
                 // zlib compression context
                 z_stream z;
 
                 // next byte to compress
-                z.next_in   = (uint8_t*) in.c_str();
+                z.next_in   = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(in.c_str()));
 
                 // number of bytes to compress
                 z.avail_in  = in.size();
 
                 // place to store compressed bytes
-                z.next_out  = (uint8_t*) m_compression_buffer;
+                z.next_out  = reinterpret_cast<uint8_t*>(m_compression_buffer);
 
                 // space for compressed data
                 z.avail_out = OSMPBF::max_uncompressed_blob_size;
@@ -282,8 +281,8 @@ namespace Osmium {
                 }
 
                 // print debug info about the compression
-                if (Osmium::debug()) {
-                    std::cerr << "pack " << in.size() << " bytes to " << z.total_out << " bytes (1:" << (double)in.size() / z.total_out << ")" << std::endl;
+                if (debug && has_debug_level(1)) {
+                    std::cerr << "pack " << in.size() << " bytes to " << z.total_out << " bytes (1:" << static_cast<double>(in.size()) / z.total_out << ")" << std::endl;
                 }
 
                 // number of compressed bytes
@@ -297,7 +296,7 @@ namespace Osmium {
              * @param type Type-string used in the BlobHeader.
              * @param msg Protobuf-message.
              */
-            void store_blob(const std::string &type, const google::protobuf::MessageLite &msg) {
+            void store_blob(const std::string& type, const google::protobuf::MessageLite& msg) {
                 // buffer to serialize the protobuf message to
                 std::string data;
 
@@ -312,7 +311,7 @@ namespace Osmium {
                     pbf_blob.set_zlib_data(m_compression_buffer, out);
                 } else { // no compression
                     // print debug info about the raw data
-                    if (Osmium::debug()) {
+                    if (debug && has_debug_level(1)) {
                         std::cerr << "store uncompressed " << data.size() << " bytes" << std::endl;
                     }
 
@@ -347,13 +346,13 @@ namespace Osmium {
                 uint32_t sz = htonl(blobhead.size());
 
                 // write to the file: the 4-byte BlobHeader-Size followed by the BlobHeader followed by the Blob
-                if (::write(get_fd(), &sz, sizeof(sz)) < 0) {
+                if (::write(fd(), &sz, sizeof(sz)) < 0) {
                     throw std::runtime_error("file error");
                 }
-                if (::write(get_fd(), blobhead.c_str(), blobhead.size()) < 0) {
+                if (::write(fd(), blobhead.c_str(), blobhead.size()) < 0) {
                     throw std::runtime_error("file error");
                 }
-                if (::write(get_fd(), data.c_str(), data.size()) < 0) {
+                if (::write(fd(), data.c_str(), data.size()) < 0) {
                     throw std::runtime_error("file error");
                 }
             }
@@ -437,9 +436,10 @@ namespace Osmium {
              * a helper function used in map_string_ids to map common interim string-ids of the
              * user name and all tags to real string ids.
              *
-             * pbf_object_t is either OSMPBF::Node, OSMPBF::Way or OSMPBF::Relation.
+             * TPBFObject is either OSMPBF::Node, OSMPBF::Way or OSMPBF::Relation.
              */
-            template <class pbf_object_t> void map_common_string_ids(pbf_object_t* in) {
+            template <class TPBFObject>
+            void map_common_string_ids(TPBFObject* in) {
                 // if the object has meta-info attached
                 if (in->has_info()) {
                     // map the interim-id of the user name to a real id
@@ -468,16 +468,17 @@ namespace Osmium {
              * convert a timestamp to an int, respecting the current blocks granularity
              */
             int64_t timestamp2int(time_t timestamp) {
-                return round(timestamp * ((double)1000 / date_granularity()));
+                return round(timestamp * (static_cast<double>(1000) / date_granularity()));
             }
 
             /**
              * helper function used in the write()-calls to apply common information from an osmium-object
              * onto a pbf-object.
              *
-             * pbf_object_t is either OSMPBF::Node, OSMPBF::Way or OSMPBF::Relation.
+             * TPBFObject is either OSMPBF::Node, OSMPBF::Way or OSMPBF::Relation.
              */
-            template <class pbf_object_t> void apply_common_info(const shared_ptr<Osmium::OSM::Object const>& in, pbf_object_t* out) {
+            template <class TPBFObject>
+            void apply_common_info(const shared_ptr<Osmium::OSM::Object const>& in, TPBFObject* out) {
                 // set the object-id
                 out->set_id(in->id());
 
@@ -510,7 +511,7 @@ namespace Osmium {
              * store the current pbf_header_block into a Blob and clear this struct afterwards.
              */
             void store_header_block() {
-                if (Osmium::debug()) {
+                if (debug && has_debug_level(1)) {
                     std::cerr << "storing header block" << std::endl;
                 }
                 store_blob("OSMHeader", pbf_header_block);
@@ -523,10 +524,14 @@ namespace Osmium {
              * this struct and all related pointers and maps afterwards.
              */
             void store_primitive_block() {
-                if (Osmium::debug()) {
+                if (debug && has_debug_level(1)) {
                     std::cerr << "storing primitive block with " << primitive_block_contents << " items" << std::endl;
                 }
 
+                // set the granularity
+                pbf_primitive_block.set_granularity(location_granularity());
+                pbf_primitive_block.set_date_granularity(date_granularity());
+
                 // store the interim StringTable into the protobuf object
                 string_table.store_stringtable(pbf_primitive_block.mutable_stringtable());
 
@@ -539,15 +544,6 @@ namespace Osmium {
                 // clear the PrimitiveBlock struct
                 pbf_primitive_block.Clear();
 
-                // add empty StringTable entry at index 0
-                // StringTable index 0 is rserved as delimiter in the densenodes key/value list
-                // this line also ensures that there's always a valid StringTable
-                pbf_primitive_block.mutable_stringtable()->add_s("");
-
-                // set the granularity
-                pbf_primitive_block.set_granularity(location_granularity());
-                pbf_primitive_block.set_date_granularity(date_granularity());
-
                 // clear the interim StringTable and its id map
                 string_table.clear();
 
@@ -581,11 +577,10 @@ namespace Osmium {
             void check_block_contents_counter() {
                 if (primitive_block_contents >= max_block_contents) {
                     store_primitive_block();
-                }
-                else if (primitive_block_size > (static_cast<uint32_t>(OSMPBF::max_uncompressed_blob_size) * buffer_fill_percent / 100)) {
-                    if (Osmium::debug()) {
+                } else if (primitive_block_size > (static_cast<uint32_t>(OSMPBF::max_uncompressed_blob_size) * buffer_fill_percent / 100)) {
+                    if (debug && has_debug_level(1)) {
                         std::cerr << "storing primitive_block with only " << primitive_block_contents << " items, because its ByteSize (" << primitive_block_size << ") reached " <<
-                            (static_cast<float>(primitive_block_size) / static_cast<float>(OSMPBF::max_uncompressed_blob_size) * 100.0) << "% of the maximum blob-size" << std::endl;
+                                  (static_cast<float>(primitive_block_size) / static_cast<float>(OSMPBF::max_uncompressed_blob_size) * 100.0) << "% of the maximum blob-size" << std::endl;
                     }
 
                     store_primitive_block();
@@ -611,8 +606,8 @@ namespace Osmium {
 
                 // modify lat & lon to integers, respecting the block's granularity and copy
                 // the ints to the pbf-object
-                pbf_node->set_lon(lonlat2int(node->get_lon()));
-                pbf_node->set_lat(lonlat2int(node->get_lat()));
+                pbf_node->set_lon(lonlat2int(node->lon()));
+                pbf_node->set_lat(lonlat2int(node->lat()));
             }
 
             /**
@@ -628,16 +623,16 @@ namespace Osmium {
                 dense->add_id(m_delta_id.update(node->id()));
 
                 // copy the longitude, delta encoded
-                dense->add_lon(m_delta_lon.update(lonlat2int(node->get_lon())));
+                dense->add_lon(m_delta_lon.update(lonlat2int(node->lon())));
 
                 // copy the latitude, delta encoded
-                dense->add_lat(m_delta_lat.update(lonlat2int(node->get_lat())));
+                dense->add_lat(m_delta_lat.update(lonlat2int(node->lat())));
 
                 // in the densenodes structure keys and vals are encoded in an intermixed
                 // array, individual nodes are seperated by a value of 0 (0 in the StringTable
                 // is always unused)
                 // so for three nodes the keys_vals array may look like this: 3 5 2 1 0 0 8 5
-                // the first node has two tags (3=>5 and 2=>1), the second node has does not
+                // the first node has two tags (3=>5 and 2=>1), the second node does not
                 // have any tags and the third node has a single tag (8=>5)
                 Osmium::OSM::TagList::const_iterator end = node->tags().end();
                 for (Osmium::OSM::TagList::const_iterator it = node->tags().begin(); it != end; ++it) {
@@ -687,7 +682,7 @@ namespace Osmium {
                 Delta<int64_t> delta_id;
 
                 // iterate over all way-nodes
-                for (int i=0, l = way->node_count(); i<l; i++) {
+                for (int i=0, l = way->nodes().size(); i<l; i++) {
                     // copy the way-node-id, delta encoded
                     pbf_way->add_refs(delta_id.update(way->get_node_id(i)));
                 }
@@ -734,7 +729,7 @@ namespace Osmium {
                             pbf_relation->add_types(OSMPBF::Relation::RELATION);
                             break;
                         default:
-                            throw std::runtime_error("Unknown relation member type: " + mem->type());
+                            throw std::runtime_error("Unknown relation member type");
                     }
                 }
 
@@ -742,12 +737,21 @@ namespace Osmium {
                 primitive_block_size += pbf_relation->ByteSize();
             }
 
+            // objects of this class can't be copied
+            PBF(const PBF&);
+            PBF& operator=(const PBF&);
+
         public:
 
             /**
              * Create PBF output object from OSMFile.
              */
-            PBF(Osmium::OSMFile& file) : Base(file),
+            PBF(const Osmium::OSMFile& file) :
+                Base(file),
+                pbf_blob(),
+                pbf_blob_header(),
+                pbf_header_block(),
+                pbf_primitive_block(),
                 pbf_nodes(NULL),
                 pbf_ways(NULL),
                 pbf_relations(NULL),
@@ -859,7 +863,7 @@ namespace Osmium {
              * the writing-program and adds the obligatory StringTable-Index 0.
              */
             void init(Osmium::OSM::Meta& meta) {
-                if (Osmium::debug()) {
+                if (debug && has_debug_level(1)) {
                     std::cerr << "pbf write init" << std::endl;
                 }
 
@@ -873,38 +877,29 @@ namespace Osmium {
 
                 // when the resulting file will carry history information, add
                 // HistoricalInformation as required feature
-                if (m_file.get_type() == Osmium::OSMFile::FileType::History()) {
+                if (m_file.type() == Osmium::OSMFile::FileType::History()) {
                     pbf_header_block.add_required_features("HistoricalInformation");
                 }
 
                 // set the writing program
-                pbf_header_block.set_writingprogram("Osmium (http://wiki.openstreetmap.org/wiki/Osmium)");
+                pbf_header_block.set_writingprogram(m_generator);
 
                 if (meta.bounds().defined()) {
                     OSMPBF::HeaderBBox* bbox = pbf_header_block.mutable_bbox();
-                    bbox->set_left(meta.bounds().bl().lon() * OSMPBF::lonlat_resolution);
-                    bbox->set_bottom(meta.bounds().bl().lat() * OSMPBF::lonlat_resolution);
-                    bbox->set_right(meta.bounds().tr().lon() * OSMPBF::lonlat_resolution);
-                    bbox->set_top(meta.bounds().tr().lat() * OSMPBF::lonlat_resolution);
+                    bbox->set_left(meta.bounds().bottom_left().lon() * OSMPBF::lonlat_resolution);
+                    bbox->set_bottom(meta.bounds().bottom_left().lat() * OSMPBF::lonlat_resolution);
+                    bbox->set_right(meta.bounds().top_right().lon() * OSMPBF::lonlat_resolution);
+                    bbox->set_top(meta.bounds().top_right().lat() * OSMPBF::lonlat_resolution);
                 }
 
                 store_header_block();
-
-                // add empty StringTable entry at index 0
-                // StringTable index 0 is reserved as delimiter in the densenodes key/value list
-                // this line also ensures that there's always a valid StringTable
-                pbf_primitive_block.mutable_stringtable()->add_s("");
-
-                // set the granularity
-                pbf_primitive_block.set_granularity(location_granularity());
-                pbf_primitive_block.set_date_granularity(date_granularity());
             }
 
             /**
              * Add a node to the pbf.
              *
              * A call to this method won't write the node to the file directly but
-             * cache it for later bulk-writing. Calling write_final ensures that everything
+             * cache it for later bulk-writing. Calling final() ensures that everything
              * gets written and every file pointer is closed.
              */
             void node(const shared_ptr<Osmium::OSM::Node const>& node) {
@@ -912,7 +907,7 @@ namespace Osmium {
                 // disk if the limit is reached. This call also increases the contents-counter
                 check_block_contents_counter();
 
-                if (Osmium::debug()) {
+                if (debug && has_debug_level(2)) {
                     std::cerr << "node " << node->id() << " v" << node->version() << std::endl;
                 }
 
@@ -932,7 +927,7 @@ namespace Osmium {
              * Add a way to the pbf.
              *
              * A call to this method won't write the way to the file directly but
-             * cache it for later bulk-writing. Calling write_final ensures that everything
+             * cache it for later bulk-writing. Calling final() ensures that everything
              * gets written and every file pointer is closed.
              */
             void way(const shared_ptr<Osmium::OSM::Way const>& way) {
@@ -940,8 +935,8 @@ namespace Osmium {
                 // disk if the limit is reached. This call also increases the contents-counter
                 check_block_contents_counter();
 
-                if (Osmium::debug()) {
-                    std::cerr << "way " << way->id() << " v" << way->version() << " with " << way->node_count() << " nodes" << std::endl;
+                if (debug && has_debug_level(2)) {
+                    std::cerr << "way " << way->id() << " v" << way->version() << " with " << way->nodes().size() << " nodes" << std::endl;
                 }
 
                 // if no PrimitiveGroup for nodes has been added, add one and save the pointer
@@ -956,7 +951,7 @@ namespace Osmium {
              * Add a relation to the pbf.
              *
              * A call to this method won't write the way to the file directly but
-             * cache it for later bulk-writing. Calling write_final ensures that everything
+             * cache it for later bulk-writing. Calling final() ensures that everything
              * gets written and every file pointer is closed.
              */
             void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
@@ -964,7 +959,7 @@ namespace Osmium {
                 // disk if the limit is reached. This call also increases the contents-counter
                 check_block_contents_counter();
 
-                if (Osmium::debug()) {
+                if (debug && has_debug_level(2)) {
                     std::cerr << "relation " << relation->id() << " v" << relation->version() << " with " << relation->members().size() << " members" << std::endl;
                 }
 
@@ -981,7 +976,7 @@ namespace Osmium {
              * close the file.
              */
             void final() {
-                if (Osmium::debug()) {
+                if (debug && has_debug_level(1)) {
                     std::cerr << "finishing" << std::endl;
                 }
 
@@ -995,6 +990,16 @@ namespace Osmium {
 
         }; // class PBF
 
+        namespace {
+
+            inline Osmium::Output::Base* CreateOutputPBF(const Osmium::OSMFile& file) {
+                return new Osmium::Output::PBF(file);
+            }
+
+            const bool pbf_registered = Osmium::Output::Factory::instance().register_output_format(Osmium::OSMFile::FileEncoding::PBF(), CreateOutputPBF);
+
+        } // namespace
+
     } // namespace Output
 
 } // namespace Osmium
diff --git a/include/osmium/output/xml.hpp b/include/osmium/output/xml.hpp
index 590ad9f..48632d3 100644
--- a/include/osmium/output/xml.hpp
+++ b/include/osmium/output/xml.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,173 +22,204 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#define OSMIUM_COMPILE_WITH_CFLAGS_XML2 `xml2-config --cflags`
+#define OSMIUM_LINK_WITH_LIBS_XML2 `xml2-config --libs`
+
 // this is required to allow using libxml's xmlwriter in parallel to expat xml parser under debian
 #undef XMLCALL
 #include <libxml/xmlwriter.h>
 
-// XXX error handling is mostly missing...
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <osmium/output.hpp>
 
 namespace Osmium {
 
     namespace Output {
 
-        class XML : public Base {
+        struct XMLWriteError {};
 
-        public:
+        namespace {
 
-            XML(Osmium::OSMFile& file) : Base(file), m_last_op('\0') {
-                xml_writer = xmlNewTextWriter(xmlOutputBufferCreateFd(this->get_fd(), NULL));
+            void check_for_error(int count) {
+                if (count < 0) {
+                    throw XMLWriteError();
+                }
             }
+        }
+
+        class XML : public Base {
+
+            // objects of this class can't be copied
+            XML(const XML&);
+            XML& operator=(const XML&);
+
+        public:
 
-            ~XML() {
+            XML(const Osmium::OSMFile& file) :
+                Base(file),
+                m_xml_output_buffer(xmlOutputBufferCreateFd(this->fd(), NULL)),
+                m_xml_writer(xmlNewTextWriter(m_xml_output_buffer)),
+                m_last_op('\0') {
+                if (!m_xml_output_buffer || !m_xml_writer) {
+                    throw XMLWriteError();
+                }
             }
 
             void init(Osmium::OSM::Meta& meta) {
-                xmlTextWriterSetIndent(xml_writer, 1);
-                xmlTextWriterSetIndentString(xml_writer, BAD_CAST "  ");
-                xmlTextWriterStartDocument(xml_writer, NULL, "utf-8", NULL); // <?xml .. ?>
+                check_for_error(xmlTextWriterSetIndent(m_xml_writer, 1));
+                check_for_error(xmlTextWriterSetIndentString(m_xml_writer, BAD_CAST "  "));
+                check_for_error(xmlTextWriterStartDocument(m_xml_writer, NULL, NULL, NULL)); // <?xml .. ?>
 
-                if (m_file.get_type() == Osmium::OSMFile::FileType::Change()) {
-                    xmlTextWriterStartElement(xml_writer, BAD_CAST "osmChange");  // <osmChange>
+                if (m_file.type() == Osmium::OSMFile::FileType::Change()) {
+                    check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "osmChange"));  // <osmChange>
                 } else {
-                    xmlTextWriterStartElement(xml_writer, BAD_CAST "osm");  // <osm>
+                    check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "osm"));  // <osm>
                 }
-                xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "version", BAD_CAST "0.6");
-                xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "generator", BAD_CAST "Osmium (http://wiki.openstreetmap.org/wiki/Osmium)");
+                check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "version", BAD_CAST "0.6"));
+                check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "generator", BAD_CAST m_generator.c_str()));
                 if (meta.bounds().defined()) {
-                    xmlTextWriterStartElement(xml_writer, BAD_CAST "bounds"); // <bounds>
+                    check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "bounds")); // <bounds>
 
-                    xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "minlon", "%.7f", meta.bounds().bl().lon());
-                    xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "minlat", "%.7f", meta.bounds().bl().lat());
-                    xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "maxlon", "%.7f", meta.bounds().tr().lon());
-                    xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "maxlat", "%.7f", meta.bounds().tr().lat());
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "minlon", "%.7f", meta.bounds().bottom_left().lon()));
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "minlat", "%.7f", meta.bounds().bottom_left().lat()));
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "maxlon", "%.7f", meta.bounds().top_right().lon()));
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "maxlat", "%.7f", meta.bounds().top_right().lat()));
 
-                    xmlTextWriterEndElement(xml_writer); // </bounds>
+                    check_for_error(xmlTextWriterEndElement(m_xml_writer)); // </bounds>
                 }
             }
 
             void node(const shared_ptr<Osmium::OSM::Node const>& node) {
-                if (m_file.get_type() == Osmium::OSMFile::FileType::Change()) {
+                if (m_file.type() == Osmium::OSMFile::FileType::Change()) {
                     open_close_op_tag(node->visible() ? (node->version() == 1 ? 'c' : 'm') : 'd');
                 }
-                xmlTextWriterStartElement(xml_writer, BAD_CAST "node"); // <node>
+                check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "node")); // <node>
 
                 write_meta(node);
 
-                const Osmium::OSM::Position position = node->position();
-                xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "lat", "%.7f", position.lat());
-                xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "lon", "%.7f", position.lon());
+                if (node->position().defined()) {
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "lat", "%.7f", node->position().lat()));
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "lon", "%.7f", node->position().lon()));
+                }
 
                 write_tags(node->tags());
 
-                xmlTextWriterEndElement(xml_writer); // </node>
+                check_for_error(xmlTextWriterEndElement(m_xml_writer)); // </node>
             }
 
             void way(const shared_ptr<Osmium::OSM::Way const>& way) {
-                if (m_file.get_type() == Osmium::OSMFile::FileType::Change()) {
+                if (m_file.type() == Osmium::OSMFile::FileType::Change()) {
                     open_close_op_tag(way->visible() ? (way->version() == 1 ? 'c' : 'm') : 'd');
                 }
-                xmlTextWriterStartElement(xml_writer, BAD_CAST "way"); // <way>
+                check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "way")); // <way>
 
                 write_meta(way);
 
                 Osmium::OSM::WayNodeList::const_iterator end = way->nodes().end();
                 for (Osmium::OSM::WayNodeList::const_iterator it = way->nodes().begin(); it != end; ++it) {
-                    xmlTextWriterStartElement(xml_writer, BAD_CAST "nd"); // <nd>
-                    xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "ref", "%d", it->ref());
-                    xmlTextWriterEndElement(xml_writer); // </nd>
+                    check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "nd")); // <nd>
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "ref", "%" PRId64, it->ref()));
+                    check_for_error(xmlTextWriterEndElement(m_xml_writer)); // </nd>
                 }
 
                 write_tags(way->tags());
 
-                xmlTextWriterEndElement(xml_writer); // </way>
+                check_for_error(xmlTextWriterEndElement(m_xml_writer)); // </way>
             }
 
             void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
-                if (m_file.get_type() == Osmium::OSMFile::FileType::Change()) {
+                if (m_file.type() == Osmium::OSMFile::FileType::Change()) {
                     open_close_op_tag(relation->visible() ? (relation->version() == 1 ? 'c' : 'm') : 'd');
                 }
-                xmlTextWriterStartElement(xml_writer, BAD_CAST "relation"); // <relation>
+                check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "relation")); // <relation>
 
                 write_meta(relation);
 
                 Osmium::OSM::RelationMemberList::const_iterator end = relation->members().end();
                 for (Osmium::OSM::RelationMemberList::const_iterator it = relation->members().begin(); it != end; ++it) {
-                    xmlTextWriterStartElement(xml_writer, BAD_CAST "member"); // <member>
+                    check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "member")); // <member>
 
-                    xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "type", BAD_CAST it->type_name());
-                    xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "ref", "%d", it->ref());
-                    xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "role", BAD_CAST it->role());
+                    check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "type", BAD_CAST it->type_name()));
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "ref", "%" PRId64, it->ref()));
+                    check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "role", BAD_CAST it->role()));
 
-                    xmlTextWriterEndElement(xml_writer); // </member>
+                    check_for_error(xmlTextWriterEndElement(m_xml_writer)); // </member>
                 }
 
                 write_tags(relation->tags());
 
-                xmlTextWriterEndElement(xml_writer); // </relation>
+                check_for_error(xmlTextWriterEndElement(m_xml_writer)); // </relation>
             }
 
             void final() {
-                if (m_file.get_type() == Osmium::OSMFile::FileType::Change()) {
+                if (m_file.type() == Osmium::OSMFile::FileType::Change()) {
                     open_close_op_tag('\0');
                 }
-                xmlTextWriterEndElement(xml_writer); // </osm> or </osmChange>
-                xmlFreeTextWriter(xml_writer);
+                check_for_error(xmlTextWriterEndElement(m_xml_writer)); // </osm> or </osmChange>
+                xmlFreeTextWriter(m_xml_writer);
                 m_file.close();
             }
 
         private:
 
-            xmlTextWriterPtr xml_writer;
+            xmlOutputBufferPtr m_xml_output_buffer;
+            xmlTextWriterPtr m_xml_writer;
+            char m_last_op;
 
             void write_meta(const shared_ptr<Osmium::OSM::Object const>& object) {
-                xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "id",      "%d", object->id());
-                xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "version", "%d", object->version());
-                xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "timestamp", BAD_CAST object->timestamp_as_string().c_str());
+                check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "id", "%" PRId64, object->id()));
+                if (object->version()) {
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "version", "%d", object->version()));
+                }
+                if (object->timestamp()) {
+                    check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "timestamp", BAD_CAST object->timestamp_as_string().c_str()));
+                }
 
-                // uid == 0 -> anonymous
+                // uid <= 0 -> anonymous
                 if (object->uid() > 0) {
-                    xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "uid", "%d", object->uid());
-                    xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "user", BAD_CAST object->user());
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "uid", "%d", object->uid()));
+                    check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "user", BAD_CAST object->user()));
                 }
 
-                xmlTextWriterWriteFormatAttribute(xml_writer, BAD_CAST "changeset", "%d", object->changeset());
+                if (object->changeset()) {
+                    check_for_error(xmlTextWriterWriteFormatAttribute(m_xml_writer, BAD_CAST "changeset", "%d", object->changeset()));
+                }
 
-                if (m_file.has_multiple_object_versions() && m_file.get_type() != Osmium::OSMFile::FileType::Change()) {
-                    xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "visible", object->visible() ? BAD_CAST "true" : BAD_CAST "false");
+                if (m_file.has_multiple_object_versions() && m_file.type() != Osmium::OSMFile::FileType::Change()) {
+                    check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "visible", object->visible() ? BAD_CAST "true" : BAD_CAST "false"));
                 }
             }
 
             void write_tags(const Osmium::OSM::TagList& tags) {
                 Osmium::OSM::TagList::const_iterator end = tags.end();
                 for (Osmium::OSM::TagList::const_iterator it = tags.begin(); it != end; ++it) {
-                    xmlTextWriterStartElement(xml_writer, BAD_CAST "tag"); // <tag>
-                    xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "k", BAD_CAST it->key());
-                    xmlTextWriterWriteAttribute(xml_writer, BAD_CAST "v", BAD_CAST it->value());
-                    xmlTextWriterEndElement(xml_writer); // </tag>
+                    check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "tag")); // <tag>
+                    check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "k", BAD_CAST it->key()));
+                    check_for_error(xmlTextWriterWriteAttribute(m_xml_writer, BAD_CAST "v", BAD_CAST it->value()));
+                    check_for_error(xmlTextWriterEndElement(m_xml_writer)); // </tag>
                 }
             }
 
-            char m_last_op;
-
-            void open_close_op_tag(char op) {
+            void open_close_op_tag(const char op) {
                 if (op == m_last_op) {
                     return;
                 }
 
                 if (m_last_op) {
-                    xmlTextWriterEndElement(xml_writer);
+                    check_for_error(xmlTextWriterEndElement(m_xml_writer));
                 }
 
                 switch (op) {
                     case 'c':
-                        xmlTextWriterStartElement(xml_writer, BAD_CAST "create");
+                        check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "create"));
                         break;
                     case 'm':
-                        xmlTextWriterStartElement(xml_writer, BAD_CAST "modify");
+                        check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "modify"));
                         break;
                     case 'd':
-                        xmlTextWriterStartElement(xml_writer, BAD_CAST "delete");
+                        check_for_error(xmlTextWriterStartElement(m_xml_writer, BAD_CAST "delete"));
                         break;
                 }
 
@@ -197,6 +228,18 @@ namespace Osmium {
 
         }; // class XML
 
+        namespace {
+
+            inline Osmium::Output::Base* CreateOutputXML(const Osmium::OSMFile& file) {
+                return new Osmium::Output::XML(file);
+            }
+
+            const bool xml_registered = Osmium::Output::Factory::instance().register_output_format(Osmium::OSMFile::FileEncoding::XML(),    CreateOutputXML) &&
+                                        Osmium::Output::Factory::instance().register_output_format(Osmium::OSMFile::FileEncoding::XMLgz(),  CreateOutputXML) &&
+                                        Osmium::Output::Factory::instance().register_output_format(Osmium::OSMFile::FileEncoding::XMLbz2(), CreateOutputXML);
+
+        } // namespace
+
     } // namespace Output
 
 } // namespace Osmium
diff --git a/include/osmium/relations/assembler.hpp b/include/osmium/relations/assembler.hpp
new file mode 100644
index 0000000..a99ec56
--- /dev/null
+++ b/include/osmium/relations/assembler.hpp
@@ -0,0 +1,527 @@
+#ifndef OSMIUM_RELATIONS_ASSEMBLER_HPP
+#define OSMIUM_RELATIONS_ASSEMBLER_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <utility>
+#include <vector>
+#include <boost/foreach.hpp>
+#include <boost/utility.hpp>
+
+#include <osmium/handler.hpp>
+#include <osmium/relations/relation_info.hpp>
+
+namespace Osmium {
+
+    /**
+     * @brief Namespace for code related to OSM relations
+     */
+    namespace Relations {
+
+        /**
+         * Helper class for the Assembler class.
+         *
+         * Stores an object ID and information where the object should be
+         * stored.
+         */
+        struct MemberInfo {
+
+            /**
+             * Object ID of this relation member. Can be a node, way, or relation ID.
+             * It depends on the vector in which this object is stored which kind of
+             * object is referenced here.
+             */
+            osm_object_id_t m_member_id;
+
+            /**
+             * Position of the relation this member is a part of in the
+             * m_relations vector.
+             */
+            unsigned int m_relation_pos;
+
+            /**
+             * Position of this member in the list of members of the
+             * relation this member if a part of.
+             */
+            osm_sequence_id_t m_member_pos;
+
+            /**
+             * Create new MemberInfo. The variant with zeros for relation_pos and
+             * member_pos is used to create dummy MemberInfo that can be compared
+             * to the MemberInfo in the vectors using the equal_range algorithm.
+             */
+            MemberInfo(osm_object_id_t member_id, int relation_pos=0, osm_sequence_id_t member_pos=0) :
+                m_member_id(member_id),
+                m_relation_pos(relation_pos),
+                m_member_pos(member_pos) {
+            }
+
+        };
+
+        /**
+         * Compares two MemberInfo objects by only looking at the member id.
+         * Used to sort a vector of MemberInfo objects and to later find
+         * them using binary search.
+         */
+        bool operator<(const MemberInfo& a, const MemberInfo& b) {
+            return a.m_member_id < b.m_member_id;
+        }
+
+        typedef std::vector<MemberInfo> member_info_vector_t;
+
+        /**
+         * The Assembler class assembles relations and their members. This is a generic
+         * base class that can be used to assemble all kinds of relations. It has numerous
+         * hooks you can implement in child classes to customize its behaviour.
+         *
+         * The assembler provides two handlers (HandlerPass1 and HandlerPass2) for a first
+         * and second pass through an input file, respectively. In the first pass all
+         * relations we are interested in are stored in TRelationInfo objects in the
+         * m_relations vector. All members we are interested in are stored in MemberInfo
+         * objects in the m_member_nodes, m_member_ways, and m_member_relations vectors.
+         * The MemberInfo objects also store the information where the relations containing
+         * those members are to be found.
+         *
+         * Later the m_member_(nodes|ways|relations) vectors are sorted according to the
+         * member ids so that a binary search (with equal_range) can be used in the second
+         * pass to find the relations for each node, way, or relation coming along. The
+         * member objects are stored together with their relation and once a relation is
+         * complete the complete_relation() method is called which you can overwrite in
+         * a child class of Assembler.
+         *
+         * @tparam TAssembler Child class of this class.
+         *
+         * @tparam TRelationInfo RelationInfo or a child class of it.
+         *
+         * @tparam N Are we interested in member nodes?
+         *
+         * @tparam W Are we interested in member ways?
+         *
+         * @tparam R Are we interested in member relations?
+         *
+         * @tparam THandler Nested handler that is called at the end of each method in
+         *         HandlerPass2. Defaults to Osmium::Handler::Base which does nothing.
+         */
+        template <class TAssembler, class TRelationInfo, bool N, bool W, bool R, class THandler=Osmium::Handler::Base>
+        class Assembler {
+
+            /// Vector of RelationInfo objects used to store relations we are interested in.
+            typedef std::vector<TRelationInfo> relation_info_vector_t;
+
+            /// This is the type used for results of the equal_range algorithm.
+            typedef std::pair<typename member_info_vector_t::iterator, typename member_info_vector_t::iterator> member_info_range_t;
+
+        public:
+
+            /**
+             * Create an Assembler without nested handler.
+             */
+            Assembler() :
+                m_base_handler(),
+                m_next_handler(m_base_handler),
+                m_handler_pass1(*static_cast<TAssembler*>(this)),
+                m_handler_pass2(*static_cast<TAssembler*>(this)),
+                m_relations(),
+                m_member_infos() {
+            }
+
+            /**
+             * Create an Assembler with nested handler.
+             */
+            Assembler(THandler& handler) :
+                m_base_handler(),
+                m_next_handler(handler),
+                m_handler_pass1(*static_cast<TAssembler*>(this)),
+                m_handler_pass2(*static_cast<TAssembler*>(this)),
+                m_relations(),
+                m_member_infos() {
+            }
+
+        protected:
+
+            const relation_info_vector_t& relations() const {
+                return m_relations;
+            }
+
+            /**
+             * This method is called from the first pass handler for every
+             * relation in the input. It calls add_relation() to add this
+             * relation to the list of relations we are interested in.
+             *
+             * Overwrite this method in a child class to only call add_relation
+             * on the relations you are interested in, for instance depending
+             * on the type tag. Storing relations takes a lot of memory, so
+             * it makes sense to filter this as much as possible.
+             */
+            void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
+                add_relation(TRelationInfo(relation));
+            }
+
+            /**
+             * This method is called for every member of every relation that
+             * is added with add_relation(). It should decide if the member
+             * is interesting or not and return true or false to signal that.
+             * Only interesting members are later added to the relation.
+             *
+             * Overwrite this method in a child class. In the MultiPolygonAssembler
+             * this is for instance used to only keep members of type way and
+             * ignore all others.
+             */
+            bool keep_member(Osmium::Relations::RelationInfo& /*relation_info*/, const Osmium::OSM::RelationMember& /*member*/) {
+                return true;
+            }
+
+            /**
+             * This method is called for all nodes that are not a member of
+             * any relation.
+             *
+             * Overwrite this method in a child class if you are interested
+             * in this.
+             */
+            void node_not_in_any_relation(const shared_ptr<Osmium::OSM::Node const>& /*node*/) {
+            }
+
+            /**
+             * This method is called for all ways that are not a member of
+             * any relation.
+             *
+             * Overwrite this method in a child class if you are interested
+             * in this.
+             */
+            void way_not_in_any_relation(const shared_ptr<Osmium::OSM::Way const>& /*way*/) {
+            }
+
+            /**
+             * This method is called for all relations that are not a member of
+             * any relation.
+             *
+             * Overwrite this method in a child class if you are interested
+             * in this.
+             */
+            void relation_not_in_any_relation(const shared_ptr<Osmium::OSM::Relation const>& /*relation*/) {
+            }
+
+            /**
+             * This method is called from the handler when all objects that are
+             * wanted as relation members are available.
+             *
+             * Overwrite this method in a child class if you are interested
+             * in this.
+             *
+             * Note that even after this call members might be missing if they
+             * were not in the input file!
+             */
+            void all_members_available() {
+            }
+
+            /**
+             * This removes all relations that have already been assembled
+             * from the m_relations vector.
+             */
+            void clean_assembled_relations() {
+                m_relations.erase(std::remove_if(m_relations.begin(), m_relations.end(), has_all_members()), m_relations.end());
+            }
+
+            /**
+             * Tell the Assembler that you are interested in this relation
+             * and want it kept until all members have been assembled and
+             * it is handed back to you.
+             */
+            void add_relation(TRelationInfo relation_info) {
+                osm_sequence_id_t n=0;
+                BOOST_FOREACH(const Osmium::OSM::RelationMember& member, relation_info.relation()->members()) {
+                    bool add = static_cast<TAssembler*>(this)->keep_member(relation_info, member);
+                    if (add) {
+                        MemberInfo member_info(member.ref(), m_relations.size(), n);
+                        switch (member.type()) {
+                            case 'n':
+                                m_member_infos[NODE].push_back(member_info);
+                                break;
+                            case 'w':
+                                m_member_infos[WAY].push_back(member_info);
+                                break;
+                            case 'r':
+                                m_member_infos[RELATION].push_back(member_info);
+                                break;
+                        }
+                        relation_info.need_member();
+                    }
+                    n++;
+                }
+                m_relations.push_back(relation_info);
+            }
+
+            /**
+             * Return a reference to the nested handler that is called
+             * in the second pass after the Assembler's own handler.
+             */
+            THandler& nested_handler() {
+                return m_next_handler;
+            }
+
+            /**
+             * Sort the vectors with the member infos so that we can do binary
+             * search on them.
+             */
+            void sort_member_infos() {
+                std::sort(m_member_infos[NODE].begin(),     m_member_infos[NODE].end());
+                std::sort(m_member_infos[WAY].begin(),      m_member_infos[WAY].end());
+                std::sort(m_member_infos[RELATION].begin(), m_member_infos[RELATION].end());
+            }
+
+        public:
+
+            uint64_t used_memory() const {
+                uint64_t nmembers = m_member_infos[NODE].size() + m_member_infos[WAY].size() + m_member_infos[RELATION].size();
+                uint64_t relations = m_relations.size() * (sizeof(TRelationInfo) + sizeof(Osmium::OSM::Relation)) + nmembers * sizeof(Osmium::OSM::RelationMember); // plus tags
+                uint64_t members = nmembers * sizeof(MemberInfo);
+
+                std::cout << "nR  = m_relations.size()             = " << m_relations.size() << "\n";
+                std::cout << "nMN = m_member_info[NODE].size()     = " << m_member_infos[NODE].size() << "\n";
+                std::cout << "nMW = m_member_info[WAY].size()      = " << m_member_infos[WAY].size() << "\n";
+                std::cout << "nMR = m_member_info[RELATION].size() = " << m_member_infos[RELATION].size() << "\n";
+                std::cout << "nM  = m_member_info[*].size()        = " << nmembers << "\n";
+
+                std::cout << "sRI = sizeof(TRelationInfo)  = " << sizeof(TRelationInfo) << "\n";
+                std::cout << "sR  = sizeof(Relation)       = " << sizeof(Osmium::OSM::Relation) << "\n";
+                std::cout << "sRM = sizeof(RelationMember) = " << sizeof(Osmium::OSM::RelationMember) << "\n";
+                std::cout << "sMI = sizeof(MemberInfo)     = " << sizeof(MemberInfo) << "\n";
+
+                std::cout << "nR * (sRI + sR) = " << m_relations.size() * (sizeof(TRelationInfo) + sizeof(Osmium::OSM::Relation)) << "\n";
+                std::cout << "nM * sRM        = " << nmembers * sizeof(Osmium::OSM::RelationMember) << "\n";
+                std::cout << "nM * sMI        = " << nmembers * sizeof(MemberInfo) << "\n";
+
+                return relations + members;
+            }
+
+            /**
+             * This is the handler class for the first pass of the Assembler.
+             */
+            class HandlerPass1 : public Osmium::Handler::Base {
+
+                TAssembler& m_assembler;
+
+            public:
+
+                HandlerPass1(TAssembler& assembler) :
+                    Osmium::Handler::Base(),
+                    m_assembler(assembler) {
+                }
+
+                void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
+                    m_assembler.relation(relation);
+                }
+
+                void after_relations() const {
+                    m_assembler.sort_member_infos();
+                    throw Osmium::Handler::StopReading();
+                }
+
+            }; // class HandlerPass1
+
+            /**
+             * This is the handler class for the second pass of the Assembler.
+             */
+            class HandlerPass2 {
+
+                TAssembler& m_assembler;
+
+                /**
+                 * This variable is initialized with the number of different
+                 * kinds of OSM objects we are interested in. If we only need
+                 * way members (for instance for the multipolygon assembler)
+                 * it is intialized with 1 for instance. If node and way
+                 * members are needed, it is initialized with 2.
+                 *
+                 * In the after_* methods of this handler, it is decremented
+                 * and once it reaches 0, we know we have all members available
+                 * that we are ever going to get.
+                 */
+                int m_want_types;
+
+                /**
+                 * Find this object in the member vectors and add it to all
+                 * relations that need it.
+                 *
+                 * @returns true if the member was added to at least one
+                 *          relation and false otherwise
+                 */
+                bool find_and_add_object(const shared_ptr<Osmium::OSM::Object const>& object) const {
+                    member_info_vector_t& miv = m_assembler.m_member_infos[object->type()];
+                    const member_info_range_t range = std::equal_range(miv.begin(), miv.end(), MemberInfo(object->id()));
+
+                    if (range.first == range.second) {
+                        // nothing found
+                        return false;
+                    }
+
+                    BOOST_FOREACH(const MemberInfo& member_info, range) {
+                        assert(member_info.m_member_id == object->id());
+                        assert(member_info.m_relation_pos < m_assembler.m_relations.size());
+                        TRelationInfo& relation_info = m_assembler.m_relations[member_info.m_relation_pos];
+                        assert(member_info.m_member_pos < relation_info.relation()->members().size());
+                        if (relation_info.add_member(object, member_info.m_member_pos)) {
+                            m_assembler.complete_relation(relation_info);
+                            m_assembler.m_relations[member_info.m_relation_pos] = TRelationInfo();
+                        }
+                    }
+
+                    return true;
+                }
+
+                /**
+                 * This method is called from the after_* methods. It reclaimes
+                 * memory that's not needed any more and calls
+                 * all_members_available() if they are.
+                 */
+                void after(osm_object_type_t type) {
+                    // clear all memory used by m_member_info of this type
+                    member_info_vector_t().swap(m_assembler.m_member_infos[type]);
+                    if (--m_want_types == 0) {
+                        m_assembler.all_members_available();
+                    }
+                }
+
+            public:
+
+                HandlerPass2(TAssembler& assembler) :
+                    m_assembler(assembler),
+                    m_want_types((N?1:0) + (W?1:0) + (R?1:0)) {
+                }
+
+                void init(Osmium::OSM::Meta& meta) const {
+                    m_assembler.m_next_handler.init(meta);
+                }
+
+                void before_nodes() const {
+                    m_assembler.m_next_handler.before_nodes();
+                }
+
+                void node(const shared_ptr<Osmium::OSM::Node const>& node) const {
+                    if (N) {
+                        if (! find_and_add_object(node)) {
+                            m_assembler.node_not_in_any_relation(node);
+                        }
+                    }
+                    m_assembler.m_next_handler.node(node);
+                }
+
+                void after_nodes() {
+                    if (N) {
+                        after(NODE);
+                    }
+                    m_assembler.m_next_handler.after_nodes();
+                }
+
+                void before_ways() const {
+                    m_assembler.m_next_handler.before_ways();
+                }
+
+                void way(const shared_ptr<Osmium::OSM::Way const>& way) const {
+                    if (W) {
+                        if (! find_and_add_object(way)) {
+                            m_assembler.way_not_in_any_relation(way);
+                        }
+                    }
+                    m_assembler.m_next_handler.way(way);
+                }
+
+                void after_ways() {
+                    if (W) {
+                        after(WAY);
+                    }
+                    m_assembler.m_next_handler.after_ways();
+                }
+
+                void before_relations() const {
+                    m_assembler.m_next_handler.before_relations();
+                }
+
+                void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) const {
+                    if (R) {
+                        if (! find_and_add_object(relation)) {
+                            m_assembler.relation_not_in_any_relation(relation);
+                        }
+                    }
+                    m_assembler.m_next_handler.relation(relation);
+                }
+
+                void after_relations() {
+                    if (R) {
+                        after(RELATION);
+                    }
+                    m_assembler.m_next_handler.after_relations();
+                }
+
+                void final() const {
+                    m_assembler.m_next_handler.final();
+                }
+
+            }; // class HandlerPass2
+
+            /**
+             * Return reference to first pass handler.
+             */
+            HandlerPass1& handler_pass1() {
+                return m_handler_pass1;
+            }
+
+            /**
+             * Return reference to second pass handler.
+             */
+            HandlerPass2& handler_pass2() {
+                return m_handler_pass2;
+            }
+
+        private:
+
+            /**
+             * This base handler is used as default if no chained handler was
+             * given to the Assembler.
+             */
+            Osmium::Handler::Base m_base_handler;
+
+            /// Reference to chained handler
+            THandler& m_next_handler;
+
+            HandlerPass1 m_handler_pass1;
+            HandlerPass2 m_handler_pass2;
+
+            /// Vector with all relations we are interested in
+            relation_info_vector_t m_relations;
+
+            /**
+             * One vector each for nodes, ways, and relations containing all
+             * mappings from member ids to their relations.
+             */
+            member_info_vector_t m_member_infos[3];
+
+        }; // class Assembler
+
+    } // namespace Relations
+
+} // namespace Osmium
+
+#endif // OSMIUM_RELATIONS_ASSEMBLER_HPP
diff --git a/include/osmium/relations/relation_info.hpp b/include/osmium/relations/relation_info.hpp
new file mode 100644
index 0000000..5db34e3
--- /dev/null
+++ b/include/osmium/relations/relation_info.hpp
@@ -0,0 +1,140 @@
+#ifndef OSMIUM_RELATIONS_RELATION_INFO_HPP
+#define OSMIUM_RELATIONS_RELATION_INFO_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <functional>
+#include <vector>
+
+#include <osmium/smart_ptr.hpp>
+#include <osmium/osm/relation.hpp>
+
+namespace Osmium {
+
+    namespace Relations {
+
+        /**
+         * Helper class for the Assembler class.
+         *
+         * Stores a shared pointer to a relation plus the information needed to
+         * add members to this relation.
+         *
+         * You can derive from this class in a child class of Assembler if you
+         * need to store more information about a relation. See the
+         * MultiPolygonRelationInfo class for an example.
+         */
+        class RelationInfo {
+
+            /// The relation we are assembling.
+            shared_ptr<Osmium::OSM::Relation const> m_relation;
+
+            /// Vector for relation members. Is initialized with the right size and empty objects.
+            std::vector< shared_ptr<Osmium::OSM::Object const> > m_members;
+
+            /**
+             * The number of members still needed before the relation is complete.
+             * This will be set to the number of members we are interested in and
+             * then count down for every member we find. When it is 0, the relation
+             * is complete.
+             */
+            int m_need_members;
+
+        public:
+
+            /**
+             * Initialize an empty RelationInfo. This is needed to zero out relations that
+             * have been completed and thus are removed from the m_relations vector.
+             */
+            RelationInfo() :
+                m_relation(),
+                m_members(),
+                m_need_members(0) {
+            }
+
+            RelationInfo(const shared_ptr<Osmium::OSM::Relation const>& relation) :
+                m_relation(relation),
+                m_members(relation->members().size()),
+                m_need_members(0) {
+            }
+
+            const shared_ptr<Osmium::OSM::Relation const>& relation() const {
+                return m_relation;
+            }
+
+            int need_members() const {
+                return m_need_members;
+            }
+
+            /**
+             * There is one more member we need.
+             */
+            void need_member() {
+                ++m_need_members;
+            }
+
+            /**
+             * Add new member. This stores a shared pointer to the member object
+             * in the RelationInfo. It also decrements the members needed counter.
+             *
+             * @return true if relation is complete, false otherwise
+             */
+            bool add_member(const shared_ptr<Osmium::OSM::Object const>& object, osm_sequence_id_t n) {
+                assert(m_need_members > 0);
+                assert(n < m_relation->members().size());
+                m_members[n] = object;
+                return --m_need_members == 0;
+            }
+
+            /**
+             * Get a vector reference with shared pointers to all member objects.
+             * Note that the pointers can be empty if a member object is of a type
+             * we have not requested from the assembler (or if it was not in the
+             * input).
+             */
+            const std::vector< shared_ptr<Osmium::OSM::Object const> >& members() const {
+                return m_members;
+            }
+
+            /**
+             * Returns true if all members for this relation are available.
+             */
+            bool has_all_members() const {
+                return m_need_members == 0;
+            }
+        };
+
+        /**
+         * Function object to check if a relation is complete.
+         *
+         * @return true if this relation is complete, false otherwise.
+         */
+        struct has_all_members : public std::unary_function<RelationInfo, bool> {
+            bool operator()(RelationInfo& relation_info) const {
+                return relation_info.has_all_members();
+            }
+        };
+
+    } // namespace Relations
+
+} // namespace Osmium
+
+#endif // OSMIUM_RELATIONS_RELATION_INFO_HPP
diff --git a/include/osmium/export.hpp b/include/osmium/smart_ptr.hpp
similarity index 61%
copy from include/osmium/export.hpp
copy to include/osmium/smart_ptr.hpp
index 9302cc3..b45a56b 100644
--- a/include/osmium/export.hpp
+++ b/include/osmium/smart_ptr.hpp
@@ -1,9 +1,9 @@
-#ifndef OSMIUM_EXPORT_HPP
-#define OSMIUM_EXPORT_HPP
+#ifndef OSMIUM_SMART_PTR_HPP
+#define OSMIUM_SMART_PTR_HPP
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,19 +22,17 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-namespace Osmium {
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/make_shared.hpp>
 
-    /**
-     * @brief Classes implementing export into non-OSM formats such as to shapefiles.
-     */
-    namespace Export {
-    } // namespace Export
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::scoped_ptr;
+using boost::static_pointer_cast;
+using boost::const_pointer_cast;
+using boost::dynamic_pointer_cast;
+using boost::make_shared;
 
-} // namespace Osmium
-
-#ifdef OSMIUM_WITH_JAVASCRIPT
-# include <osmium/export/csv.hpp>
-# include <osmium/export/shapefile.hpp>
-#endif
-
-#endif // OSMIUM_EXPORT_HPP
+#endif // OSMIUM_SMART_PTR_HPP
diff --git a/include/osmium/storage/byid.hpp b/include/osmium/storage/byid.hpp
index 1029829..0ed3f79 100644
--- a/include/osmium/storage/byid.hpp
+++ b/include/osmium/storage/byid.hpp
@@ -1,9 +1,9 @@
-#ifndef OSMIUM_HANDLER_STORE_HPP
-#define OSMIUM_HANDLER_STORE_HPP
+#ifndef OSMIUM_STORAGE_BYID_HPP
+#define OSMIUM_STORAGE_BYID_HPP
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,14 +22,7 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
-#include <stdexcept>
-#include <cstdlib>
-#include <google/sparsetable>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <fcntl.h>
+#include <stdint.h>
 #include <boost/utility.hpp>
 
 namespace Osmium {
@@ -39,331 +32,73 @@ namespace Osmium {
      */
     namespace Storage {
 
-        /**
-         * This abstract class defines an interface to storage classes
-         * intended for storing small pieces of data (such as coordinates)
-         * indexed by a positive object ID. The storage must be very
-         * space efficient and able to scale to billions of objects.
-         *
-         * Subclasses have different implementations that will store the
-         * data in different ways in memory and/or on disk. Some storage
-         * classes are better suited when working with the whole planet,
-         * some are better for data extracts.
-         *
-         * Note that these classes are not required to track "empty" fields.
-         * When reading data you have to be sure you have put something in
-         * there before.
-         */
-        template <typename TValue>
-        class ById : boost::noncopyable {
-
-        public:
-
-            virtual ~ById() {
-            }
-
-            /// The "value" type, usually a coordinates class or similar.
-            typedef TValue value_type;
-
-            /// Set the field with id to value.
-            virtual void set(uint64_t id, TValue value) = 0;
-
-            /// Retrieve value by key. Does not check for overflow or empty fields.
-            virtual const TValue& operator[](uint64_t id) const = 0;
-
-            /**
-             * Get the approximate number of items in the storage. The storage
-             * might allocate memory in blocks, so this size might not be
-             * accurate. You can not use this to find out how much memory the
-             * storage uses. Use used_memory() for that.
-             */
-            virtual uint64_t size() const = 0;
-
-            /**
-             * Get the memory used for this storage in bytes. Note that this
-             * is not necessarily entirely accurate but an approximation.
-             * For storage classes that store the data in memory, this is
-             * the main memory used, for storage classes storing data on disk
-             * this is the memory used on disk.
-             */
-            virtual uint64_t used_memory() const = 0;
-
-            /**
-             * Clear memory used for this storage. After this you can not
-             * use the storage container any more.
-             */
-            virtual void clear() = 0;
-
-        };
-
-        /**
-        * The FixedArray storage stores location in a huge array. The size of
-        * the array is given when initializing the object, it must be large
-        * enough to hold all items.
-        *
-        * Only use this store when you know beforehand how many IDs there are.
-        * It is mainly provided for cases where the more flexible Mmap storage
-        * class does not work.
-        *
-        * There is no range checking on accessing the store.
-        *
-        * If you are storing node coordinates, you'll need 8 bytes for each node.
-        * At the time of writing this, the largest node ID is about 1.3 billion,
-        * so you'll need about 10 GB of memory.
-        *
-        * Note that this storage class will only work on 64 bit systems if
-        * used for storing node coordinates. 32 bit systems just can't address
-        * that much memory!
-        */
-        template <typename TValue>
-        class FixedArray : public ById<TValue> {
-
-        public:
-
-            /**
-             * Constructor.
-             *
-             * @param max_id One larger than the largest ID you will ever have.
-             * @exception std::bad_alloc Thrown when there is not enough memory.
-             */
-            FixedArray(const uint64_t max_id) : ById<TValue>(), m_size(max_id) {
-                m_items = (TValue*) malloc(sizeof(TValue) * max_id);
-                if (!m_items) {
-                    throw std::bad_alloc();
-                }
-            }
-
-            ~FixedArray() {
-                clear();
-            }
-
-            void set(uint64_t id, TValue value) {
-                m_items[id] = value;
-            }
-
-            const TValue& operator[](uint64_t id) const {
-                return m_items[id];
-            }
-
-            uint64_t size() const {
-                return m_size;
-            }
-
-            uint64_t used_memory() const {
-                return m_size * sizeof(TValue);
-            }
-
-            void clear() {
-                free(m_items);
-                m_items = NULL;
-            }
-
-        private:
-
-            TValue* m_items;
-
-            uint64_t m_size;
-
-        }; // class FixedArray
-
-        /**
-        * The SparseTable store stores items in a Google sparsetable,
-        * a data structure that can hold sparsly filled tables in a
-        * very space efficient way. It will resize automatically.
-        *
-        * Use this node location store if the ID space is only sparsly
-        * populated, such as when working with smaller OSM files (like
-        * country extracts).
-        */
-        template <typename TValue>
-        class SparseTable : public ById<TValue> {
-
-        public:
-
-            /**
-             * Constructor.
-             *
-             * @param grow_size The initial size of the storage (in items).
-             *                  The storage will grow by at least this size
-             *                  every time it runs out of space.
-             */
-            SparseTable(const uint64_t grow_size=10000)
-                : ById<TValue>(),
-                  m_grow_size(grow_size),
-                  m_items(grow_size) {
-            }
-
-            ~SparseTable() {
-            }
-
-            void set(uint64_t id, TValue value) {
-                if (id >= m_items.size()) {
-                    m_items.resize(id + m_grow_size);
-                }
-                m_items[id] = value;
-            }
-
-            const TValue& operator[](uint64_t id) const {
-                return m_items[id];
-            }
-
-            uint64_t size() const {
-                return m_items.size();
-            }
-
-            uint64_t used_memory() const {
-                // unused items use 1 bit, used items sizeof(TValue) bytes
-                // http://google-sparsehash.googlecode.com/svn/trunk/doc/sparsetable.html
-                return (m_items.size() / 8) + (m_items.num_nonempty() * sizeof(TValue));
-            }
-
-            void clear() {
-                m_items.clear();
-            }
-
-        private:
-
-            uint64_t m_grow_size;
-
-            google::sparsetable<TValue> m_items;
-
-        }; // class SparseTable
-
-        /**
-        * The Mmap store stores location using the mmap() system call,
-        * either backed by a file on disk or just in-memory. It will grow
-        * automatically.
-        *
-        * If you have enough memory it is preferred to use the in-memory
-        * version. If you don't have enough memory or want the information
-        * to persist, use the file-backed version. Note that you still need
-        * substantial amounts of memory for this to work efficiently.
-        *
-        * Note that this storage class will only work on 64 bit systems if
-        * used for storing node coordinates. 32 bit systems just can't address
-        * that much memory!
-        */
-        template <typename TValue>
-        class Mmap : public ById<TValue> {
-
-        public:
-
-            static const uint64_t size_increment = 10 * 1024 * 1024;
+        namespace ById {
 
             /**
-             * Create anonymous mapping without a backing file.
-             * @exception std::bad_alloc Thrown when there is not enough memory.
-             */
-            Mmap() : ById<TValue>(), m_size(size_increment), m_fd(-1) {
-                m_items = (TValue*) mmap(NULL, sizeof(TValue) * m_size, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-                if (m_items == MAP_FAILED) {
-                    throw std::bad_alloc();
+            * This abstract class defines an interface to storage classes
+            * intended for storing small pieces of data (such as coordinates)
+            * indexed by a positive object ID. The storage must be very
+            * space efficient and able to scale to billions of objects.
+            *
+            * Subclasses have different implementations that will store the
+            * data in different ways in memory and/or on disk. Some storage
+            * classes are better suited when working with the whole planet,
+            * some are better for data extracts.
+            *
+            * Note that these classes are not required to track "empty" fields.
+            * When reading data you have to be sure you have put something in
+            * there before.
+            *
+            * This storage class will only work on 64 bit systems if used for
+            * storing node coordinates. 32 bit systems just can't address
+            * that much memory!
+            */
+            template <typename TValue>
+            class Base : boost::noncopyable {
+
+            public:
+
+                virtual ~Base() {
                 }
-            }
 
-            /**
-             * Create mapping backed by file. If filename is empty, a temporary
-             * file will be created.
-             *
-             * @param filename The filename (including the path) for the storage.
-             * @param remove Should the file be removed after use?
-             * @exception std::bad_alloc Thrown when there is not enough memory or some other problem.
-             */
-            Mmap(std::string& filename, bool remove=true) : ById<TValue>(), m_size(1) {
-                if (filename == "") {
-                    FILE* file = tmpfile();
-                    if (!file) {
-                        throw std::bad_alloc();
-                    }
-                    m_fd = fileno(file);
-                } else {
-                    m_fd = open(filename.c_str(), O_RDWR | O_CREAT, 0600);
-                }
+                /// The "value" type, usually a coordinates class or similar.
+                typedef TValue value_type;
 
-                if (m_fd < 0) {
-                    throw std::bad_alloc();
-                }
-
-                // now that the file is open we can immediately remove it
-                // (temporary files are always removed)
-                if (remove && filename != "") {
-                    if (unlink(filename.c_str()) < 0) {
-                        // XXX what to do here?
-                    }
-                }
-
-                // make sure the file is at least as large as the initial size
-                if (get_file_size() < sizeof(TValue) * m_size) {
-                    if (ftruncate(m_fd, sizeof(TValue) * m_size) < 0) {
-                        throw std::bad_alloc();
-                    }
-                }
-
-                m_items = (TValue*) mmap(NULL, sizeof(TValue) * m_size, PROT_READ|PROT_WRITE, MAP_SHARED, m_fd, 0);
-                if (m_items == MAP_FAILED) {
-                    throw std::bad_alloc();
-                }
-            }
+                /// Set the field with id to value.
+                virtual void set(const uint64_t id, const TValue value) = 0;
 
-            ~Mmap() {
-                clear();
-            }
+                /// Retrieve value by key. Does not check for overflow or empty fields.
+                virtual const TValue operator[](const uint64_t id) const = 0;
 
-            void set(uint64_t id, TValue value) {
-                if (id >= m_size) {
-                    uint64_t new_size = id + size_increment;
+                /**
+                * Get the approximate number of items in the storage. The storage
+                * might allocate memory in blocks, so this size might not be
+                * accurate. You can not use this to find out how much memory the
+                * storage uses. Use used_memory() for that.
+                */
+                virtual uint64_t size() const = 0;
 
-                    // if there is a file backing this mmap and its smaller than needed, increase its size
-                    if (m_fd >= 0 && get_file_size() < sizeof(TValue) * new_size) {
-                        if (ftruncate(m_fd, sizeof(TValue) * new_size) < 0) {
-                            throw std::bad_alloc();
-                        }
-                    }
+                /**
+                * Get the memory used for this storage in bytes. Note that this
+                * is not necessarily entirely accurate but an approximation.
+                * For storage classes that store the data in memory, this is
+                * the main memory used, for storage classes storing data on disk
+                * this is the memory used on disk.
+                */
+                virtual uint64_t used_memory() const = 0;
 
-                    m_items = (TValue*) mremap(m_items, sizeof(TValue) * m_size, sizeof(TValue) * new_size, MREMAP_MAYMOVE);
-                    if (m_items == MAP_FAILED) {
-                        throw std::bad_alloc();
-                    }
-                    m_size = new_size;
-                }
-                m_items[id] = value;
-            }
+                /**
+                * Clear memory used for this storage. After this you can not
+                * use the storage container any more.
+                */
+                virtual void clear() = 0;
 
-            const TValue& operator[](uint64_t id) const {
-                return m_items[id];
-            }
-
-            uint64_t size() const {
-                return m_size;
-            }
-
-            uint64_t used_memory() const {
-                return m_size * sizeof(TValue);
-            }
-
-            void clear() {
-                munmap(m_items, sizeof(TValue) * m_size);
-            }
-
-        private:
-
-            uint64_t m_size;
-            int m_fd;
-            TValue* m_items;
-
-            /// Get file size in bytes.
-            uint64_t get_file_size() {
-                struct stat s;
-                if (fstat(m_fd, &s) < 0) {
-                    throw std::bad_alloc();
-                }
-                return s.st_size;
-            }
+            }; // class Base
 
-        }; // class Mmap
+        } // namespace ById
 
     } // namespace Storage
 
 } // namespace Osmium
 
-#endif // OSMIUM_HANDLER_STORE_HPP
+#endif // OSMIUM_STORAGE_BYID_HPP
diff --git a/include/osmium/storage/byid/fixed_array.hpp b/include/osmium/storage/byid/fixed_array.hpp
new file mode 100644
index 0000000..5db4b49
--- /dev/null
+++ b/include/osmium/storage/byid/fixed_array.hpp
@@ -0,0 +1,113 @@
+#ifndef OSMIUM_STORAGE_BYID_FIXED_ARRAY_HPP
+#define OSMIUM_STORAGE_BYID_FIXED_ARRAY_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <cstdlib>
+
+#include <osmium/storage/byid.hpp>
+
+namespace Osmium {
+
+    namespace Storage {
+
+        namespace ById {
+
+            /**
+            * The FixedArray storage stores location in a huge array. The size of
+            * the array is given when initializing the object, it must be large
+            * enough to hold all items.
+            *
+            * Only use this store when you know beforehand how many IDs there are.
+            * It is mainly provided for cases where the more flexible Mmap storage
+            * class does not work.
+            *
+            * There is no range checking on accessing the store.
+            *
+            * If you are storing node coordinates, you'll need 8 bytes for each node.
+            * At the time of writing this, the largest node ID is about 1.3 billion,
+            * so you'll need about 10 GB of memory.
+            *
+            * Note that this storage class will only work on 64 bit systems if
+            * used for storing node coordinates. 32 bit systems just can't address
+            * that much memory!
+            */
+            template <typename TValue>
+            class FixedArray : public Osmium::Storage::ById::Base<TValue> {
+
+            public:
+
+                /**
+                * Constructor.
+                *
+                * @param max_id One larger than the largest ID you will ever have.
+                * @exception std::bad_alloc Thrown when there is not enough memory.
+                */
+                FixedArray(const uint64_t max_id) :
+                    Base<TValue>(),
+                    m_size(max_id) {
+                    m_items = static_cast<TValue*>(malloc(sizeof(TValue) * max_id));
+                    if (!m_items) {
+                        throw std::bad_alloc();
+                    }
+                }
+
+                ~FixedArray() {
+                    clear();
+                }
+
+                void set(const uint64_t id, const TValue value) {
+                    m_items[id] = value;
+                }
+
+                const TValue operator[](const uint64_t id) const {
+                    return m_items[id];
+                }
+
+                uint64_t size() const {
+                    return m_size;
+                }
+
+                uint64_t used_memory() const {
+                    return m_size * sizeof(TValue);
+                }
+
+                void clear() {
+                    free(m_items);
+                    m_items = NULL;
+                }
+
+            private:
+
+                uint64_t m_size;
+
+                TValue* m_items;
+
+            }; // class FixedArray
+
+        } // namespace ById
+
+    } // namespace Storage
+
+} // namespace Osmium
+
+#endif // OSMIUM_STORAGE_BYID_FIXED_ARRAY_HPP
diff --git a/include/osmium/storage/byid/mmap_anon.hpp b/include/osmium/storage/byid/mmap_anon.hpp
new file mode 100644
index 0000000..15979c0
--- /dev/null
+++ b/include/osmium/storage/byid/mmap_anon.hpp
@@ -0,0 +1,126 @@
+#ifndef OSMIUM_STORAGE_BYID_MMAP_ANON_HPP
+#define OSMIUM_STORAGE_BYID_MMAP_ANON_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#ifdef __linux__
+
+#include <cstdlib>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <osmium/storage/byid.hpp>
+
+namespace Osmium {
+
+    namespace Storage {
+
+        namespace ById {
+
+            /**
+            * MmapAnon stores data in memory using the mmap() system call.
+            * It will grow automatically.
+            *
+            * This does not work on Mac OS X, because it doesn't support mremap().
+            * Use MmapFile or FixedArray on Mac OS X instead.
+            *
+            * If you have enough memory it is preferred to use this in-memory
+            * version. If you don't have enough memory or want the data to
+            * persist, use the file-backed version MmapFile. Note that in any
+            * case you need substantial amounts of memory for this to work
+            * efficiently.
+            */
+            template <typename TValue>
+            class MmapAnon : public Osmium::Storage::ById::Base<TValue> {
+
+            public:
+
+                static const uint64_t size_increment = 10 * 1024 * 1024;
+
+                /**
+                * Create anonymous mapping without a backing file.
+                * @exception std::bad_alloc Thrown when there is not enough memory.
+                */
+                MmapAnon() :
+                    Base<TValue>(),
+                    m_size(size_increment) {
+                    m_items = static_cast<TValue*>(mmap(NULL, sizeof(TValue) * m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+                    if (m_items == MAP_FAILED) {
+                        throw std::bad_alloc();
+                    }
+                }
+
+                ~MmapAnon() {
+                    clear();
+                }
+
+                void set(const uint64_t id, const TValue value) {
+                    if (id >= m_size) {
+                        uint64_t new_size = id + size_increment;
+
+                        m_items = static_cast<TValue*>(mremap(m_items, sizeof(TValue) * m_size, sizeof(TValue) * new_size, MREMAP_MAYMOVE));
+                        if (m_items == MAP_FAILED) {
+                            throw std::bad_alloc();
+                        }
+                        m_size = new_size;
+                    }
+                    m_items[id] = value;
+                }
+
+                const TValue operator[](const uint64_t id) const {
+                    return m_items[id];
+                }
+
+                uint64_t size() const {
+                    return m_size;
+                }
+
+                uint64_t used_memory() const {
+                    return m_size * sizeof(TValue);
+                }
+
+                void clear() {
+                    munmap(m_items, sizeof(TValue) * m_size);
+                }
+
+            private:
+
+                uint64_t m_size;
+
+                TValue* m_items;
+
+            }; // class MmapAnon
+
+        } // namespace ById
+
+    } // namespace Storage
+
+} // namespace Osmium
+
+#else
+#  warning "Osmium::Storage::ById::MmapAnon only works on Linux!"
+#endif // __linux__
+
+#endif // OSMIUM_STORAGE_BYID_MMAP_ANON_HPP
diff --git a/include/osmium/storage/byid/mmap_file.hpp b/include/osmium/storage/byid/mmap_file.hpp
new file mode 100644
index 0000000..fcd60a1
--- /dev/null
+++ b/include/osmium/storage/byid/mmap_file.hpp
@@ -0,0 +1,172 @@
+#ifndef OSMIUM_STORAGE_BYID_MMAP_FILE_HPP
+#define OSMIUM_STORAGE_BYID_MMAP_FILE_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <cstdio>
+#include <cstdlib>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string>
+
+#include <osmium/storage/byid.hpp>
+
+namespace Osmium {
+
+    namespace Storage {
+
+        namespace ById {
+
+            /**
+            * MmapFile stores data in files using the mmap() system call.
+            * It will grow automatically.
+            *
+            * If you have enough memory it is preferred to use the in-memory
+            * version MmapAnon. If you don't have enough memory or want the
+            * data to persist, use this version. Note that in any case you need
+            * substantial amounts of memory for this to work efficiently.
+            */
+            template <typename TValue>
+            class MmapFile : public Osmium::Storage::ById::Base<TValue> {
+
+            public:
+
+                static const uint64_t size_increment = 10 * 1024 * 1024;
+
+                /**
+                * Create mapping backed by file. If filename is empty, a temporary
+                * file will be created.
+                *
+                * @param filename The filename (including the path) for the storage.
+                * @param remove Should the file be removed after use?
+                * @exception std::bad_alloc Thrown when there is not enough memory or some other problem.
+                */
+                MmapFile(const std::string& filename="", bool remove=true) :
+                    Base<TValue>(),
+                    m_size(1) {
+                    if (filename == "") {
+                        FILE* file = tmpfile();
+                        if (!file) {
+                            throw std::bad_alloc();
+                        }
+                        m_fd = fileno(file);
+                    } else {
+                        m_fd = open(filename.c_str(), O_RDWR | O_CREAT, 0600);
+                    }
+
+                    if (m_fd < 0) {
+                        throw std::bad_alloc();
+                    }
+
+                    // now that the file is open we can immediately remove it
+                    // (temporary files are always removed)
+                    if (remove && filename != "") {
+                        if (unlink(filename.c_str()) < 0) {
+                            // XXX what to do here?
+                        }
+                    }
+
+                    // make sure the file is at least as large as the initial size
+                    if (get_file_size() < sizeof(TValue) * m_size) {
+                        if (ftruncate(m_fd, sizeof(TValue) * m_size) < 0) {
+                            throw std::bad_alloc();
+                        }
+                    }
+
+                    m_items = static_cast<TValue*>(mmap(NULL, sizeof(TValue) * m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0));
+                    if (m_items == MAP_FAILED) {
+                        throw std::bad_alloc();
+                    }
+                }
+
+                ~MmapFile() {
+                    clear();
+                }
+
+                void set(const uint64_t id, const TValue value) {
+                    if (id >= m_size) {
+                        uint64_t new_size = id + size_increment;
+
+                        // if the file backing this mmap is smaller than needed, increase its size
+                        if (get_file_size() < sizeof(TValue) * new_size) {
+                            if (ftruncate(m_fd, sizeof(TValue) * new_size) < 0) {
+                                throw std::bad_alloc();
+                            }
+                        }
+
+                        if (munmap(m_items, sizeof(TValue) * m_size) < 0) {
+                            throw std::bad_alloc();
+                        }
+                        m_items = static_cast<TValue*>(mmap(NULL, sizeof(TValue) * new_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0));
+                        if (m_items == MAP_FAILED) {
+                            throw std::bad_alloc();
+                        }
+                        m_size = new_size;
+                    }
+                    m_items[id] = value;
+                }
+
+                const TValue operator[](const uint64_t id) const {
+                    return m_items[id];
+                }
+
+                uint64_t size() const {
+                    return m_size;
+                }
+
+                uint64_t used_memory() const {
+                    return m_size * sizeof(TValue);
+                }
+
+                void clear() {
+                    munmap(m_items, sizeof(TValue) * m_size);
+                }
+
+            private:
+
+                uint64_t m_size;
+
+                TValue* m_items;
+
+                int m_fd;
+
+                /// Get file size in bytes.
+                uint64_t get_file_size() const {
+                    struct stat s;
+                    if (fstat(m_fd, &s) < 0) {
+                        throw std::bad_alloc();
+                    }
+                    return s.st_size;
+                }
+
+            }; // class MmapFile
+
+        } // namespace ById
+
+    } // namespace Storage
+
+} // namespace Osmium
+
+#endif // OSMIUM_STORAGE_BYID_MMAP_FILE_HPP
diff --git a/include/osmium/storage/byid/sparse_table.hpp b/include/osmium/storage/byid/sparse_table.hpp
new file mode 100644
index 0000000..0718a19
--- /dev/null
+++ b/include/osmium/storage/byid/sparse_table.hpp
@@ -0,0 +1,104 @@
+#ifndef OSMIUM_STORAGE_BYID_SPARSE_TABLE_HPP
+#define OSMIUM_STORAGE_BYID_SPARSE_TABLE_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <google/sparsetable>
+
+#include <osmium/storage/byid.hpp>
+
+namespace Osmium {
+
+    namespace Storage {
+
+        namespace ById {
+
+            /**
+            * The SparseTable store stores items in a Google sparsetable,
+            * a data structure that can hold sparsly filled tables in a
+            * very space efficient way. It will resize automatically.
+            *
+            * Use this node location store if the ID space is only sparsly
+            * populated, such as when working with smaller OSM files (like
+            * country extracts).
+            *
+            * SparseTable needs a 64bit architecture.
+            */
+            template <typename TValue>
+            class SparseTable : public Osmium::Storage::ById::Base<TValue> {
+
+            public:
+
+                /**
+                * Constructor.
+                *
+                * @param grow_size The initial size of the storage (in items).
+                *                  The storage will grow by at least this size
+                *                  every time it runs out of space.
+                */
+                SparseTable(const uint64_t grow_size=10000) :
+                    Base<TValue>(),
+                    m_grow_size(grow_size),
+                    m_items(grow_size) {
+                    assert(sizeof(typename google::sparsetable<TValue>::size_type) >= 8 && "google::sparsetable needs 64bit machine");
+                }
+
+                void set(const uint64_t id, const TValue value) {
+                    if (id >= m_items.size()) {
+                        m_items.resize(id + m_grow_size);
+                    }
+                    m_items[id] = value;
+                }
+
+                const TValue operator[](const uint64_t id) const {
+                    return m_items[id];
+                }
+
+                uint64_t size() const {
+                    return m_items.size();
+                }
+
+                uint64_t used_memory() const {
+                    // unused items use 1 bit, used items sizeof(TValue) bytes
+                    // http://google-sparsehash.googlecode.com/svn/trunk/doc/sparsetable.html
+                    return (m_items.size() / 8) + (m_items.num_nonempty() * sizeof(TValue));
+                }
+
+                void clear() {
+                    m_items.clear();
+                }
+
+            private:
+
+                uint64_t m_grow_size;
+
+                google::sparsetable<TValue> m_items;
+
+            }; // class SparseTable
+
+        } // namespace ById
+
+    } // namespace Storage
+
+} // namespace Osmium
+
+#endif // OSMIUM_STORAGE_BYID_SPARSE_TABLE_HPP
diff --git a/include/osmium/storage/byid/stl_map.hpp b/include/osmium/storage/byid/stl_map.hpp
new file mode 100644
index 0000000..1b43f48
--- /dev/null
+++ b/include/osmium/storage/byid/stl_map.hpp
@@ -0,0 +1,82 @@
+#ifndef OSMIUM_STORAGE_BYID_STL_MAP_HPP
+#define OSMIUM_STORAGE_BYID_STL_MAP_HPP
+
+/*
+
+Copyright 2013 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <map>
+
+#include <osmium/storage/byid.hpp>
+
+namespace Osmium {
+
+    namespace Storage {
+
+        namespace ById {
+
+            /**
+             * The StlMap storage stores location in an STL map.
+             */
+            template <typename TValue>
+            class StlMap : public Osmium::Storage::ById::Base<TValue> {
+
+            public:
+
+                StlMap() :
+                    Osmium::Storage::ById::Base<TValue>(),
+                    m_items() {
+                }
+
+                ~StlMap() {
+                }
+
+                void set(const uint64_t id, const TValue value) {
+                    m_items[id] = value;
+                }
+
+                const TValue operator[](const uint64_t id) const {
+                    return m_items.at(id);
+                }
+
+                uint64_t size() const {
+                    return m_items.size();
+                }
+
+                uint64_t used_memory() const {
+                    return 0; // XXX dummy
+                }
+
+                void clear() {
+                }
+
+            private:
+
+                std::map<uint64_t, TValue> m_items;
+
+            }; // class StlMap
+
+        } // namespace ById
+
+    } // namespace Storage
+
+} // namespace Osmium
+
+#endif // OSMIUM_STORAGE_BYID_STL_MAP_HPP
diff --git a/include/osmium/storage/byid/vector.hpp b/include/osmium/storage/byid/vector.hpp
new file mode 100644
index 0000000..f28376b
--- /dev/null
+++ b/include/osmium/storage/byid/vector.hpp
@@ -0,0 +1,119 @@
+#ifndef OSMIUM_STORAGE_BYID_VECTOR_HPP
+#define OSMIUM_STORAGE_BYID_VECTOR_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include <osmium/osm/types.hpp>
+#include <osmium/storage/byid.hpp>
+
+namespace Osmium {
+
+    namespace Storage {
+
+        namespace ById {
+
+            /**
+            * This class uses a vector of ID/Value pairs to store the
+            * data. The vector must be filled ordered by ID (OSM files
+            * are generally ordered that way, so thats usually not a
+            * problem). Lookup uses a binary search.
+            *
+            * This has very low memory overhead for small OSM datasets.
+            */
+            template <typename TValue>
+            class Vector : public Osmium::Storage::ById::Base<TValue> {
+
+                struct item_t {
+                    osm_object_id_t id;
+                    TValue value;
+
+                    item_t(osm_object_id_t i, TValue v = TValue()) :
+                        id(i),
+                        value(v) {
+                    }
+
+                    bool operator<(const item_t& other) const {
+                        return this->id < other.id;
+                    }
+
+                    bool operator==(const item_t& other) const {
+                        return this->id == other.id;
+                    }
+
+                    bool operator!=(const item_t& other) const {
+                        return !(*this == other);
+                    }
+                };
+
+                typedef std::vector<item_t> item_vector_t;
+                typedef typename item_vector_t::const_iterator item_vector_it_t;
+
+            public:
+
+                Vector() :
+                    Base<TValue>(),
+                    m_items() {
+                }
+
+                void set(const uint64_t id, const TValue value) {
+                    m_items.push_back(item_t(id, value));
+                }
+
+                const TValue operator[](const uint64_t id) const {
+                    const item_t item(id);
+                    const item_vector_it_t result = std::lower_bound(m_items.begin(), m_items.end(), item);
+                    if (result == m_items.end() || *result != item) {
+                        return TValue(); // nothing found
+                    } else {
+                        return result->value;
+                    }
+                }
+
+                uint64_t size() const {
+                    return m_items.size();
+                }
+
+                uint64_t used_memory() const {
+                    return size() * sizeof(item_t);
+                }
+
+                void clear() {
+                    item_vector_t().swap(m_items);
+                }
+
+            private:
+
+                item_vector_t m_items;
+
+            }; // class Vector
+
+        } // namespace ById
+
+    } // namespace Storage
+
+} // namespace Osmium
+
+#endif // OSMIUM_STORAGE_BYID_VECTOR_HPP
diff --git a/include/osmium/storage/objectstore.hpp b/include/osmium/storage/objectstore.hpp
index 2971866..628d23b 100644
--- a/include/osmium/storage/objectstore.hpp
+++ b/include/osmium/storage/objectstore.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,6 +22,7 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#include <algorithm>
 #include <set>
 #include <boost/bind.hpp>
 
@@ -34,6 +35,7 @@ namespace Osmium {
         /**
          * Stores Nodes, Ways, and Relations in main memory. Can store multiple
          * versions of the same object. Storage is ordered by id and version.
+         * Stored objects are const, they can not be changed.
          *
          * The object store uses the handler interface, so storage is simply by
          * calling the node(), way(), and relation() methods like in any other
@@ -47,7 +49,11 @@ namespace Osmium {
 
         public:
 
-            ObjectStore() : Base(), m_nodes(), m_ways(), m_relations() {
+            ObjectStore() :
+                Base(),
+                m_nodes(),
+                m_ways(),
+                m_relations() {
             }
 
             /**
@@ -167,72 +173,72 @@ namespace Osmium {
 
             public:
 
-                ApplyHandler(ObjectStore& os, THandler* handler, Osmium::OSM::Meta& meta) :
+                ApplyHandler(ObjectStore& object_store, THandler& handler, Osmium::OSM::Meta& meta) :
                     Osmium::Handler::Forward<THandler>(handler),
-                    m_object_store(os),
+                    m_object_store(object_store),
                     m_handler(handler),
                     m_meta(meta),
-                    m_nodes_iter(os.m_nodes.begin()),
-                    m_nodes_end(os.m_nodes.end()),
-                    m_ways_iter(os.m_ways.begin()),
-                    m_ways_end(os.m_ways.end()),
-                    m_relations_iter(os.m_relations.begin()),
-                    m_relations_end(os.m_relations.end()) {
+                    m_nodes_iter(object_store.m_nodes.begin()),
+                    m_nodes_end(object_store.m_nodes.end()),
+                    m_ways_iter(object_store.m_ways.begin()),
+                    m_ways_end(object_store.m_ways.end()),
+                    m_relations_iter(object_store.m_relations.begin()),
+                    m_relations_end(object_store.m_relations.end()) {
                 }
 
                 void init(const Osmium::OSM::Meta&) {
-                    m_handler->init(m_meta);
+                    m_handler.init(m_meta);
                 }
 
                 void node(const shared_ptr<Osmium::OSM::Node>& node) {
                     while (m_nodes_iter != m_nodes_end && **m_nodes_iter < *node) {
-                        m_handler->node(*m_nodes_iter++);
+                        m_handler.node(*m_nodes_iter++);
                     }
-                    m_handler->node(node);
+                    m_handler.node(node);
                 }
 
                 void after_nodes() {
                     while (m_nodes_iter != m_nodes_end) {
-                        m_handler->node(*m_nodes_iter++);
+                        m_handler.node(*m_nodes_iter++);
                     }
-                    m_handler->after_nodes();
+                    m_handler.after_nodes();
                     m_object_store.clear_nodes();
                 }
 
                 void way(const shared_ptr<Osmium::OSM::Way>& way) {
                     while (m_ways_iter != m_ways_end && **m_ways_iter < *way) {
-                        m_handler->way(*m_ways_iter++);
+                        m_handler.way(*m_ways_iter++);
                     }
-                    m_handler->way(way);
+                    m_handler.way(way);
                 }
 
                 void after_ways() {
                     while (m_ways_iter != m_ways_end) {
-                        m_handler->way(*m_ways_iter++);
+                        m_handler.way(*m_ways_iter++);
                     }
-                    m_handler->after_ways();
+                    m_handler.after_ways();
                     m_object_store.clear_ways();
                 }
 
                 void relation(const shared_ptr<Osmium::OSM::Relation>& relation) {
                     while (m_relations_iter != m_relations_end && **m_relations_iter < *relation) {
-                        m_handler->relation(*m_relations_iter++);
+                        m_handler.relation(*m_relations_iter++);
                     }
-                    m_handler->relation(relation);
+                    m_handler.relation(relation);
                 }
 
                 void after_relations() {
                     while (m_relations_iter != m_relations_end) {
-                        m_handler->relation(*m_relations_iter++);
+                        m_handler.relation(*m_relations_iter++);
                     }
-                    m_handler->after_relations();
+                    m_handler.after_relations();
                     m_object_store.clear_relations();
                 }
 
             private:
 
                 ObjectStore& m_object_store;
-                THandler* m_handler;
+                THandler& m_handler;
                 Osmium::OSM::Meta& m_meta;
 
                 ObjectStore::nodeset::iterator     m_nodes_iter;
diff --git a/include/osmium/tags/key_filter.hpp b/include/osmium/tags/key_filter.hpp
new file mode 100644
index 0000000..1cfb52f
--- /dev/null
+++ b/include/osmium/tags/key_filter.hpp
@@ -0,0 +1,82 @@
+#ifndef OSMIUM_TAGS_KEY_FILTER_HPP
+#define OSMIUM_TAGS_KEY_FILTER_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <functional>
+#include <vector>
+#include <boost/foreach.hpp>
+#include <boost/iterator/filter_iterator.hpp>
+
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/tag_list.hpp>
+
+namespace Osmium {
+
+    namespace Tags {
+
+        class KeyFilter : public std::unary_function<const Osmium::OSM::Tag&, bool> {
+
+            struct rule_t {
+                bool result;
+                std::string key;
+
+                rule_t(bool r, const char* k) :
+                    result(r),
+                    key(k) {
+                }
+
+            };
+
+            std::vector<rule_t> m_rules;
+            bool m_default_result;
+
+        public:
+
+            typedef boost::filter_iterator<KeyFilter, Osmium::OSM::TagList::const_iterator> iterator;
+
+            KeyFilter(bool default_result) :
+                m_rules(),
+                m_default_result(default_result) {
+            }
+
+            KeyFilter& add(bool result, const char* key) {
+                m_rules.push_back(rule_t(result, key));
+                return *this;
+            }
+
+            bool operator()(const Osmium::OSM::Tag& tag) const {
+                BOOST_FOREACH(const rule_t& rule, m_rules) {
+                    if (tag.key() == rule.key) {
+                        return rule.result;
+                    }
+                }
+                return m_default_result;
+            }
+
+        };
+
+    } // namespace Tags
+
+} // namespace Osmium
+
+#endif // OSMIUM_TAGS_KEY_FILTER_HPP
diff --git a/include/osmium/tags/key_value_filter.hpp b/include/osmium/tags/key_value_filter.hpp
new file mode 100644
index 0000000..66714db
--- /dev/null
+++ b/include/osmium/tags/key_value_filter.hpp
@@ -0,0 +1,84 @@
+#ifndef OSMIUM_TAGS_KEY_VALUE_FILTER_HPP
+#define OSMIUM_TAGS_KEY_VALUE_FILTER_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <functional>
+#include <vector>
+#include <boost/foreach.hpp>
+#include <boost/iterator/filter_iterator.hpp>
+
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/tag_list.hpp>
+
+namespace Osmium {
+
+    namespace Tags {
+
+        class KeyValueFilter : public std::unary_function<const Osmium::OSM::Tag&, bool> {
+
+            struct rule_t {
+                bool result;
+                std::string key;
+                std::string value;
+
+                rule_t(bool r, const char* k, const char* v) :
+                    result(r),
+                    key(k),
+                    value(v ? v : "") {
+                }
+
+            };
+
+            std::vector<rule_t> m_rules;
+            bool m_default_result;
+
+        public:
+
+            typedef boost::filter_iterator<KeyValueFilter, Osmium::OSM::TagList::const_iterator> iterator;
+
+            KeyValueFilter(bool default_result) :
+                m_rules(),
+                m_default_result(default_result) {
+            }
+
+            KeyValueFilter& add(bool result, const char* key, const char* value = NULL) {
+                m_rules.push_back(rule_t(result, key, value));
+                return *this;
+            }
+
+            bool operator()(const Osmium::OSM::Tag& tag) const {
+                BOOST_FOREACH(const rule_t &rule, m_rules) {
+                    if (tag.key() == rule.key && (rule.value.empty() || tag.value() == rule.value)) {
+                        return rule.result;
+                    }
+                }
+                return m_default_result;
+            }
+
+        };
+
+    } // namespace Tags
+
+} // namespace Osmium
+
+#endif // OSMIUM_TAGS_KEY_VALUE_FILTER_HPP
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/tags/regex_filter.hpp
new file mode 100644
index 0000000..ba3e780
--- /dev/null
+++ b/include/osmium/tags/regex_filter.hpp
@@ -0,0 +1,90 @@
+#ifndef OSMIUM_TAGS_REGEX_FILTER_HPP
+#define OSMIUM_TAGS_REGEX_FILTER_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#define OSMIUM_LINK_WITH_LIBS_GEOS -lboost_regex
+
+#include <functional>
+#include <vector>
+#include <boost/foreach.hpp>
+#include <boost/iterator/filter_iterator.hpp>
+#include <boost/regex.hpp>
+
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/tag_list.hpp>
+
+namespace Osmium {
+
+    namespace Tags {
+
+        class RegexFilter : public std::unary_function<const Osmium::OSM::Tag&, bool> {
+
+            struct rule_t {
+                bool result;
+                boost::regex key;
+                boost::regex value;
+
+                rule_t(bool r, const char* k, const char* v) :
+                    result(r),
+                    key(k),
+                    value() {
+                    if (v) {
+                        value = v;
+                    }
+                }
+
+            };
+
+            std::vector<rule_t> m_rules;
+            bool m_default_result;
+
+        public:
+
+            typedef boost::filter_iterator<RegexFilter, Osmium::OSM::TagList::const_iterator> iterator;
+
+            RegexFilter(bool default_result) :
+                m_rules(),
+                m_default_result(default_result) {
+            }
+
+            RegexFilter& add(bool result, const char* key, const char* value = NULL) {
+                m_rules.push_back(rule_t(result, key, value));
+                return *this;
+            }
+
+            bool operator()(const Osmium::OSM::Tag& tag) const {
+                BOOST_FOREACH(const rule_t &rule, m_rules) {
+                    if (boost::regex_match(tag.key(), rule.key) && (rule.value.empty() || boost::regex_match(tag.value(), rule.value))) {
+                        return rule.result;
+                    }
+                }
+                return m_default_result;
+            }
+
+        };
+
+    } // namespace Tags
+
+} // namespace Osmium
+
+#endif // OSMIUM_TAGS_REGEX_FILTER_HPP
diff --git a/include/osmium/tags/to_string.hpp b/include/osmium/tags/to_string.hpp
new file mode 100644
index 0000000..bbcdf60
--- /dev/null
+++ b/include/osmium/tags/to_string.hpp
@@ -0,0 +1,116 @@
+#ifndef OSMIUM_TAGS_TAG_LIST_TO_STRING_HPP
+#define OSMIUM_TAGS_TAG_LIST_TO_STRING_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <functional>
+#include <string>
+
+#include <osmium/osm/tag.hpp>
+
+namespace Osmium {
+
+    namespace Tags {
+
+        /**
+         * Operation that turns a Tag into a string with many parameters that define how this is done.
+         *
+         * @param escape String that contains all characters that should be escaped with a backslash (\)
+         * @param prefix String printed before a tag.
+         * @param infix String printed between key and value of a tag.
+         * @param suffix String printed after a tag.
+         * @param join String used to join several tags together.
+         */
+        class TagToStringOp : public std::binary_function<std::string&, const Osmium::OSM::Tag&, std::string&> {
+
+        public:
+
+            TagToStringOp(const std::string& escape, const char* prefix, const char* infix, const char* suffix, const char* join) :
+                m_escape(escape),
+                m_prefix(prefix),
+                m_infix(infix),
+                m_suffix(suffix),
+                m_join(join) {
+            }
+
+            std::string& operator()(std::string& output, const Osmium::OSM::Tag& tag) const {
+                if (!output.empty()) {
+                    output.append(m_join);
+                }
+                output.append(m_prefix);
+                append_escaped_string(output, tag.key());
+                output.append(m_infix);
+                append_escaped_string(output, tag.value());
+                output.append(m_suffix);
+                return output;
+            }
+
+        private:
+
+            const std::string m_escape;
+            const char* m_prefix;
+            const char* m_infix;
+            const char* m_suffix;
+            const char* m_join;
+
+            void append_escaped_string(std::string& output, const char* in) const {
+                while (*in) {
+                    if (m_escape.find(*in) != std::string::npos) {
+                        output.append(1, '\\');
+                    }
+                    output.append(1, *in++);
+                }
+            }
+
+        }; // class TagToStringOp
+
+        /**
+         * Operation that turns a Tag into a string in the format "key=value".
+         */
+        class TagToKeyEqualsValueStringOp : public TagToStringOp {
+
+        public:
+
+            TagToKeyEqualsValueStringOp(const char* join) :
+                TagToStringOp("", "", "=", "", join) {
+            }
+
+        }; // class TagToKeyEqualsValueStringOp
+
+        /**
+         * Operation that turns a Tag into a string in the format used for the hstore PostgreSQL extension.
+         */
+        class TagToHStoreStringOp : public TagToStringOp {
+
+        public:
+
+            TagToHStoreStringOp() :
+                TagToStringOp("\\\"", "\"", "\"=>\"", "\"", ",") {
+            }
+
+        }; // class TagToHStoreStringOp
+
+    } // namespace Tags
+
+} // namespace Osmium
+
+#endif // OSMIUM_TAGS_TAG_LIST_TO_STRING_HPP
diff --git a/include/osmium/utils/delta.hpp b/include/osmium/utils/delta.hpp
index e4d0291..3ccb9df 100644
--- a/include/osmium/utils/delta.hpp
+++ b/include/osmium/utils/delta.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -31,12 +31,13 @@ namespace Osmium {
      * it was last set to and returns the delta between old and
      * new value from the update() call.
      */
-    template<typename T>
+    template <typename T>
     class Delta {
 
     public:
 
-        Delta() : m_value(0) {
+        Delta() :
+            m_value(0) {
         }
 
         void clear() {
diff --git a/include/osmium/utils/filter_and_accumulate.hpp b/include/osmium/utils/filter_and_accumulate.hpp
new file mode 100644
index 0000000..1d8c3c5
--- /dev/null
+++ b/include/osmium/utils/filter_and_accumulate.hpp
@@ -0,0 +1,48 @@
+#ifndef OSMIUM_UTILS_FILTER_AND_ACCUMULATE_HPP
+#define OSMIUM_UTILS_FILTER_AND_ACCUMULATE_HPP
+
+/*
+
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
+
+This file is part of Osmium (https://github.com/joto/osmium).
+
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
+
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
+
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#include <numeric>
+
+namespace Osmium {
+
+    /**
+     * Similar to the std::accumulate function, but first filters while
+     * iterating over the container.
+     *
+     * @param container Some container class, must support begin() and end() functions.
+     * @param filter Filter class, must support operator() that takes a member of the container and returns bool.
+     * @param init Initial value for accumulation.
+     * @param binary_op Operation called when accumulating.
+     */
+    template <class TContainer, class TFilter, class TAccum, class TBinaryOp>
+    inline TAccum filter_and_accumulate(TContainer& container, TFilter& filter, const TAccum& init, TBinaryOp binary_op) {
+        typename TFilter::iterator fi_begin(filter, container.begin(), container.end());
+        typename TFilter::iterator fi_end(filter, container.end(), container.end());
+
+        return std::accumulate(fi_begin, fi_end, init, binary_op);
+    }
+
+} // namespace Osmium
+
+#endif // OSMIUM_UTILS_FILTER_AND_ACCUMULATE_HPP
diff --git a/include/osmium/utils/sqlite.hpp b/include/osmium/utils/sqlite.hpp
deleted file mode 100644
index f54c2b2..0000000
--- a/include/osmium/utils/sqlite.hpp
+++ /dev/null
@@ -1,195 +0,0 @@
-#ifndef OSMIUM_UTILS_SQLITE_HPP
-#define OSMIUM_UTILS_SQLITE_HPP
-
-/*
-
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
-
-This file is part of Osmium (https://github.com/joto/osmium).
-
-Osmium is free software: you can redistribute it and/or modify it under the
-terms of the GNU Lesser General Public License or (at your option) the GNU
-General Public License as published by the Free Software Foundation, either
-version 3 of the Licenses, or (at your option) any later version.
-
-Osmium 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 Lesser General Public License and the GNU
-General Public License for more details.
-
-You should have received a copy of the Licenses along with Osmium. If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdexcept>
-#include <string>
-#include <iostream>
-
-#include <sqlite3.h>
-
-namespace Osmium {
-
-    /**
-    *  @brief The %Sqlite classes wrap the %Sqlite C library.
-    */
-    namespace Sqlite {
-
-        /**
-        *  Exception returned by Sqlite wrapper classes when there are errors in the Sqlite3 lib
-        */
-        class Exception : public std::runtime_error {
-
-        public:
-
-            Exception(const std::string &msg, const std::string &error) : std::runtime_error(msg + ": " + error + '\n') {
-            }
-
-        };
-
-        class Statement;
-
-        /**
-        *  Wrapper class for Sqlite database
-        */
-        class Database {
-
-        private:
-
-            sqlite3* db;
-
-        public:
-
-            Database(const char* filename) {
-                if (sqlite3_open_v2(filename, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0)) {
-                    sqlite3_close(db);
-                    throw Sqlite::Exception("Can't open database", errmsg());
-                }
-            }
-
-            ~Database() {
-                sqlite3_close(db);
-            }
-
-            const std::string &errmsg() const {
-                static std::string error = std::string(sqlite3_errmsg(db));
-                return error;
-            }
-
-            sqlite3* get_sqlite3() {
-                return db;
-            }
-
-            void begin_transaction() {
-                if (SQLITE_OK != sqlite3_exec(db, "BEGIN TRANSACTION;", 0, 0, 0)) {
-                    std::cerr << "Database error: " << sqlite3_errmsg(db) << "\n";
-                    sqlite3_close(db);
-                    exit(1);
-                }
-            }
-
-            void commit() {
-                if (SQLITE_OK != sqlite3_exec(db, "COMMIT;", 0, 0, 0)) {
-                    std::cerr << "Database error: " << sqlite3_errmsg(db) << "\n";
-                    sqlite3_close(db);
-                    exit(1);
-                }
-            }
-
-            Statement* prepare(const char* sql);
-
-        }; // class Database
-
-        /**
-        * Wrapper class for Sqlite prepared statement.
-        */
-        class Statement {
-
-        private:
-
-            Database* db_;
-            sqlite3_stmt* statement;
-
-            int bindnum;
-
-        public:
-
-            Statement(Database* db, const char* sql) : db_(db), statement(0), bindnum(1) {
-                sqlite3_prepare_v2(db->get_sqlite3(), sql, -1, &statement, 0);
-                if (statement == 0) {
-                    throw Sqlite::Exception("Can't prepare statement", db_->errmsg());
-                }
-            }
-
-            ~Statement() {
-                sqlite3_finalize(statement);
-            }
-
-            Statement* bind_null() {
-                if (SQLITE_OK != sqlite3_bind_null(statement, bindnum++)) {
-                    throw Sqlite::Exception("Can't bind null value", db_->errmsg());
-                }
-                return this;
-            }
-
-            Statement* bind_text(const char* value) {
-                if (SQLITE_OK != sqlite3_bind_text(statement, bindnum++, value, -1, SQLITE_STATIC)) {
-                    throw Sqlite::Exception("Can't bind text value", db_->errmsg());
-                }
-                return this;
-            }
-
-            Statement* bind_text(const std::string& value) {
-                if (SQLITE_OK != sqlite3_bind_text(statement, bindnum++, value.c_str(), -1, SQLITE_STATIC)) {
-                    throw Sqlite::Exception("Can't bind text value", db_->errmsg());
-                }
-                return this;
-            }
-
-            Statement* bind_int(int value) {
-                if (SQLITE_OK != sqlite3_bind_int(statement, bindnum++, value)) {
-                    throw Sqlite::Exception("Can't bind int value", db_->errmsg());
-                }
-                return this;
-            }
-
-            Statement* bind_int64(int64_t value) {
-                if (SQLITE_OK != sqlite3_bind_int64(statement, bindnum++, value)) {
-                    throw Sqlite::Exception("Can't bind int64 value", db_->errmsg());
-                }
-                return this;
-            }
-
-            Statement* bind_double(double value) {
-                if (SQLITE_OK != sqlite3_bind_double(statement, bindnum++, value)) {
-                    throw Sqlite::Exception("Can't bind double value", db_->errmsg());
-                }
-                return this;
-            }
-
-            Statement* bind_blob(const void* value, int length) {
-                if (SQLITE_OK != sqlite3_bind_blob(statement, bindnum++, value, length, 0)) {
-                    throw Sqlite::Exception("Can't bind blob value", db_->errmsg());
-                }
-                return this;
-            }
-
-            void execute() {
-                sqlite3_step(statement);
-                if (SQLITE_OK != sqlite3_reset(statement)) {
-                    throw Sqlite::Exception("Can't execute statement", db_->errmsg());
-                }
-                bindnum = 1;
-            }
-
-        }; // class Statement
-
-        inline Statement* Database::prepare(const char* sql) {
-            return new Statement(this, sql);
-        }
-
-    } // namespace Sqlite
-
-} // namespace Osmium
-
-#endif // OSMIUM_UTILS_SQLITE_HPP
diff --git a/include/osmium/utils/stringtable.hpp b/include/osmium/utils/stringtable.hpp
index 6b25c8c..69063bd 100644
--- a/include/osmium/utils/stringtable.hpp
+++ b/include/osmium/utils/stringtable.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,13 +22,27 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#include <algorithm>
+#include <iostream>
+#include <map>
 #include <stdint.h>
 #include <string>
-#include <map>
-#include <iostream>
+#include <utility>
+#include <vector>
+
+#include <osmpbf/osmpbf.h>
 
 namespace Osmium {
 
+    namespace {
+
+        template<typename A, typename B>
+        std::pair<B,A> flip_pair(const std::pair<A,B>& p) {
+            return std::pair<B,A>(p.second, p.first);
+        }
+
+    }
+
     /**
      * StringTable management for PBF writer
      *
@@ -59,21 +73,13 @@ namespace Osmium {
          * IDs means less used space in the resulting file.
          */
         struct string_info {
-            /**
-             * number of occurrences of this string
-             */
+            /// number of occurrences of this string
             uint16_t count;
 
-            /**
-             * an intermediate-id
-             */
+            /// an intermediate-id
             string_id_t interim_id;
         };
 
-        friend bool operator<(const string_info& lhs, const string_info& rhs) {
-            return lhs.count > rhs.count;
-        }
-
         /**
          * Interim StringTable, storing all strings that should be written to
          * the StringTable once the block is written to disk.
@@ -92,7 +98,14 @@ namespace Osmium {
 
     public:
 
-        StringTable() : m_strings(), m_id2id_map(), m_size(0) {
+        StringTable() :
+            m_strings(),
+            m_id2id_map(),
+            m_size(0) {
+        }
+
+        friend bool operator<(const string_info& lhs, const string_info& rhs) {
+            return lhs.count > rhs.count;
         }
 
         /**
@@ -109,11 +122,6 @@ namespace Osmium {
             return info.interim_id;
         }
 
-        template<typename A, typename B>
-        static std::pair<B,A> flip_pair(const std::pair<A,B>& p) {
-            return std::pair<B,A>(p.second, p.first);
-        }
-
         /**
          * Sort the interim StringTable and store it to the real protobuf StringTable.
          * while storing to the real table, this function fills the id2id_map with
@@ -126,6 +134,11 @@ namespace Osmium {
          * and then by reverse lexicographic order.
          */
         void store_stringtable(OSMPBF::StringTable* st) {
+            // add empty StringTable entry at index 0
+            // StringTable index 0 is reserved as delimiter in the densenodes key/value list
+            // this line also ensures that there's always a valid StringTable
+            st->add_s("");
+
             typedef std::multimap<string_info, std::string> cmap;
             cmap sortedbycount;
 
@@ -157,6 +170,7 @@ namespace Osmium {
          */
         void clear() {
             m_strings.clear();
+            m_id2id_map.clear();
             m_size = 0;
         }
 
diff --git a/include/osmium/utils/timer.h b/include/osmium/utils/timer.h
deleted file mode 100644
index 3035e47..0000000
--- a/include/osmium/utils/timer.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * BSD licensed from http://sites.google.com/site/jivsoft/Home/ (Ken Wilder)
- */
- 
-#ifndef TIMER_H
-#define TIMER_H
-
-#include <ctime>
-#include <iostream>
-#include <iomanip>
-
-class timer
-{
-    friend std::ostream& operator<<(std::ostream& os, timer& t);
-
-    private:
-    bool running;
-    clock_t start_clock;
-    time_t start_time;
-    double acc_time;
-
-    double elapsed_time();
-
-    public:
-    // 'running' is initially false.  A timer needs to be explicitly started
-    // using 'start' or 'restart'
-    timer() : running(false), start_clock(0), start_time(0), acc_time(0) { }
-
-    void start(const char* msg = 0);
-    void restart(const char* msg = 0);
-    void stop(const char* msg = 0);
-    void check(const char* msg = 0);
-
-}; // class timer
-
-//===========================================================================
-// Return the total time that the timer has been in the "running"
-// state since it was first "started" or last "restarted".  For
-// "short" time periods (less than an hour), the actual cpu time
-// used is reported instead of the elapsed time.
-
-inline double timer::elapsed_time()
-{
-    time_t acc_sec = time(0) - start_time;
-    if (acc_sec < 3600)
-        return (clock() - start_clock) / (1.0 * CLOCKS_PER_SEC);
-    else
-        return (1.0 * acc_sec);
-
-} // timer::elapsed_time
-
-//===========================================================================
-// Start a timer.  If it is already running, let it continue running.
-// Print an optional message.
-
-inline void timer::start(const char* msg)
-{
-    // Print an optional message, something like "Starting timer t";
-    if (msg) std::cout << msg << std::endl;
-
-    // Return immediately if the timer is already running
-    if (running) return;
-
-    // Set timer status to running and set the start time
-    running = true;
-    start_clock = clock();
-    start_time = time(0);
-
-} // timer::start
-
-//===========================================================================
-// Turn the timer off and start it again from 0.  Print an optional message.
-
-inline void timer::restart(const char* msg)
-{
-    // Print an optional message, something like "Restarting timer t";
-    if (msg) std::cout << msg << std::endl;
-
-    // Set timer status to running, reset accumulated time, and set start time
-    running = true;
-    acc_time = 0;
-    start_clock = clock();
-    start_time = time(0);
-
-} // timer::restart
-
-//===========================================================================
-// Stop the timer and print an optional message.
-
-inline void timer::stop(const char* msg)
-{
-    // Print an optional message, something like "Stopping timer t";
-    if (msg) std::cout << msg << std::endl;
-
-    // Compute accumulated running time and set timer status to not running
-    if (running) acc_time += elapsed_time();
-    running = false;
-
-} // timer::stop
-
-//===========================================================================
-// Print out an optional message followed by the current timer timing.
-
-inline void timer::check(const char* msg)
-{
-    // Print an optional message, something like "Checking timer t";
-    if (msg) std::cout << msg << " : ";
-
-    std::cout << "Elapsed time [" << std::setiosflags(std::ios::fixed)
-        << std::setprecision(2)
-        << acc_time + (running ? elapsed_time() : 0) << "] seconds" << std::endl;
-
-} // timer::check
-
-//===========================================================================
-// Allow timers to be printed to ostreams using the syntax 'os << t'
-// for an ostream 'os' and a timer 't'.  For example, "cout << t" will
-// print out the total amount of time 't' has been "running".
-
-inline std::ostream& operator<<(std::ostream& os, timer& t)
-{
-    os << std::setprecision(2) << std::setiosflags(std::ios::fixed)
-        << t.acc_time + (t.running ? t.elapsed_time() : 0);
-    return os;
-}
-
-//===========================================================================
-
-#endif // TIMER_H
diff --git a/include/osmium/utils/timestamp.hpp b/include/osmium/utils/timestamp.hpp
index 8a8686a..49be3e9 100644
--- a/include/osmium/utils/timestamp.hpp
+++ b/include/osmium/utils/timestamp.hpp
@@ -3,7 +3,7 @@
 
 /*
 
-Copyright 2011 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
 This file is part of Osmium (https://github.com/joto/osmium).
 
@@ -22,62 +22,77 @@ You should have received a copy of the Licenses along with Osmium. If not, see
 
 */
 
+#include <ctime>
 #include <stdexcept>
-#include <time.h>
+#include <string>
 
 namespace Osmium {
 
-    namespace Utils {
+    /**
+     * Contains some helper functions to convert timestamps from time_t to
+     * the ISO format used by OSM and back.
+     */
+    namespace Timestamp {
 
-        /**
-         * Contains some helper functions to convert timestamps from time_t to
-         * the ISO format used by OSM and back.
-         */
-        class Timestamp {
+        namespace {
 
             static const int timestamp_length = 20 + 1; // length of ISO timestamp string yyyy-mm-ddThh:mm:ssZ\0
 
             /**
-             * The timestamp format for OSM timestamps in strftime(3) format.
-             * This is the ISO-Format yyyy-mm-ddThh:mm:ssZ
-             */
-            static const char* timestamp_format() {
+            * The timestamp format for OSM timestamps in strftime(3) format.
+            * This is the ISO-Format yyyy-mm-ddThh:mm:ssZ
+            */
+            inline const char* timestamp_format() {
                 static const char f[] = "%Y-%m-%dT%H:%M:%SZ";
                 return f;
             }
+        }
 
-            /// Constructor is private, this class is not supposed to be instantiated
-            Timestamp() {
+        /**
+         * Return UTC Unix time as string in ISO date/time format.
+         */
+        inline std::string to_iso(time_t timestamp) {
+            if (timestamp == 0) {
+                return std::string("");
             }
+            struct tm* tm = std::gmtime(&timestamp);
+            std::string s(timestamp_length, '\0');
+            /* This const_cast is ok, because we know we have enough space
+               in the string for the format we are using (well at least until
+               the year will have 5 digits). And by setting the size
+               afterwards from the result of strftime we make sure thats set
+               right, too. */
+            s.resize(strftime(const_cast<char*>(s.c_str()), timestamp_length, timestamp_format(), tm));
+            return s;
+        }
 
-        public:
-
-            static std::string to_iso(time_t timestamp) {
-                if (timestamp == 0) {
-                    return std::string("");
-                }
-                struct tm* tm = gmtime(&timestamp);
-                std::string s(timestamp_length, '\0');
-                /* This const_cast is ok, because we know we have enough space
-                   in the string for the format we are using (well at least until
-                   the year will have 5 digits). And by setting the size
-                   afterwards from the result of strftime we make sure thats set
-                   right, too. */
-                s.resize(strftime(const_cast<char*>(s.c_str()), timestamp_length, timestamp_format(), tm));
-                return s;
+        /**
+         * Parse ISO date/time string and return UTC unix time.
+         * Throws std::invalid_argument, if the timestamp can not be parsed.
+         */
+        inline time_t parse_iso(const char* timestamp) {
+#ifndef WIN32
+            struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+            if (strptime(timestamp, timestamp_format(), &tm) == NULL) {
+                throw std::invalid_argument("can't parse timestamp");
             }
-
-            static time_t parse_iso(const char* timestamp) {
-                struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-                if (strptime(timestamp, Osmium::Utils::Timestamp::timestamp_format(), &tm) == NULL) {
-                    throw std::invalid_argument("can't parse timestamp");
-                }
-                return timegm(&tm);
+            return timegm(&tm);
+#else
+            struct tm tm;
+            int n = sscanf(timestamp, "%4d-%2d-%2dT%2d:%2d:%2dZ", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+            if (n != 6) {
+                throw std::invalid_argument("can't parse timestamp");
             }
-
-        }; // class Timestamp
-
-    } // namespace Utils
+            tm.tm_year -= 1900;
+            tm.tm_mon--;
+            tm.tm_wday = 0;
+            tm.tm_yday = 0;
+            tm.tm_isdst = 0;
+            return _mkgmtime(&tm);
+#endif
+        }
+
+    } // namespace Timestamp
 
 } // namespace Osmium
 
diff --git a/osmjs/Makefile b/osmjs/Makefile
index 46e5c9a..9f56394 100644
--- a/osmjs/Makefile
+++ b/osmjs/Makefile
@@ -1,44 +1,46 @@
 #------------------------------------------------------------------------------
 #
-#  Osmium osmjs makefile
+#  Osmium osmjs Makefile
 #
 #------------------------------------------------------------------------------
+#
+#  You can set several environment variables before running make if you don't
+#  like the defaults:
+#
+#  CXX                - Your C++ compiler.
+#  CPLUS_INCLUDE_PATH - Include file search path.
+#  CXXFLAGS           - Extra compiler flags.
+#  LDFLAGS            - Extra linker flags.
+#  
+#------------------------------------------------------------------------------
 
-CXX = g++
-
-CXXFLAGS = -g
-#CXXFLAGS = -O3
-
-CXXFLAGS += -Wall -Wextra -Wredundant-decls -Wdisabled-optimization -pedantic
-#CXXFLAGS += -Wpadded -Winline
-
-# uncomment this if you want information on how long it took to build the multipolygons
-#CXXFLAGS += -DOSMIUM_WITH_MULTIPOLYGON_PROFILING
-
+CXXFLAGS += -O3
+#CXXFLAGS += -g
 CXXFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
-CXXFLAGS += -DOSMIUM_WITH_GEOS $(shell geos-config --cflags)
-CXXFLAGS += -DOSMIUM_WITH_SHPLIB
-CXXFLAGS += -DOSMIUM_WITH_JAVASCRIPT
+CXXFLAGS += -I../include -I/usr/include/libshp
+
+# remove this if you do not want debugging to be compiled in
+CXXFLAGS += -DOSMIUM_WITH_DEBUG
 
 # Add this to force V8 garbage collection after each node/way/relation/area callback.
 # Use only to find memory leaks. It will make osmjs really slow.
 #CXXFLAGS += -DOSMIUM_V8_FORCE_GC
 
-CXXFLAGS += -I../include
-
-LDFLAGS = -L/usr/local/lib -lexpat -lpthread
-LDFLAGS += $(shell geos-config --libs)
+CXXFLAGS_GEOS     := $(shell geos-config --cflags)
+CXXFLAGS_WARNINGS := -Wall -Wextra -Wdisabled-optimization -pedantic -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wno-long-long
 
-LIB_V8       = -lv8 -licuuc
-LIB_SHAPE    = -lshp
-LIB_PROTOBUF = -lz -lprotobuf-lite -losmpbf
+LIB_EXPAT := -lexpat
+LIB_PBF   := -lz -lpthread -lprotobuf-lite -losmpbf
+LIB_V8    := -lv8 -licuuc
+LIB_SHAPE := -lshp
+LIB_GEOS  := $(shell geos-config --libs)
 
-.PHONY: all clean install
+.PHONY: all install clean deb deb-clean
 
 all: osmjs
 
 osmjs: osmjs.cpp
-	$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIB_PROTOBUF) $(LIB_V8) $(LIB_SHAPE)
+	$(CXX) $(CXXFLAGS) $(CXXFLAGS_GEOS) $(CXXFLAGS_WARNINGS) -o $@ $< $(LDFLAGS) $(LIB_EXPAT) $(LIB_PBF) $(LIB_V8) $(LIB_SHAPE) $(LIB_GEOS)
 
 install:
 	install -m 755 -g root -o root -d $(DESTDIR)/usr/bin
diff --git a/osmjs/README b/osmjs/README
index e01eb2e..628b2f0 100644
--- a/osmjs/README
+++ b/osmjs/README
@@ -93,22 +93,22 @@ The following callbacks can be defined:
     Osmium.Callbacks.before_relations
     Osmium.Callbacks.relation
     Osmium.Callbacks.after_relations
-    Osmium.Callbacks.multipolygon
+    Osmium.Callbacks.area
     Osmium.Callbacks.end
 
 If the --2pass/-2 option is *not* given, the callbacks are called in the
 following order: init, before_nodes, node (for each node), after_nodes,
 before_ways, way (for each way), after_ways, before_relations, relation (for
-each relation), after_relations, end. The 'multipolygon' callback is never
+each relation), after_relations, end. The 'area' callback is never
 called!
 
 If the --2pass/-2 option is given, the callbacks are called in the following
-order: init, before_relations, relation (for each relation), after_relations,
-before_nodes, node (for each node), after_nodes, before_ways, way (for each
-way) / multipolygon (for each completed multipolygon), after_ways, end. The
-way and multipolygon callbacks can come in any order.
+order: init, before_nodes, node (for each node), after_nodes, before_ways,
+way (for each way) / area (for each completed multipolygon), after_ways,
+before_relations, relation (for each relation), after_relations, end. The
+way and area callbacks can come in any order.
 
-The 'node', 'way', 'relation' and 'multipolygon' callbacks will be called
+The 'node', 'way', 'relation' and 'area' callbacks will be called
 with an OSM object as 'this'.
 
 Objects have the following attributes: id, version, timestamp, uid, user,
@@ -151,7 +151,7 @@ Do not give any suffix for the filename. The suffixes '.shp', '.shx', and
 
 Define a shapefile named "example" with point geometries:
     var shp = Osmium.Output.Shapefile.open("example", "point");
-    shp.add_field("id", "integer", 10);
+    shp.add_field("id", "string", 12);
     shp.add_field("name", "string", 40);
 
 Field types are integer, string, double, and bool. Bools are always of length
@@ -176,13 +176,13 @@ Converting OSM data to Shapefiles
 There is an easier way to work with Shapefiles. If you include the osm2shp
 Javascript library from the command line, you can use a different syntax:
 
-osmjs -2 -i osm2shape -j my_config.js some_osm_file.osm.pbf
+osmjs -2 -m -l sparsetable -i osm2shape -j my_config.js some_osm_file.osm.pbf
 
 You can define shapefiles like this:
 
     shapefile('pois').
         type(POINT).
-        column('id', INTEGER, 10).
+        column('id', STRING, 12).
         column('type', STRING, 32).
         column('name', STRING, 32);
 
diff --git a/osmjs/js/callback_test.js b/osmjs/js/callback_test.js
new file mode 100644
index 0000000..8d9a4b8
--- /dev/null
+++ b/osmjs/js/callback_test.js
@@ -0,0 +1,42 @@
+// eg:
+// osmjs -l sparsetable -2 -m -j callback_test.js <osm_file>
+
+var called_node, called_way, called_relation, called_area;
+
+Osmium.Callbacks.init = function() { print('✔ init'); }
+ 
+Osmium.Callbacks.before_nodes = function() { print('✔ before_nodes'); }
+Osmium.Callbacks.node = function() { 
+    if (!called_node) {
+        print('✔ node');
+        called_node = true;
+    }
+}
+Osmium.Callbacks.after_nodes = function() { print('✔ after_nodes'); }
+ 
+Osmium.Callbacks.before_ways = function() { print('✔ before_ways'); }
+Osmium.Callbacks.way = function() {
+    if (!called_way) {
+        print('✔ way');
+        called_way = true;
+    }
+}
+Osmium.Callbacks.after_ways = function() { print('✔ after_ways'); }
+ 
+Osmium.Callbacks.before_relations = function() { print('✔ before_relations'); }
+Osmium.Callbacks.relation = function() {
+    if (!called_relation) {
+        print('✔ relation');
+        called_relation = true;
+    }
+}
+Osmium.Callbacks.after_relations = function() { print('✔ after_relations'); }
+ 
+Osmium.Callbacks.area = function() {
+    if (!called_area) {
+        print('✔ area');
+        called_area = true;
+    }
+}
+
+Osmium.Callbacks.end = function() { print('✔ end'); }
diff --git a/osmjs/js/config.js b/osmjs/js/config.js
index 5cd74c9..7d3349f 100644
--- a/osmjs/js/config.js
+++ b/osmjs/js/config.js
@@ -15,7 +15,7 @@
 
 shapefile('natural_pois').
     type(POINT).
-    column('id', INTEGER, 10).
+    column('id', STRING, 12).
     column('type', STRING, 32).
     column('name', STRING, 32);
 
diff --git a/osmjs/js/osm2csv.js b/osmjs/js/osm2csv.js
new file mode 100644
index 0000000..18b3776
--- /dev/null
+++ b/osmjs/js/osm2csv.js
@@ -0,0 +1,20 @@
+/*
+
+  Osmium Javascript Example
+
+  osm2csv.js
+
+  run with: osmjs -j osm2csv.js OSMFILE
+
+*/
+
+var out = Osmium.Output.CSV.open('out.csv');
+
+Osmium.Callbacks.node = function() {
+    out.print(this.id, this.tags['name'], this.tags['note']);
+}
+
+Osmium.Callbacks.end = function() {
+    out.close();
+}
+
diff --git a/osmjs/js/shape_export.js b/osmjs/js/shape_export.js
index c466775..5f79201 100644
--- a/osmjs/js/shape_export.js
+++ b/osmjs/js/shape_export.js
@@ -9,7 +9,7 @@
 */
 
 var shp_pois = Osmium.Output.Shapefile.open('./pois', 'point');
-shp_pois.add_field('id', 'integer', 10);
+shp_pois.add_field('id', 'string', 12);
 shp_pois.add_field('type', 'string', 32);
 shp_pois.add_field('name', 'string', 32);
 
diff --git a/osmjs/js/testgeom.js b/osmjs/js/testgeom.js
index c33f993..e43d7d0 100644
--- a/osmjs/js/testgeom.js
+++ b/osmjs/js/testgeom.js
@@ -34,11 +34,11 @@ Osmium.Callbacks.way = function() {
 
 Osmium.Callbacks.area = function() {
     print("area id:              " + this.id);
+    print("geom.toArray():       " + JSON.stringify(this.geom.toArray()));
     print("geom.toWKT():         " + this.geom.toWKT());
     print("geom.toWKT(true):     " + this.geom.toWKT(true));
     print("geom.toHexWKB():      " + this.geom.toHexWKB());
     print("geom.toHexWKB(true):  " + this.geom.toHexWKB(true));
-    print("geom.toArray():       " + JSON.stringify(this.geom.toArray()));
     print("");
 }
 
diff --git a/osmjs/osmjs.cpp b/osmjs/osmjs.cpp
index 6b46446..08f231e 100644
--- a/osmjs/osmjs.cpp
+++ b/osmjs/osmjs.cpp
@@ -1,193 +1,46 @@
+/*
 
-#include <cstdlib>
-#include <getopt.h>
-#include <unistd.h>
-
-#include <osmium.hpp>
-#include <osmium/storage/byid.hpp>
-#include <osmium/handler/coordinates_for_ways.hpp>
-#include <osmium/handler/multipolygon.hpp>
-
-#ifdef OSMIUM_WITH_MULTIPOLYGON_PROFILING
-std::vector<std::pair<std::string, timer *> > Osmium::OSM::AreaFromRelation::timers;
-
-timer Osmium::OSM::AreaFromRelation::write_complex_poly_timer;
-timer Osmium::OSM::AreaFromRelation::assemble_ways_timer;
-timer Osmium::OSM::AreaFromRelation::assemble_nodes_timer;
-timer Osmium::OSM::AreaFromRelation::make_one_ring_timer;
-timer Osmium::OSM::AreaFromRelation::mor_polygonizer_timer;
-timer Osmium::OSM::AreaFromRelation::mor_union_timer;
-timer Osmium::OSM::AreaFromRelation::contains_timer;
-timer Osmium::OSM::AreaFromRelation::extra_polygons_timer;
-timer Osmium::OSM::AreaFromRelation::polygon_build_timer;
-timer Osmium::OSM::AreaFromRelation::inner_ring_touch_timer;
-timer Osmium::OSM::AreaFromRelation::polygon_intersection_timer;
-timer Osmium::OSM::AreaFromRelation::multipolygon_build_timer;
-timer Osmium::OSM::AreaFromRelation::multipolygon_write_timer;
-timer Osmium::OSM::AreaFromRelation::error_write_timer;
-#endif // OSMIUM_WITH_MULTIPOLYGON_PROFILING
-
-typedef Osmium::Storage::ById<Osmium::OSM::Position> storage_byid_t;
-typedef Osmium::Storage::Mmap<Osmium::OSM::Position> storage_mmap_t;
-typedef Osmium::Handler::CoordinatesForWays<storage_byid_t, storage_mmap_t> cfw_handler_t;
-
-class SinglePass : public Osmium::Handler::Base {
-
-    cfw_handler_t* handler_cfw;
-    Osmium::Handler::Javascript* handler_javascript;
-
-public:
-
-    SinglePass(cfw_handler_t* cfw = NULL,
-               Osmium::Handler::Javascript* js  = NULL)
-        : Base(),
-          handler_cfw(cfw),
-          handler_javascript(js) {
-    }
-
-    void init(Osmium::OSM::Meta& meta) {
-        if (handler_cfw) {
-            handler_cfw->init(meta);
-        }
-        handler_javascript->init(meta);
-    }
-
-    void node(const shared_ptr<Osmium::OSM::Node const>& node) {
-        if (handler_cfw) {
-            handler_cfw->node(node);
-        }
-        handler_javascript->node(node);
-    }
-
-    void after_nodes() {
-        if (handler_cfw)
-            handler_cfw->after_nodes();
-    }
-
-    void way(const shared_ptr<Osmium::OSM::Way>& way) {
-        if (handler_cfw) {
-            handler_cfw->way(way);
-        }
-        handler_javascript->way(way);
-    }
-
-    void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
-        handler_javascript->relation(relation);
-    }
-
-    void final() {
-        handler_javascript->final();
-    }
-
-};
-
-class DualPass1 : public Osmium::Handler::Base {
-
-    cfw_handler_t* handler_cfw;
-    Osmium::Handler::Multipolygon* handler_multipolygon;
-    Osmium::Handler::Javascript* handler_javascript;
-
-public:
-
-    DualPass1(cfw_handler_t* cfw = NULL,
-              Osmium::Handler::Multipolygon* mp  = NULL,
-              Osmium::Handler::Javascript* js  = NULL)
-        : Base(),
-          handler_cfw(cfw),
-          handler_multipolygon(mp),
-          handler_javascript(js) {
-    }
-
-    void init(Osmium::OSM::Meta& meta) {
-        if (handler_multipolygon) {
-            handler_multipolygon->init(meta);
-        }
-        if (handler_cfw) {
-            handler_cfw->init(meta);
-        }
-        handler_javascript->init(meta);
-    }
-
-    void node(const shared_ptr<Osmium::OSM::Node const>& node) {
-        handler_javascript->node(node);
-    }
-
-    void before_relations() {
-        if (handler_multipolygon) {
-            handler_multipolygon->before_relations();
-        }
-    }
-
-    void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) {
-        if (handler_multipolygon) {
-            handler_multipolygon->relation(relation);
-        }
-        handler_javascript->relation(relation);
-    }
+Copyright 2012 Jochen Topf <jochen at topf.org> and others (see README).
 
-    void after_relations() {
-        if (handler_multipolygon) {
-            handler_multipolygon->after_relations();
-        }
-        std::cerr << "1st pass finished" << std::endl;
-    }
+This file is part of Osmium (https://github.com/joto/osmium).
 
-};
+Osmium is free software: you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License or (at your option) the GNU
+General Public License as published by the Free Software Foundation, either
+version 3 of the Licenses, or (at your option) any later version.
 
-class DualPass2 : public Osmium::Handler::Base {
+Osmium 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 Lesser General Public License and the GNU
+General Public License for more details.
 
-    cfw_handler_t* handler_cfw;
-    Osmium::Handler::Multipolygon* handler_multipolygon;
-    Osmium::Handler::Javascript* handler_javascript;
+You should have received a copy of the Licenses along with Osmium. If not, see
+<http://www.gnu.org/licenses/>.
 
-public:
-
-    DualPass2(cfw_handler_t* cfw = NULL,
-              Osmium::Handler::Multipolygon* mp  = NULL,
-              Osmium::Handler::Javascript* js  = NULL)
-        : Base(),
-          handler_cfw(cfw),
-          handler_multipolygon(mp),
-          handler_javascript(js) {
-    }
-
-    void node(const shared_ptr<Osmium::OSM::Node const>& node) {
-        if (handler_cfw) {
-            handler_cfw->node(node);
-        }
-    }
+*/
 
-    void after_nodes() {
-        if (handler_cfw) {
-            handler_cfw->after_nodes();
-        }
-    }
-
-    void way(const shared_ptr<Osmium::OSM::Way>& way) {
-        if (handler_cfw) {
-            handler_cfw->way(way);
-        }
-        if (handler_multipolygon) {
-            handler_multipolygon->way(way);
-        }
-        handler_javascript->way(way);
-    }
+#include <cstdlib>
+#include <getopt.h>
+#include <unistd.h>
 
-    void after_ways() {
-        if (handler_multipolygon) {
-            handler_multipolygon->after_ways();
-        }
-    }
+#define OSMIUM_WITH_PBF_INPUT
+#define OSMIUM_WITH_XML_INPUT
+#include <osmium.hpp>
 
-    void final() {
-        handler_javascript->final();
-        if (handler_multipolygon) {
-            handler_multipolygon->final();
-        }
-    }
-};
+#include <osmium/javascript.hpp>
+#include <osmium/storage/byid/fixed_array.hpp>
+#include <osmium/storage/byid/sparse_table.hpp>
+#include <osmium/storage/byid/mmap_file.hpp>
+#include <osmium/storage/byid/vector.hpp>
+#ifdef __linux__
+#  include <osmium/storage/byid/mmap_anon.hpp>
+#endif
+#include <osmium/handler/coordinates_for_ways.hpp>
+#include <osmium/multipolygon/assembler.hpp>
 
-/* ================================================== */
+typedef Osmium::Storage::ById::Base<Osmium::OSM::Position> storage_byid_t;
+typedef Osmium::Storage::ById::MmapFile<Osmium::OSM::Position> storage_mmap_t;
+typedef Osmium::Handler::CoordinatesForWays<storage_byid_t, storage_mmap_t> cfw_handler_t;
 
 v8::Persistent<v8::Context> global_context;
 
@@ -207,11 +60,13 @@ void print_help() {
               << "  array       - Store node locations in large array (use for large OSM files)\n"
               << "  disk        - Store node locations on disk (use when low on memory)\n"
               << "  sparsetable - Store node locations in sparse table (use for small OSM files)\n"
+              << "  vector      - Store node locations in vector of ID/Value pairs (very low memory overhead for small OSM datasets)\n"
               ;
 }
 
 std::string find_include_file(std::string filename) {
     std::vector<std::string> search_path;
+    search_path.push_back("/");
     search_path.push_back(".");
     search_path.push_back("js");
     search_path.push_back(std::string(getenv("HOME")) + "/.osmjs");
@@ -224,7 +79,7 @@ std::string find_include_file(std::string filename) {
     }
 
     // go through search path and find where the file is
-    for (std::vector<std::string>::iterator vi = search_path.begin(); vi != search_path.end(); vi++) {
+    for (std::vector<std::string>::iterator vi = search_path.begin(); vi != search_path.end(); ++vi) {
         std::string f = *vi + "/" + filename;
         if (!access(f.c_str(), R_OK)) {
             return f;
@@ -232,7 +87,7 @@ std::string find_include_file(std::string filename) {
     }
 
     std::cerr << "Could not find include file " << filename << " in search path (";
-    for (std::vector<std::string>::iterator vi = search_path.begin(); vi != search_path.end(); vi++) {
+    for (std::vector<std::string>::iterator vi = search_path.begin(); vi != search_path.end(); ++vi) {
         if (vi != search_path.begin()) std::cerr << ":";
         std::cerr << *vi;
     }
@@ -240,13 +95,7 @@ std::string find_include_file(std::string filename) {
     exit(1);
 }
 
-Osmium::Handler::Javascript* handler_javascript;
-
-void cbmp(Osmium::OSM::Area* area) {
-    handler_javascript->area(area);
-}
-
-int main(int argc, char *argv[]) {
+int main(int argc, char* argv[]) {
     std::ios_base::sync_with_stdio(false);
 
     bool two_passes = false;
@@ -258,7 +107,8 @@ int main(int argc, char *argv[]) {
         NONE,
         ARRAY,
         DISK,
-        SPARSETABLE
+        SPARSETABLE,
+        VECTOR
     } location_store = NONE;
 
     static struct option long_options[] = {
@@ -303,10 +153,12 @@ int main(int argc, char *argv[]) {
                     location_store = ARRAY;
                 } else if (!strcmp(optarg, "disk")) {
                     location_store = DISK;
+                } else if (!strcmp(optarg, "vector")) {
+                    location_store = VECTOR;
                 } else if (!strcmp(optarg, "sparsetable")) {
                     location_store = SPARSETABLE;
                 } else {
-                    std::cerr << "Unknown location store: " << optarg << " (available are: 'none, 'array', 'disk' and 'sparsetable')" << std::endl;
+                    std::cerr << "Unknown location store: " << optarg << " (available are: 'none, 'array', 'disk', 'vector' and 'sparsetable')" << std::endl;
                     exit(1);
                 }
                 break;
@@ -346,14 +198,18 @@ int main(int argc, char *argv[]) {
         std::cerr << "Warning! The command line option -2 has changed its meaning.\nIt now only enables the two-pass mode, multipolygon assembly has to be enabled with -m.\n";
     }
 
-    Osmium::init(debug);
+    if (multipolygon && location_store == NONE) {
+        std::cerr << "You need to set the location store with -l, otherwise multipolygon assembly will not work.\n";
+        exit(1);
+    }
+
     Osmium::OSMFile infile(osm_filename);
 
     v8::HandleScope handle_scope;
 
     v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
-    global_template->Set(v8::String::New("print"), v8::FunctionTemplate::New(Osmium::Handler::Javascript::Print));
-    global_template->Set(v8::String::New("include"), v8::FunctionTemplate::New(Osmium::Handler::Javascript::Include));
+    global_template->Set(v8::String::New("print"), v8::FunctionTemplate::New(Osmium::Javascript::Handler::Print));
+    global_template->Set(v8::String::New("include"), v8::FunctionTemplate::New(Osmium::Javascript::Handler::Include));
 
     global_context = v8::Persistent<v8::Context>::New(v8::Context::New(0, global_template));
     v8::Context::Scope context_scope(global_context);
@@ -368,37 +224,50 @@ int main(int argc, char *argv[]) {
     global_context->Global()->Set(v8::String::New("argv"), js_argv);
 
     storage_byid_t* store_pos = NULL;
-    if (location_store == ARRAY) {
-        store_pos = new Osmium::Storage::Mmap<Osmium::OSM::Position>();
-    } else if (location_store == DISK) {
-        std::string filename("");
-        store_pos = new Osmium::Storage::Mmap<Osmium::OSM::Position>(filename);
+    if (location_store == DISK) {
+        store_pos = new Osmium::Storage::ById::MmapFile<Osmium::OSM::Position>();
+    } else if (location_store == ARRAY) {
+#ifdef __linux__
+        store_pos = new Osmium::Storage::ById::MmapAnon<Osmium::OSM::Position>();
+#else
+        std::cerr << "Option -l array is not available on non-linux system. Use -l disk instead.\n";
+        exit(1);
+#endif
     } else if (location_store == SPARSETABLE) {
-        store_pos = new Osmium::Storage::SparseTable<Osmium::OSM::Position>();
+        store_pos = new Osmium::Storage::ById::SparseTable<Osmium::OSM::Position>();
+    } else if (location_store == VECTOR) {
+        store_pos = new Osmium::Storage::ById::Vector<Osmium::OSM::Position>();
     }
-    Osmium::Storage::Mmap<Osmium::OSM::Position> store_neg;
-    cfw_handler_t* handler_cfw = (store_pos == NULL) ? NULL : new cfw_handler_t(*store_pos, store_neg);
-    handler_javascript = new Osmium::Handler::Javascript(include_files, javascript_filename.c_str());
+    Osmium::Storage::ById::MmapFile<Osmium::OSM::Position> store_neg;
+    Osmium::Javascript::Handler handler_javascript(include_files, javascript_filename.c_str());
+    handler_javascript.set_debug_level(debug ? 1 : 0);
 
     if (two_passes) {
-        Osmium::Handler::Multipolygon* handler_multipolygon = NULL;
-        if (multipolygon) {
-            handler_multipolygon = new Osmium::Handler::Multipolygon(attempt_repair, cbmp);
-        }
-        DualPass1 handler1(handler_cfw, handler_multipolygon, handler_javascript);
-        infile.read(handler1);
-        DualPass2 handler2(handler_cfw, handler_multipolygon, handler_javascript);
-        infile.read(handler2);
-        delete handler_multipolygon;
+        typedef Osmium::MultiPolygon::Assembler<Osmium::Javascript::Handler> assembler_t;
+        assembler_t assembler(handler_javascript, attempt_repair);
+
+        cfw_handler_t handler_cfw(*store_pos, store_neg);
+
+        typedef Osmium::Handler::Sequence<cfw_handler_t, assembler_t::HandlerPass2> sequence_handler_t;
+        sequence_handler_t sequence_handler(handler_cfw, assembler.handler_pass2());
+
+        Osmium::Input::read(infile, assembler.handler_pass1());
+        Osmium::Input::read(infile, sequence_handler);
+    } else if (store_pos) {
+        cfw_handler_t handler_cfw(*store_pos, store_neg);
+
+        typedef Osmium::Handler::Sequence<cfw_handler_t, Osmium::Javascript::Handler> sequence_handler_t;
+        sequence_handler_t sequence_handler(handler_cfw, handler_javascript);
+
+        Osmium::Input::read(infile, sequence_handler);
     } else {
-        SinglePass handler(handler_cfw, handler_javascript);
-        infile.read(handler);
+        Osmium::Input::read(infile, handler_javascript);
     }
 
-    delete handler_javascript;
-    delete handler_cfw;
     delete store_pos;
 
     global_context.Dispose();
+
+    google::protobuf::ShutdownProtobufLibrary();
 }
 
diff --git a/test/.gitignore b/test/.gitignore
index 27248a8..5a8675a 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -1,2 +1,11 @@
 tmp
 tests
+test_main
+test_main_cov
+tests.info
+coverage
+gcov
+*.o
+*.ocov
+*.gcda
+*.gcno
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..4563d3d
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,82 @@
+CXX = g++
+
+CXXFLAGS += -g
+
+CXXFLAGS += -Wall -Wextra -Wdisabled-optimization -pedantic -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wno-long-long
+
+CXXFLAGS_GEOS    = $(shell geos-config --cflags)
+CXXFLAGS_LIBXML2 = $(shell xml2-config --cflags)
+CXXFLAGS_OGR     = $(shell gdal-config --cflags)
+
+CXXFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DBOOST_TEST_DYN_LINK
+CXXFLAGS += -I../include -I.
+
+# remove this if you do not want debugging to be compiled in
+CXXFLAGS += -DOSMIUM_WITH_DEBUG
+
+LIB_EXPAT  = -lexpat
+LIB_GD     = -lgd -lz -lm
+LIB_GEOS   = $(shell geos-config --libs)
+LIB_OGR    = $(shell gdal-config --libs)
+LIB_PBF    = -lz -lpthread -lprotobuf-lite -losmpbf
+LIB_SHAPE  = -lshp $(LIB_GEOS)
+LIB_SQLITE = -lsqlite3
+LIB_XML2   = $(shell xml2-config --libs)
+
+LDFLAGS += $(LIB_EXPAT) $(LIB_PBF) -lboost_unit_test_framework -lboost_regex -lboost_iostreams -lboost_filesystem -lboost_system
+
+SCAN_DIRS = \
+	t/geometry \
+	t/osm \
+	t/handler \
+	t/geometry_geos \
+	t/geometry_ogr \
+	t/osmfile \
+	t/utils \
+	t/tags \
+
+ALL_TESTS = $(shell find $(SCAN_DIRS) -name "*.cpp" | sed -e "s/.cpp$$/.o/")
+ALL_TESTS_COVERAGE = $(shell find $(SCAN_DIRS) -name "*.cpp" | sed -e "s/.cpp$$/.ocov/")
+
+.PHONY: test_main clean coverage
+
+ALL:	test_main
+	./test_main
+
+clean:
+	find . -name "*.o" -exec rm {} ";"
+	find . -name "*.ocov" -exec rm {} ";"
+	find . -name "*.gcda" -exec rm {} ";"
+	find . -name "*.gcno" -exec rm {} ";"
+	rm -rf coverage gcov tests.info test_main test_main_cov tests
+
+test_main: $(ALL_TESTS) test_main.o test_utils.o
+	$(CXX) $(LDFLAGS) $(LIB_SHAPE) $(LIB_OGR) $(LIB_GD) $(LIB_GEOS) $(LIB_XML2) -o $@ $^
+
+%.o: %.cpp
+	$(CXX) -c $(CXXFLAGS) $(CXXFLAGS_LIBXML2) $(CXXFLAGS_GEOS) $(CXXFLAGS_OGR) -o $@ $< 
+
+%.ocov: %.cpp
+	$(CXX) -c $(CXXFLAGS) $(CXXFLAGS_LIBXML2) $(CXXFLAGS_GEOS) $(CXXFLAGS_OGR) --coverage -o $@ $< 
+
+%.test: %.o test_main.o test_utils.o
+	$(CXX) $(LDFLAGS) $(LIB_SHAPE) $(LIB_OGR) $(LIB_GD) $(LIB_GEOS) $(LIB_XML2) -o $@ $< test_main.o test_utils.o
+
+test_main_cov:	$(ALL_TESTS_COVERAGE) test_main.ocov test_utils.ocov
+	$(CXX) $(LDFLAGS) $(LIB_SHAPE) $(LIB_OGR) $(LIB_GD) $(LIB_GEOS) $(LIB_XML2) --coverage -o $@ $^
+
+coverage: test_main_cov
+	lcov --zerocounters --directory .
+	#export CXXFLAGS="$(CXXFLAGS) --coverage" LDFLAGS="$(LDFLAGS) --coverage" && make ALL || echo "test errors"
+	./test_main_cov || true
+	lcov --capture --directory . --base-directory . -o tests.info
+	#lcov --extract -o tests-programcoverage 
+	genhtml -o coverage tests.info
+
+coverage-gcov: test_main_cov
+	lcov --zerocounters --directory .
+	#export CXXFLAGS="$(CXXFLAGS) --coverage" LDFLAGS="$(LDFLAGS) --coverage" && make ALL || echo "test errors"
+	./test_main_cov || true
+	find . -name "*.cpp" | xargs gcov -r -p -s ../include
+	mkdir gcov || true
+	mv *.gcov gcov/
diff --git a/test/run_tests.sh b/test/run_tests.sh
index 6fb5d25..e2f8df6 100755
--- a/test/run_tests.sh
+++ b/test/run_tests.sh
@@ -2,54 +2,121 @@
 #
 #  Compile and run unit tests
 #
-#  ./run_tests.sh [-v]                         -- compiles and runs all tests
-#  ./run_tests.sh [-v] DIR|GROUP               -- compiles and runs tests in one group
-#  ./run_tests.sh [-v] DIR|GROUP SOME_FILE.CPP -- compiles and runs only one test in one group
+#  ./run_tests.sh [-v] [-o]               -- compiles and runs all tests
+#  ./run_tests.sh [-v] [-o] SOME_FILE.CPP -- compiles and runs only one test
 #
 #  -v  -- Run tests under valgrind
+#  -o  -- Show standard output also if tests passed
 #  
 
 set -e
 
-CXX="g++"
-CXXFLAGS="-g -Wall -Wextra -Wredundant-decls -Wdisabled-optimization -pedantic -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo"
-COMPILE="$CXX -I../include -I. $CXXFLAGS -lboost_unit_test_framework -o tests test_utils.cpp"
+if [ -z "$CXX" ]; then
+    CXX="c++"
+fi
+
+if [ -z "$CXXFLAGS_WARNINGS" ]; then
+    CXXFLAGS_WARNINGS="-Wall -Wextra -Wredundant-decls -Wdisabled-optimization -pedantic -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wno-long-long"
+fi
+
+if [ -z "$CXXFLAGS" ]; then
+    CXXFLAGS="-g"
+fi
+
+COMPILE="$CXX -I../include -I. $CXXFLAGS $CXXFLAGS_WARNINGS -o tests"
 
 if [ "x$1" = "x-v" ]; then
-    VALGRIND="valgrind --leak-check=full --show-reachable=yes"
+    VALGRIND="valgrind --leak-check=full --show-reachable=yes --error-exitcode=1"
     shift
 else
     VALGRIND=""
 fi
 
-#set -x
+if [ "x$1" = "x-o" ]; then
+    ALWAYS_SHOW_OUTPUT="1"
+    shift
+else
+    ALWAYS_SHOW_OUTPUT="0"
+fi
+
+BOLD="\033[1m"
+NORM="\033[0m"
+GREEN="\033[1;32m"
+DARKRED="\033[31m"
+RED="\033[1;31m"
+
+TESTS_COMPILE_ERROR=0
+TESTS_FAILED=0
+TESTS_OK=0
+
+OPTS_CFLAGS="$(geos-config --cflags) $(gdal-config --cflags)"
+OPTS_LIBS="$(geos-config --libs) $(gdal-config --libs) -lboost_regex -lboost_iostreams -lboost_filesystem -lboost_system"
 
+test_file () {
+    FILES="test_main.o test_utils.o $1"
+    echo -n "Checking $BOLD$1$NORM..."
+    if ! output=$($COMPILE $FILES $OPTS_CFLAGS $OPTS_LIBS -DBOOST_TEST_DYN_LINK $LDFLAGS -lboost_unit_test_framework 2>&1 )
+    then
+        echo "$DARKRED[COMPILE ERROR]$NORM"
+        TESTS_COMPILE_ERROR=$(($TESTS_COMPILE_ERROR+1))
+        echo "=========================="
+        echo $COMPILE $FILES $OPTS_CFLAGS $OPTS_LIBS -DBOOST_TEST_DYN_LINK $LDFLAGS -lboost_unit_test_framework
+        echo "--------------------------"
+        echo "$output"
+        echo "=========================="
+        return
+    fi
+
+    if ! output=$($VALGRIND ./tests 2>&1 )
+    then
+        echo "$RED[TEST FAILED]$NORM"
+        TESTS_FAILED=$(($TESTS_FAILED+1))
+        echo "=========================="
+        echo "$output"
+        echo "=========================="
+        return
+    else
+        echo "$GREEN[SUCCESS]$NORM"
+        TESTS_OK=$((TESTS_OK+1))
+        if [ $ALWAYS_SHOW_OUTPUT = 1 ]
+        then
+            echo "=========================="
+            echo "$output"
+            echo "=========================="
+        fi
+    fi
+}
+
+setup() {
+    if [ \( ! -e test_main.o \) -o \( test_main.cpp -nt test_main.o \) ]
+    then
+        echo "Compiling test runner"
+        $CXX -I../include -I. $CXXFLAGS -DBOOST_TEST_DYN_LINK -c test_main.cpp 
+    fi
+    if [ \( ! -e test_utils.o \) -o \( test_utils.cpp -nt test_utils.o \) ]
+    then
+        echo "Compiling test helper"
+        $CXX -I../include -I. $CXXFLAGS -DBOOST_TEST_DYN_LINK -c test_utils.cpp
+    fi 
+}
+
+my_path=`dirname $0`
+cd $my_path
+setup
 if [ "x$1" = "x" ]; then
-    for DIR in testgroup_*; do
-        GROUP=${DIR##testgroup_}
-        echo "\nTesting group $GROUP...\n"
-        . $DIR/setup.sh
-        FILES="test_main.cpp $DIR/*/test_*.cpp"
-        echo $COMPILE $FLAGS $FILES
-        $COMPILE $FLAGS $FILES
-        $VALGRIND ./tests
+    for FILE in t/*/test_*.cpp; do
+        test_file $FILE
     done
 else
-    GROUP=${1##testgroup_}
-    DIR=testgroup_$GROUP
-    . $DIR/setup.sh
-    if [ "x$2" = "x" ]; then
-        echo "\nTesting group $GROUP...\n"
-        FILES="test_main.cpp $DIR/*/test_*.cpp"
-        echo $COMPILE $FLAGS $FILES
-        $COMPILE $FLAGS $FILES
-        $VALGRIND ./tests
-    else
-        echo "\nTesting file $2 in group $GROUP...\n"
-        FILES="-DSTAND_ALONE $DIR/$2"
-        echo $COMPILE $FLAGS $FILES
-        $COMPILE $FLAGS $FILES
-        $VALGRIND ./tests
-    fi
+    test_file $1
 fi
 
+if [ $(($TESTS_COMPILE_ERROR + $TESTS_FAILED)) = 0 ]
+then
+    echo "all tests succeeded"
+    exit 0
+else
+    echo "some tests failed"
+    echo "$TESTS_OK ok, $TESTS_COMPILE_ERROR compile error, $TESTS_FAILED fail"
+    exit 1
+fi
diff --git a/test/t/geometry/test_haversine.cpp b/test/t/geometry/test_haversine.cpp
new file mode 100644
index 0000000..a464652
--- /dev/null
+++ b/test/t/geometry/test_haversine.cpp
@@ -0,0 +1,23 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <osmium/osm.hpp>
+#include <osmium/geometry/haversine.hpp>
+
+#include "test_utils.hpp"
+
+BOOST_AUTO_TEST_SUITE(Haversine)
+
+BOOST_AUTO_TEST_CASE(Haversine) {
+    double d = Osmium::Geometry::Haversine::distance(-86.67, 36.12, -118.4, 33.94);
+    BOOST_CHECK(d - 2887259.95060711 < 0.001);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/test/testgroup_plain/geometry/test_linestring_geometry.cpp b/test/t/geometry/test_linestring_geometry.cpp
similarity index 97%
rename from test/testgroup_plain/geometry/test_linestring_geometry.cpp
rename to test/t/geometry/test_linestring_geometry.cpp
index e87543d..3e3a187 100644
--- a/test/testgroup_plain/geometry/test_linestring_geometry.cpp
+++ b/test/t/geometry/test_linestring_geometry.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -11,7 +10,7 @@
 #include <osmium/osm.hpp>
 #include <osmium/geometry/linestring.hpp>
 
-#include <test_utils.hpp>
+#include "test_utils.hpp"
 
 BOOST_AUTO_TEST_SUITE(LineStringGeometry)
 
diff --git a/test/testgroup_plain/geometry/test_point_geometry.cpp b/test/t/geometry/test_point_geometry.cpp
similarity index 96%
rename from test/testgroup_plain/geometry/test_point_geometry.cpp
rename to test/t/geometry/test_point_geometry.cpp
index 3a93171..2bc7c8c 100644
--- a/test/testgroup_plain/geometry/test_point_geometry.cpp
+++ b/test/t/geometry/test_point_geometry.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -11,7 +10,7 @@
 #include <osmium/osm.hpp>
 #include <osmium/geometry/point.hpp>
 
-#include <test_utils.hpp>
+#include "test_utils.hpp"
 
 BOOST_AUTO_TEST_SUITE(PointGeometry)
 
diff --git a/test/testgroup_plain/geometry/test_polygon_geometry.cpp b/test/t/geometry/test_polygon_geometry.cpp
similarity index 96%
rename from test/testgroup_plain/geometry/test_polygon_geometry.cpp
rename to test/t/geometry/test_polygon_geometry.cpp
index 4cc4fd3..81f9e0f 100644
--- a/test/testgroup_plain/geometry/test_polygon_geometry.cpp
+++ b/test/t/geometry/test_polygon_geometry.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -11,7 +10,7 @@
 #include <osmium/osm.hpp>
 #include <osmium/geometry/polygon.hpp>
 
-#include <test_utils.hpp>
+#include "test_utils.hpp"
 
 BOOST_AUTO_TEST_SUITE(PolygonGeometry)
 
@@ -24,7 +23,7 @@ BOOST_AUTO_TEST_CASE(instantiation) {
     wnl.add(n1);
     wnl.add(n2);
     wnl.add(n3);
-    BOOST_CHECK_THROW(Osmium::Geometry::Polygon polygon(wnl), Osmium::Exception::IllegalGeometry); // not closed
+    BOOST_CHECK_THROW(Osmium::Geometry::Polygon polygon(wnl), Osmium::Geometry::RingNotClosed);
     wnl.add(n4); // now its closed
     Osmium::Geometry::Polygon polygon(wnl);
 }
diff --git a/test/testgroup_geos/geometry/test_point_geometry.cpp b/test/t/geometry_geos/test_point_geometry.cpp
similarity index 80%
rename from test/testgroup_geos/geometry/test_point_geometry.cpp
rename to test/t/geometry_geos/test_point_geometry.cpp
index f827179..d5c7bf0 100644
--- a/test/testgroup_geos/geometry/test_point_geometry.cpp
+++ b/test/t/geometry_geos/test_point_geometry.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -6,6 +5,9 @@
 
 #include <osmium/osm/position.hpp>
 #include <osmium/geometry/point.hpp>
+#include <osmium/geometry/geos.hpp>
+
+BOOST_AUTO_TEST_SUITE(GEOS)
 
 BOOST_AUTO_TEST_SUITE(PointGeometry)
 
@@ -20,7 +22,7 @@ BOOST_AUTO_TEST_CASE(geos_geometry) {
     Osmium::OSM::Position pos1(1.2, 3.4);
     Osmium::Geometry::Point point1(pos1);
 
-    geos::geom::Point* gp = point1.create_geos_geometry();
+    geos::geom::Point* gp = Osmium::Geometry::create_geos_geometry(point1);
     BOOST_CHECK(gp);
     BOOST_CHECK_EQUAL(gp->getX(), 1.2);
     BOOST_CHECK_EQUAL(gp->getY(), 3.4);
@@ -29,3 +31,4 @@ BOOST_AUTO_TEST_CASE(geos_geometry) {
 
 BOOST_AUTO_TEST_SUITE_END()
 
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/testgroup_ogr/geometry/test_geometry.cpp b/test/t/geometry_ogr/test_geometry.cpp
similarity index 88%
rename from test/testgroup_ogr/geometry/test_geometry.cpp
rename to test/t/geometry_ogr/test_geometry.cpp
index 4d312e3..3291235 100644
--- a/test/testgroup_ogr/geometry/test_geometry.cpp
+++ b/test/t/geometry_ogr/test_geometry.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -10,6 +9,7 @@ using boost::test_tools::output_test_stream;
 #include <osmium/geometry/point.hpp>
 #include <osmium/geometry/linestring.hpp>
 #include <osmium/geometry/polygon.hpp>
+#include <osmium/geometry/ogr.hpp>
 
 BOOST_AUTO_TEST_SUITE(Geometry)
 
@@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(point_from_position) {
     Osmium::Geometry::Point point(pos);
     BOOST_CHECK_EQUAL(point.lon(), 1.2);
     BOOST_CHECK_EQUAL(point.lat(), 3.4);
-    OGRPoint* ogrpoint = point.create_ogr_geometry();
+    OGRPoint* ogrpoint = Osmium::Geometry::create_ogr_geometry(point);
     BOOST_CHECK_EQUAL(ogrpoint->getX(), 1.2);
     BOOST_CHECK_EQUAL(ogrpoint->getY(), 3.4);
     delete ogrpoint;
@@ -31,7 +31,7 @@ BOOST_AUTO_TEST_CASE(linestring_from_way) {
     wnl.add(Osmium::OSM::WayNode(1, pos1));
     wnl.add(Osmium::OSM::WayNode(2, pos2));
     Osmium::Geometry::LineString linestring(wnl);
-    OGRLineString* ogrlinestring = linestring.create_ogr_geometry();
+    OGRLineString* ogrlinestring = Osmium::Geometry::create_ogr_geometry(linestring);
     OGRPoint ogrpoint;
     ogrlinestring->StartPoint(&ogrpoint);
     BOOST_CHECK_EQUAL(ogrpoint.getX(), 1.2);
@@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(polygon_from_way) {
     wnl.add(Osmium::OSM::WayNode(2, pos2));
     wnl.add(Osmium::OSM::WayNode(1, pos1));
     Osmium::Geometry::Polygon polygon(wnl);
-    OGRPolygon* ogrpolygon = polygon.create_ogr_geometry();
+    OGRPolygon* ogrpolygon = Osmium::Geometry::create_ogr_geometry(polygon);
     std::string ogrwkb;
     ogrwkb.resize(ogrpolygon->WkbSize());
     ogrpolygon->exportToWkb(wkbNDR, (unsigned char*)ogrwkb.c_str());
diff --git a/test/t/handler/test_handler.cpp b/test/t/handler/test_handler.cpp
new file mode 100644
index 0000000..3559699
--- /dev/null
+++ b/test/t/handler/test_handler.cpp
@@ -0,0 +1,199 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+#include <boost/test/output_test_stream.hpp>
+
+#include <osmium/handler.hpp>
+#include <osmium/handler/debug.hpp>
+
+BOOST_AUTO_TEST_SUITE(Handler_Forward)
+
+BOOST_AUTO_TEST_CASE(ForwardHandler_methods_forwardAllHandlerCalls) {
+    // we use a debug handler to check the correct order of method calls
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    // this is the object under test, let it forward all calls to the debugHandler
+    Osmium::Handler::Forward<Osmium::Handler::Debug> forwardHandler(debugHandler);
+
+    // test all handler calls
+    Osmium::OSM::Meta meta;
+    forwardHandler.init(meta);
+
+    forwardHandler.before_nodes();
+    shared_ptr<Osmium::OSM::Node> node_ptr = make_shared<Osmium::OSM::Node>();
+    forwardHandler.node(node_ptr);
+    forwardHandler.after_nodes();
+
+    forwardHandler.before_ways();
+    shared_ptr<Osmium::OSM::Way> way_ptr = make_shared<Osmium::OSM::Way>();
+    forwardHandler.way(way_ptr);
+    forwardHandler.after_ways();
+
+    forwardHandler.before_relations();
+    shared_ptr<Osmium::OSM::Relation> rel_ptr = make_shared<Osmium::OSM::Relation>();
+    forwardHandler.relation(rel_ptr);
+    forwardHandler.after_relations();
+
+    forwardHandler.final();
+
+    BOOST_CHECK(output.is_equal("\
+meta:\n\
+  generator=\n\
+before_nodes\n\
+node:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  lon=214.7483647\n\
+  lat=214.7483647\n\
+after_nodes\n\
+before_ways\n\
+way:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  node_count=0\n\
+  nodes:\n\
+after_ways\n\
+before_relations\n\
+relation:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  members: (count=0)\n\
+after_relations\n\
+final\n\
+"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(Handler_Sequence)
+
+BOOST_AUTO_TEST_CASE(SequenceHandler_methods_forwardAllHandlerCallsInSequence) {
+    // we use two debug handler to check the correct order of method calls
+    boost::test_tools::output_test_stream output1, output2;
+    Osmium::Handler::Debug debugHandler1(false, output1);
+    Osmium::Handler::Debug debugHandler2(false, output2);
+
+    // this is the object under test, let it forward all calls to the debugHandler
+    Osmium::Handler::Sequence<Osmium::Handler::Debug,Osmium::Handler::Debug> sequenceHandler(debugHandler1, debugHandler2);
+
+    // test all handler calls
+    Osmium::OSM::Meta meta;
+    sequenceHandler.init(meta);
+
+    sequenceHandler.before_nodes();
+    shared_ptr<Osmium::OSM::Node> node_ptr = make_shared<Osmium::OSM::Node>();
+    sequenceHandler.node(node_ptr);
+    sequenceHandler.after_nodes();
+
+    sequenceHandler.before_ways();
+    shared_ptr<Osmium::OSM::Way> way_ptr = make_shared<Osmium::OSM::Way>();
+    sequenceHandler.way(way_ptr);
+    sequenceHandler.after_ways();
+
+    sequenceHandler.before_relations();
+    shared_ptr<Osmium::OSM::Relation> rel_ptr = make_shared<Osmium::OSM::Relation>();
+    sequenceHandler.relation(rel_ptr);
+    sequenceHandler.after_relations();
+
+    sequenceHandler.final();
+
+    BOOST_CHECK(output1.is_equal("\
+meta:\n\
+  generator=\n\
+before_nodes\n\
+node:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  lon=214.7483647\n\
+  lat=214.7483647\n\
+after_nodes\n\
+before_ways\n\
+way:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  node_count=0\n\
+  nodes:\n\
+after_ways\n\
+before_relations\n\
+relation:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  members: (count=0)\n\
+after_relations\n\
+final\n\
+"));
+    BOOST_CHECK(output2.is_equal("\
+meta:\n\
+  generator=\n\
+before_nodes\n\
+node:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  lon=214.7483647\n\
+  lat=214.7483647\n\
+after_nodes\n\
+before_ways\n\
+way:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  node_count=0\n\
+  nodes:\n\
+after_ways\n\
+before_relations\n\
+relation:\n\
+  id=0\n\
+  version=0\n\
+  uid=-1\n\
+  user=||\n\
+  changeset=0\n\
+  timestamp=\n\
+  tags: (count=0)\n\
+  members: (count=0)\n\
+after_relations\n\
+final\n\
+"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/handler/test_handler_debug.cpp b/test/t/handler/test_handler_debug.cpp
new file mode 100644
index 0000000..89c11a8
--- /dev/null
+++ b/test/t/handler/test_handler_debug.cpp
@@ -0,0 +1,268 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+#include <boost/test/output_test_stream.hpp>
+
+#include <osmium/handler/debug.hpp>
+
+BOOST_AUTO_TEST_SUITE(Handler_Debug)
+
+BOOST_AUTO_TEST_CASE(Debug_init_showsMetadata) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    Osmium::OSM::Meta meta;
+    meta.generator("unit tests");
+
+    debugHandler.init(meta);
+    BOOST_CHECK(output.is_equal("meta:\n  generator=unit tests\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_init_showsBounds) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    Osmium::OSM::Meta meta;
+    meta.generator("unit tests");
+    meta.bounds().extend(Osmium::OSM::Position(12000000, 45000000));
+    meta.bounds().extend(Osmium::OSM::Position(13000000, 46000000));
+
+    debugHandler.init(meta);
+    BOOST_CHECK(output.is_equal("meta:\n  generator=unit tests\n  bounds=(1.2,4.5,1.3,4.6)\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_beforeNodes_showsMessage) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    debugHandler.before_nodes();
+    BOOST_CHECK(output.is_equal("before_nodes\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_afterNodes_showsMessage) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    debugHandler.after_nodes();
+    BOOST_CHECK(output.is_equal("after_nodes\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_beforeWays_showsMessage) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    debugHandler.before_ways();
+    BOOST_CHECK(output.is_equal("before_ways\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_afterWays_showsMessage) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    debugHandler.after_ways();
+    BOOST_CHECK(output.is_equal("after_ways\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_beforeRelations_showsMessage) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    debugHandler.before_relations();
+    BOOST_CHECK(output.is_equal("before_relations\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_afterRelations_showsMessage) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    debugHandler.after_relations();
+    BOOST_CHECK(output.is_equal("after_relations\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_final_showsMessage) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    debugHandler.final();
+    BOOST_CHECK(output.is_equal("final\n"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_node_showsNodeData) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    shared_ptr<Osmium::OSM::Node> node_ptr = make_shared<Osmium::OSM::Node>();
+
+    node_ptr->id(12);
+    node_ptr->version(1u);
+    node_ptr->uid(13);
+    node_ptr->user("L33t User");
+    node_ptr->visible(true);
+    node_ptr->changeset(14);
+    node_ptr->timestamp((time_t)1362135600u);
+    node_ptr->position(Osmium::OSM::Position(12000000, 45000000));
+
+    debugHandler.node(node_ptr);
+    BOOST_CHECK(output.is_equal("\
+node:\n\
+  id=12\n\
+  version=1\n\
+  uid=13\n\
+  user=|L33t User|\n\
+  changeset=14\n\
+  timestamp=2013-03-01T11:00:00Z\n\
+  tags: (count=0)\n\
+  lon=1.2000000\n\
+  lat=4.5000000\n\
+"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_node_showsTagList) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    shared_ptr<Osmium::OSM::Node> node_ptr = make_shared<Osmium::OSM::Node>();
+
+    node_ptr->id(12);
+    node_ptr->version(1u);
+    node_ptr->uid(13);
+    node_ptr->user("L33t User");
+    node_ptr->visible(true);
+    node_ptr->changeset(14);
+    node_ptr->timestamp((time_t)1362135600u);
+    node_ptr->position(Osmium::OSM::Position(12000000, 45000000));
+    
+    node_ptr->tags().add("example", "one");
+    node_ptr->tags().add("example2", "two");
+
+    debugHandler.node(node_ptr);
+    BOOST_CHECK(output.is_equal("\
+node:\n\
+  id=12\n\
+  version=1\n\
+  uid=13\n\
+  user=|L33t User|\n\
+  changeset=14\n\
+  timestamp=2013-03-01T11:00:00Z\n\
+  tags: (count=2)\n\
+    k=|example| v=|one|\n\
+    k=|example2| v=|two|\n\
+  lon=1.2000000\n\
+  lat=4.5000000\n\
+"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_way_showsWayData) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    shared_ptr<Osmium::OSM::Way> way_ptr = make_shared<Osmium::OSM::Way>();
+
+    way_ptr->id(12);
+    way_ptr->version(1u);
+    way_ptr->uid(13);
+    way_ptr->user("L33t User");
+    way_ptr->visible(true);
+    way_ptr->changeset(14);
+    way_ptr->timestamp((time_t)1362135600u);
+
+    way_ptr->nodes().add(1);
+    way_ptr->nodes().add(2);
+    way_ptr->nodes().add(3);
+
+    debugHandler.way(way_ptr);
+
+    BOOST_CHECK(output.is_equal("\
+way:\n\
+  id=12\n\
+  version=1\n\
+  uid=13\n\
+  user=|L33t User|\n\
+  changeset=14\n\
+  timestamp=2013-03-01T11:00:00Z\n\
+  tags: (count=0)\n\
+  node_count=3\n\
+  nodes:\n\
+    ref=1\n\
+    ref=2\n\
+    ref=3\n\
+"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_relation_showsRelationData) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    shared_ptr<Osmium::OSM::Relation> rel_ptr = make_shared<Osmium::OSM::Relation>();
+
+    rel_ptr->id(12);
+    rel_ptr->version(1u);
+    rel_ptr->uid(13);
+    rel_ptr->user("L33t User");
+    rel_ptr->visible(true);
+    rel_ptr->changeset(14);
+    rel_ptr->timestamp((time_t)1362135600u);
+
+    rel_ptr->add_member('n', 1, "role1");
+    rel_ptr->add_member('w', 2, "role2");
+    rel_ptr->add_member('r', 3, "role3");
+
+    debugHandler.relation(rel_ptr);
+
+    BOOST_CHECK(output.is_equal("\
+relation:\n\
+  id=12\n\
+  version=1\n\
+  uid=13\n\
+  user=|L33t User|\n\
+  changeset=14\n\
+  timestamp=2013-03-01T11:00:00Z\n\
+  tags: (count=0)\n\
+  members: (count=3)\n\
+    type=n ref=1 role=|role1|\n\
+    type=w ref=2 role=|role2|\n\
+    type=r ref=3 role=|role3|\n\
+"));
+}
+
+BOOST_AUTO_TEST_CASE(Debug_node_showsVisibilityInformationIfStreamHasMultipleObjectVersions) {
+    boost::test_tools::output_test_stream output;
+    Osmium::Handler::Debug debugHandler(false, output);
+
+    shared_ptr<Osmium::OSM::Node> node_ptr = make_shared<Osmium::OSM::Node>();
+
+    node_ptr->id(12);
+    node_ptr->version(1u);
+    node_ptr->uid(13);
+    node_ptr->user("L33t User");
+    node_ptr->visible(false);
+    node_ptr->changeset(14);
+    node_ptr->timestamp((time_t)1362135600u);
+    node_ptr->endtime((time_t)1362135600u);
+    node_ptr->position(Osmium::OSM::Position(12000000, 45000000));
+
+    Osmium::OSM::Meta meta;
+    meta.generator("unit tests");
+    meta.has_multiple_object_versions(true);
+    debugHandler.init(meta);
+
+    debugHandler.node(node_ptr);
+    BOOST_CHECK(output.is_equal("\
+meta:\n\
+  generator=unit tests\n\
+node:\n\
+  id=12\n\
+  version=1\n\
+  uid=13\n\
+  user=|L33t User|\n\
+  changeset=14\n\
+  timestamp=2013-03-01T11:00:00Z\n\
+  visible=no\n\
+  endtime=2013-03-01T11:00:00Z\n\
+  tags: (count=0)\n\
+  lon=1.2000000\n\
+  lat=4.5000000\n\
+"));
+}
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/testgroup_plain/osm/test_bounds.cpp b/test/t/osm/test_bounds.cpp
similarity index 73%
rename from test/testgroup_plain/osm/test_bounds.cpp
rename to test/t/osm/test_bounds.cpp
index 71fc399..19a9e27 100644
--- a/test/testgroup_plain/osm/test_bounds.cpp
+++ b/test/t/osm/test_bounds.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -13,20 +12,20 @@ BOOST_AUTO_TEST_SUITE(Bounds)
 BOOST_AUTO_TEST_CASE(instantiation) {
     Osmium::OSM::Bounds b;
     BOOST_CHECK(!b.defined());
-    BOOST_CHECK(!b.bl().defined());
-    BOOST_CHECK(!b.tr().defined());
+    BOOST_CHECK(!b.bottom_left().defined());
+    BOOST_CHECK(!b.top_right().defined());
 }
 
 BOOST_AUTO_TEST_CASE(instantiation_and_extend) {
     Osmium::OSM::Bounds b;
     b.extend(Osmium::OSM::Position(1.2, 3.4));
     BOOST_CHECK(b.defined());
-    BOOST_CHECK(b.bl().defined());
-    BOOST_CHECK(b.tr().defined());
+    BOOST_CHECK(b.bottom_left().defined());
+    BOOST_CHECK(b.top_right().defined());
     b.extend(Osmium::OSM::Position(3.4, 4.5));
     b.extend(Osmium::OSM::Position(5.6, 7.8));
-    BOOST_CHECK_EQUAL(b.bl(), Osmium::OSM::Position(1.2, 3.4));
-    BOOST_CHECK_EQUAL(b.tr(), Osmium::OSM::Position(5.6, 7.8));
+    BOOST_CHECK_EQUAL(b.bottom_left(), Osmium::OSM::Position(1.2, 3.4));
+    BOOST_CHECK_EQUAL(b.top_right(), Osmium::OSM::Position(5.6, 7.8));
 }
 
 BOOST_AUTO_TEST_CASE(output) {
diff --git a/test/t/osm/test_node.cpp b/test/t/osm/test_node.cpp
new file mode 100644
index 0000000..2cd3f13
--- /dev/null
+++ b/test/t/osm/test_node.cpp
@@ -0,0 +1,104 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/node.hpp>
+
+BOOST_AUTO_TEST_SUITE(Node)
+
+BOOST_AUTO_TEST_CASE(instantiation_with_default_parameters) {
+    Osmium::OSM::Node n;
+    BOOST_CHECK_EQUAL(0, n.id());
+    BOOST_CHECK_EQUAL(-1, n.uid());
+}
+
+BOOST_AUTO_TEST_CASE(order) {
+    Osmium::OSM::Node n1;
+    Osmium::OSM::Node n2;
+
+    n1.id(10);
+    n1.version(1);
+    n2.id(15);
+    n2.version(2);
+    BOOST_CHECK_EQUAL(true, n1 < n2);
+    BOOST_CHECK_EQUAL(false, n1 > n2);
+
+    n1.id(20);
+    n1.version(1);
+    n2.id(20);
+    n2.version(2);
+    BOOST_CHECK_EQUAL(true, n1 < n2);
+    BOOST_CHECK_EQUAL(false, n1 > n2);
+    n1.id(-10);
+    n1.version(2);
+    n2.id(-15);
+    n2.version(1);
+    BOOST_CHECK_EQUAL(true, n1 < n2);
+    BOOST_CHECK_EQUAL(false, n1 > n2);
+}
+
+BOOST_AUTO_TEST_CASE(order_for_pointers) {
+    shared_ptr<Osmium::OSM::Node> ptr1 = make_shared<Osmium::OSM::Node>();
+    shared_ptr<Osmium::OSM::Node> ptr2 = make_shared<Osmium::OSM::Node>();
+
+    ptr1->id(10);
+    ptr1->version(1);
+    ptr2->id(15);
+    ptr2->version(2);
+
+    BOOST_CHECK_EQUAL(true, ptr1 < ptr2);
+    shared_ptr<Osmium::OSM::Node const> ptr1a = ptr1;
+    shared_ptr<Osmium::OSM::Node const> ptr2a = ptr2;
+    BOOST_CHECK_EQUAL(true, ptr1a < ptr2a);
+    //BOOST_CHECK_EQUAL(false, ptr1a > ptr2a);
+
+    ptr2->id(20);
+    ptr2->version(1);
+    ptr1->id(20);
+    ptr1->version(2);
+    //BOOST_CHECK_EQUAL(false, ptr1 < ptr2);
+    //BOOST_CHECK_EQUAL(false, ptr1 > ptr2);
+    ptr1->id(-10);
+    ptr1->version(2);
+    ptr2->id(-15);
+    ptr2->version(1);
+    BOOST_CHECK_EQUAL(true, ptr1 < ptr2);
+    //BOOST_CHECK_EQUAL(false, ptr1 > ptr2);
+}
+
+BOOST_AUTO_TEST_CASE(Node_position_setsPosition) {
+    Osmium::OSM::Node node;
+    Osmium::OSM::Position example_position(83902210,490096164);
+
+    node.position(example_position);
+    BOOST_CHECK_EQUAL(node.position().x(), 83902210);
+    BOOST_CHECK_EQUAL(node.position().y(), 490096164);
+}
+
+BOOST_AUTO_TEST_CASE(Node_type_returnsNodeType) {
+    Osmium::OSM::Node node;
+
+    BOOST_CHECK_EQUAL(node.type(), NODE);
+}
+
+BOOST_AUTO_TEST_CASE(Node_lonlatGetter_convertPositionToDouble) {
+    Osmium::OSM::Node node;
+    Osmium::OSM::Position example_position(83902210,490096164);
+
+    node.position(example_position);
+    BOOST_CHECK_CLOSE(node.lon(), 8.390221,   0.000000001);
+    BOOST_CHECK_CLOSE(node.lat(), 49.0096164, 0.000000001);
+}
+
+BOOST_AUTO_TEST_CASE(Node_lonlatSetter_convertPositionToInt) {
+    Osmium::OSM::Node node;
+    node.lon(8.390221);
+    node.lat(49.0096164);
+
+    BOOST_CHECK_EQUAL(node.position().x(), 83902210);
+    BOOST_CHECK_EQUAL(node.position().y(), 490096164);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/test/t/osm/test_object.cpp b/test/t/osm/test_object.cpp
new file mode 100644
index 0000000..f5c934a
--- /dev/null
+++ b/test/t/osm/test_object.cpp
@@ -0,0 +1,253 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/node.hpp>
+
+BOOST_AUTO_TEST_SUITE(Object)
+
+void check_taglists_for_equality(const Osmium::OSM::TagList &tl1, const Osmium::OSM::TagList &tl2) {
+    Osmium::OSM::TagList::const_iterator it1 = tl1.begin(), end1=tl1.end();
+    Osmium::OSM::TagList::const_iterator it2 = tl2.begin(), end2=tl2.end();
+
+    while ( (it1 != end1) && (it2 != end2) ) {
+        BOOST_CHECK_EQUAL(it1->key(), it2->key());
+        BOOST_CHECK_EQUAL(it1->value(), it2->value());
+        ++it1;
+        ++it2;
+    }
+    BOOST_CHECK( (it1 == end1) && (it2 == end2));
+}
+
+BOOST_AUTO_TEST_CASE(Object_id_shouldDecodeStrings) {
+    Osmium::OSM::Node obj;
+
+    obj.id("12");
+    BOOST_CHECK_EQUAL(obj.id(), 12);
+
+    obj.id("-3");
+    BOOST_CHECK_EQUAL(obj.id(), -3);
+}
+
+BOOST_AUTO_TEST_CASE(Object_version_shouldDecodeStrings) {
+    Osmium::OSM::Node obj;
+
+    obj.version("2");
+    BOOST_CHECK_EQUAL(obj.version(), 2u);
+
+    obj.version("3445");
+    BOOST_CHECK_EQUAL(obj.version(), 3445u);
+}
+
+BOOST_AUTO_TEST_CASE(Object_changeset_shouldDecodeStrings) {
+    Osmium::OSM::Node obj;
+
+    obj.changeset("2");
+    BOOST_CHECK_EQUAL(obj.changeset(), 2);
+
+    obj.changeset("3445");
+    BOOST_CHECK_EQUAL(obj.changeset(), 3445);
+}
+
+BOOST_AUTO_TEST_CASE(Object_uid_shouldDecodeStrings) {
+    Osmium::OSM::Node obj;
+
+    obj.uid("2");
+    BOOST_CHECK_EQUAL(obj.uid(), 2);
+
+    obj.uid("3445");
+    BOOST_CHECK_EQUAL(obj.uid(), 3445);
+}
+
+BOOST_AUTO_TEST_CASE(Object_userIsAnonymous_considersMinusOneAsAnonymouse) {
+    Osmium::OSM::Node obj;
+
+    obj.uid("-1");
+    BOOST_CHECK_EQUAL(obj.user_is_anonymous(), true);
+
+    obj.uid("37331");
+    BOOST_CHECK_EQUAL(obj.user_is_anonymous(), false);
+}
+
+BOOST_AUTO_TEST_CASE(Object_timestampAsString_convertsTimestampToIsoFormat) {
+    Osmium::OSM::Node obj;
+    time_t ts = 1362135600u;
+
+    obj.timestamp(ts);
+    BOOST_CHECK_EQUAL(obj.timestamp_as_string(), "2013-03-01T11:00:00Z");
+}
+
+
+BOOST_AUTO_TEST_CASE(Object_timestampAsString_considersZeroAsUnset) {
+    Osmium::OSM::Node obj;
+
+    obj.timestamp((time_t)0u);
+    BOOST_CHECK_EQUAL(obj.timestamp_as_string(), "");
+}
+
+
+BOOST_AUTO_TEST_CASE(Object_timestamp_convertsIsoFormatToTimestamp) {
+    Osmium::OSM::Node obj;
+
+    obj.timestamp("2013-03-01T11:00:00Z");
+    BOOST_CHECK_EQUAL(obj.timestamp(), (time_t)1362135600u);
+}
+
+BOOST_AUTO_TEST_CASE(Object_timestampCalledWithInvalidFormat_throwsInvalidArgument) {
+    Osmium::OSM::Node obj;
+
+    BOOST_CHECK_THROW(obj.timestamp("invalid_timestamp"), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Object_endtime_setsEndtime) {
+    Osmium::OSM::Node obj;
+    time_t ts = 1362135600u;
+
+    obj.endtime(ts);
+    BOOST_CHECK_EQUAL(obj.endtime(), ts);
+}
+
+BOOST_AUTO_TEST_CASE(Object_endtimeAsString_convertsTimestampToIsoFormat) {
+    Osmium::OSM::Node obj;
+    time_t ts = 1362135600u;
+
+    obj.endtime(ts);
+    BOOST_CHECK_EQUAL(obj.endtime_as_string(), "2013-03-01T11:00:00Z");
+}
+
+
+BOOST_AUTO_TEST_CASE(Object_endtimeAsString_considersZeroAsUnset) {
+    Osmium::OSM::Node obj;
+
+    obj.endtime((time_t)0u);
+    BOOST_CHECK_EQUAL(obj.endtime_as_string(), "");
+}
+
+
+BOOST_AUTO_TEST_CASE(Object_changeset_setsChangeset) {
+    Osmium::OSM::Node obj;
+
+    obj.changeset(14);
+    BOOST_CHECK_EQUAL(obj.changeset(), 14);
+}
+
+BOOST_AUTO_TEST_CASE(Object_uid_setsUid) {
+    Osmium::OSM::Node obj;
+
+    obj.uid(15);
+    BOOST_CHECK_EQUAL(obj.uid(), 15);
+}
+
+BOOST_AUTO_TEST_CASE(Object_user_setsUsername) {
+    Osmium::OSM::Node obj;
+
+    obj.user("L33t User");
+    BOOST_CHECK_EQUAL(obj.user(), "L33t User");
+}
+
+
+BOOST_AUTO_TEST_CASE(Object_visible_setsVisible) {
+    Osmium::OSM::Node obj;
+
+    obj.visible(false);
+    BOOST_CHECK_EQUAL(obj.visible(), false);
+
+    obj.visible(true);
+    BOOST_CHECK_EQUAL(obj.visible(), true);
+}
+
+
+BOOST_AUTO_TEST_CASE(Object_userCalledWithTooLongName_shouldThrowLenghtError) {
+    Osmium::OSM::Node obj;
+    std::string username_too_long("\
+00 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+01 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+02 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+03 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+04 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+05 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+06 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+07 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+08 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+09 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+10 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+11 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+12 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+13 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+14 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+15 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+xyz");
+
+    BOOST_CHECK_THROW(obj.user(username_too_long.c_str()), std::length_error);
+}
+
+
+BOOST_AUTO_TEST_CASE(Object_visible_shouldConvertStringValues) {
+    Osmium::OSM::Node obj;
+
+    obj.visible("true");
+    BOOST_CHECK_EQUAL(obj.visible(), true);
+
+    obj.visible("false");
+    BOOST_CHECK_EQUAL(obj.visible(), false);
+
+    obj.visible("arbitrary_string");
+    BOOST_CHECK_EQUAL(obj.visible(), true);
+}
+
+BOOST_AUTO_TEST_CASE(Object_tags_setsTagList) {
+    Osmium::OSM::Node obj;
+
+    Osmium::OSM::TagList taglist;
+    taglist.add("example", "one");
+
+    obj.tags(taglist);
+    check_taglists_for_equality(obj.tags(), taglist);
+}
+
+BOOST_AUTO_TEST_CASE(Object_setAttribute_shouldSetAttributesByName) {
+    Osmium::OSM::Node obj;
+
+    obj.set_attribute("id", "12");
+    obj.set_attribute("version", "13");
+    obj.set_attribute("changeset", "14");
+    obj.set_attribute("timestamp", "2013-03-01T11:00:00Z");
+    obj.set_attribute("uid", "15");
+    obj.set_attribute("user", "L33t User");
+    obj.set_attribute("visible", "false");
+
+    BOOST_CHECK_EQUAL(obj.id(), 12);
+    BOOST_CHECK_EQUAL(obj.version(), 13u);
+    BOOST_CHECK_EQUAL(obj.changeset(), 14);
+    BOOST_CHECK_EQUAL(obj.timestamp(), (time_t)1362135600u);
+    BOOST_CHECK_EQUAL(obj.uid(), 15);
+    BOOST_CHECK_EQUAL(obj.user(), "L33t User");
+    BOOST_CHECK_EQUAL(obj.visible(), false);
+}
+
+BOOST_AUTO_TEST_CASE(Object_copyConstructor_copiesAllAttributes) {
+    Osmium::OSM::Node src;
+
+    src.id(12);
+    src.version(13u);
+    src.changeset(14);
+    src.timestamp((time_t)1362135600u);
+    src.uid(15);
+    src.user("L33t User");
+    src.visible(false);
+    src.tags().add("example", "one");
+
+    Osmium::OSM::Node dst(src);
+    BOOST_CHECK_EQUAL(dst.id(), 12);
+    BOOST_CHECK_EQUAL(dst.version(), 13u);
+    BOOST_CHECK_EQUAL(dst.changeset(), 14);
+    BOOST_CHECK_EQUAL(dst.timestamp(), (time_t)1362135600u);
+    BOOST_CHECK_EQUAL(dst.uid(), 15);
+    BOOST_CHECK_EQUAL(dst.user(), "L33t User");
+    BOOST_CHECK_EQUAL(dst.visible(), false);
+    BOOST_CHECK_EQUAL(dst.tags()[0].key(), "example");
+    BOOST_CHECK_EQUAL(dst.tags()[0].value(), "one");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/testgroup_plain/osm/test_position.cpp b/test/t/osm/test_position.cpp
similarity index 59%
rename from test/testgroup_plain/osm/test_position.cpp
rename to test/t/osm/test_position.cpp
index 71e1a4b..317b2db 100644
--- a/test/testgroup_plain/osm/test_position.cpp
+++ b/test/t/osm/test_position.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -43,15 +42,37 @@ BOOST_AUTO_TEST_CASE(output) {
     BOOST_CHECK(out.is_equal("(-3.2,47.3)"));
 }
 
-BOOST_AUTO_TEST_CASE(conversion_to_uint32_t) {
-    Osmium::OSM::Position p1(-180.0, -90.0);
-    Osmium::OSM::Position p2(-180.0,  90.0);
-    Osmium::OSM::Position p3( 180.0,  90.0);
-    Osmium::OSM::Position p4( 180.0, -90.0);
-    BOOST_CHECK_EQUAL(64440, static_cast<uint32_t>(p1));
-    BOOST_CHECK_EQUAL(    0, static_cast<uint32_t>(p2));
-    BOOST_CHECK_EQUAL(  359, static_cast<uint32_t>(p3));
-    BOOST_CHECK_EQUAL(64799, static_cast<uint32_t>(p4));
+BOOST_AUTO_TEST_CASE(Position_constructor_initializesFrom64int) {
+    int64_t x=12000000, y=45000000;
+    Osmium::OSM::Position position(x,y);
+
+    BOOST_CHECK_EQUAL(position.x(), 12000000);
+    BOOST_CHECK_EQUAL(position.y(), 45000000);
+}
+
+BOOST_AUTO_TEST_CASE(Position_comparisonOperator_comparesFirstByxThenByyCoordinate) {
+    Osmium::OSM::Position p1, p2;
+
+    p1.x(12000000);
+    p1.y(45000000);
+    p2.x(12000000);
+    p2.y(45000000);
+    BOOST_CHECK_EQUAL(p1 < p2, false);
+    BOOST_CHECK_EQUAL(p1 > p2, false);
+
+    p1.x(12000000);
+    p1.y(45000000);
+    p2.x(13000000);
+    p2.y(44000000);
+    BOOST_CHECK_EQUAL(p1 < p2, true);
+    BOOST_CHECK_EQUAL(p1 > p2, false);
+
+    p1.x(12000000);
+    p1.y(45000000);
+    p2.x(12000000);
+    p2.y(44000000);
+    BOOST_CHECK_EQUAL(p1 < p2, false);
+    BOOST_CHECK_EQUAL(p1 > p2, true);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/osm/test_relation.cpp b/test/t/osm/test_relation.cpp
new file mode 100644
index 0000000..f507602
--- /dev/null
+++ b/test/t/osm/test_relation.cpp
@@ -0,0 +1,100 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/relation.hpp>
+
+BOOST_AUTO_TEST_SUITE(Relation)
+
+struct FilledRelationFixture {
+    FilledRelationFixture() {
+        rel.id(123);
+        rel.add_member('n', 1, "role1");
+        rel.add_member('w', 2, "role2");
+        rel.add_member('r', 3, "role3");
+    }
+
+    Osmium::OSM::Relation rel;
+};
+
+void check_relationMemberLists_for_equaltiy(const Osmium::OSM::RelationMemberList& rml1, const Osmium::OSM::RelationMemberList& rml2) {
+    Osmium::OSM::RelationMemberList::const_iterator it1 = rml1.begin();
+    Osmium::OSM::RelationMemberList::const_iterator it2 = rml2.begin();
+
+    while ((it1 != rml1.end()) && (it2 != rml2.end())) {
+        BOOST_CHECK_EQUAL(it1->ref(), it2->ref());
+        BOOST_CHECK_EQUAL(it1->type(), it2->type());
+        BOOST_CHECK_EQUAL(it1->role(), it2->role());
+        ++it1;
+        ++it2;
+    }
+    BOOST_CHECK((it1 == rml1.end()) && (it2 == rml2.end()));
+}
+
+BOOST_AUTO_TEST_CASE(Relation_constructor_createsEmptyRelation) {
+    Osmium::OSM::Relation rel;
+
+    BOOST_CHECK_EQUAL(rel.members().size(), 0u);
+}
+
+BOOST_AUTO_TEST_CASE(Relation_copyConstructor_copiesRelationContents) {
+    FilledRelationFixture fix;
+    Osmium::OSM::Relation copiedRel(fix.rel);
+
+    check_relationMemberLists_for_equaltiy(fix.rel.members(), copiedRel.members());
+    BOOST_CHECK_EQUAL(fix.rel.id(), copiedRel.id());
+}
+
+BOOST_AUTO_TEST_CASE(Relation_members_accesesMembers) {
+    FilledRelationFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.rel.members()[0].ref(), 1);
+    BOOST_CHECK_EQUAL(fix.rel.members()[0].role(), "role1");
+    BOOST_CHECK_EQUAL(fix.rel.members()[1].ref(), 2);
+    BOOST_CHECK_EQUAL(fix.rel.members()[1].role(), "role2");
+}
+
+BOOST_AUTO_TEST_CASE(Relation_type_returnsRelationType) {
+    Osmium::OSM::Relation rel;
+
+    BOOST_CHECK_EQUAL(rel.type(), RELATION);
+}
+
+BOOST_AUTO_TEST_CASE(Relation_addMember_addsMember) {
+    Osmium::OSM::Relation rel;
+
+    rel.add_member('n', 1, "role1");
+    BOOST_CHECK_EQUAL(rel.members()[0].type(), 'n');
+    BOOST_CHECK_EQUAL(rel.members()[0].ref(), 1);
+    BOOST_CHECK_EQUAL(rel.members()[0].role(), "role1");
+}
+
+BOOST_AUTO_TEST_CASE(Relation_getMember_returnsPointerToMember) {
+    FilledRelationFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.rel.get_member(0)->ref(), 1);
+    BOOST_CHECK_EQUAL(fix.rel.get_member(1)->ref(), 2);
+    BOOST_CHECK_EQUAL(fix.rel.get_member(4) == NULL, true);
+}
+
+BOOST_AUTO_TEST_CASE(Relation_comparisonOperator_comparesByIdThenByVersion) {
+    Osmium::OSM::Relation rel1, rel2;
+
+    BOOST_CHECK_EQUAL(rel1 < rel2, false);
+    BOOST_CHECK_EQUAL(rel1 > rel2, false);
+
+    rel1.id(12);
+    rel2.id(10);
+    BOOST_CHECK_EQUAL(rel1 < rel2, false);
+    BOOST_CHECK_EQUAL(rel1 > rel2, true);
+
+    rel1.id(12);
+    rel2.id(12);
+    rel1.version(1);
+    rel2.version(2);
+    BOOST_CHECK_EQUAL(rel1 < rel2, true);
+    BOOST_CHECK_EQUAL(rel1 > rel2, false);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/osm/test_relation_member.cpp b/test/t/osm/test_relation_member.cpp
new file mode 100644
index 0000000..9ccc1a0
--- /dev/null
+++ b/test/t/osm/test_relation_member.cpp
@@ -0,0 +1,82 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/relation_member.hpp>
+
+BOOST_AUTO_TEST_SUITE(RelationMember)
+
+BOOST_AUTO_TEST_CASE(RelationMember_constructor_createsEmptyRelationMember) {
+    Osmium::OSM::RelationMember relationmember;
+
+    BOOST_CHECK_EQUAL(relationmember.ref(), 0);
+    BOOST_CHECK_EQUAL(relationmember.type(), 'x');
+    BOOST_CHECK_EQUAL(relationmember.role(), "");
+}
+
+BOOST_AUTO_TEST_CASE(RelationMember_ref_changesRef) {
+    Osmium::OSM::RelationMember relationmember;
+
+    relationmember.ref(12);
+    BOOST_CHECK_EQUAL(relationmember.ref(), 12);
+}
+
+BOOST_AUTO_TEST_CASE(RelationMember_type_changesType) {
+    Osmium::OSM::RelationMember relationmember;
+
+    relationmember.type('w');
+    BOOST_CHECK_EQUAL(relationmember.type(), 'w');
+}
+
+BOOST_AUTO_TEST_CASE(RelationMember_typeName_givesTextualDescriptionOfType) {
+    Osmium::OSM::RelationMember relationmember;
+
+    BOOST_CHECK_EQUAL(relationmember.type_name(), "unknown");
+
+    relationmember.type('n');
+    BOOST_CHECK_EQUAL(relationmember.type_name(), "node");
+
+    relationmember.type('w');
+    BOOST_CHECK_EQUAL(relationmember.type_name(), "way");
+
+    relationmember.type('r');
+    BOOST_CHECK_EQUAL(relationmember.type_name(), "relation");
+
+    relationmember.type('a');
+    BOOST_CHECK_EQUAL(relationmember.type_name(), "unknown");
+}
+
+BOOST_AUTO_TEST_CASE(RelationMember_role_setsRole) {
+    Osmium::OSM::RelationMember relationmember;
+
+    relationmember.role("role1");
+    BOOST_CHECK_EQUAL(relationmember.role(), "role1");
+}
+
+BOOST_AUTO_TEST_CASE(RelationMember_role_throwsIfNameTooLong) {
+    // A role, which is too long (more than 256 4-byte characters = 1024 bytes)
+    std::string role_too_long("\
+00 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+01 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+02 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+03 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+04 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+05 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+06 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+07 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+08 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+09 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+10 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+11 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+12 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+13 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+14 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+15 456789ABCDEF 123456789ABCDEF 123456789ABCDEF 123456789ABCDEF \
+xyz");
+    Osmium::OSM::RelationMember relationmember;
+
+    BOOST_CHECK_THROW(relationmember.role(role_too_long.c_str());, std::length_error);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/osm/test_relation_member_list.cpp b/test/t/osm/test_relation_member_list.cpp
new file mode 100644
index 0000000..251bab5
--- /dev/null
+++ b/test/t/osm/test_relation_member_list.cpp
@@ -0,0 +1,100 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/relation_member_list.hpp>
+
+BOOST_AUTO_TEST_SUITE(RelationMemberList)
+
+struct FilledRelationMemberListFixture {
+    FilledRelationMemberListFixture() {
+        relationmemberlist.add_member('n', 1, "role1");
+        relationmemberlist.add_member('w', 2, "role2");
+        relationmemberlist.add_member('r', 3, "role3");
+    }
+
+    Osmium::OSM::RelationMemberList relationmemberlist;
+};
+
+BOOST_AUTO_TEST_CASE(RelationMemberList_constructor_createsEmptyList) {
+    Osmium::OSM::RelationMemberList relationmemberlist;
+
+    BOOST_CHECK_EQUAL(relationmemberlist.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RelationMemberList_size_returnsNumberOfElements) {
+    Osmium::OSM::RelationMemberList relationmemberlist;
+
+    BOOST_CHECK_EQUAL(relationmemberlist.size(), 0);
+
+    relationmemberlist.add_member('n', 1, "role1");
+    BOOST_CHECK_EQUAL(relationmemberlist.size(), 1);
+
+    relationmemberlist.add_member('w', 2, "role2");
+    BOOST_CHECK_EQUAL(relationmemberlist.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(RelationMemberList_clear_clearsList) {
+    FilledRelationMemberListFixture fix;
+
+    fix.relationmemberlist.clear();
+    BOOST_CHECK_EQUAL(fix.relationmemberlist.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RelationMemberList_bracketOperator_accessesElements) {
+    FilledRelationMemberListFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.relationmemberlist[0].ref(), 1);
+    BOOST_CHECK_EQUAL(fix.relationmemberlist[1].ref(), 2);
+    BOOST_CHECK_EQUAL(fix.relationmemberlist[2].ref(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(RelationMemberList_constBracketOperator_accesesElements) {
+    FilledRelationMemberListFixture fix;
+    const Osmium::OSM::RelationMemberList constrelationmemberlist = fix.relationmemberlist;
+
+    BOOST_CHECK_EQUAL(constrelationmemberlist[0].ref(), 1);
+    BOOST_CHECK_EQUAL(constrelationmemberlist[1].ref(), 2);
+    BOOST_CHECK_EQUAL(constrelationmemberlist[2].ref(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(RelationMemberList_iterator_iteratesOverList) {
+    FilledRelationMemberListFixture fix;
+    Osmium::OSM::RelationMemberList::iterator it;
+
+    it = fix.relationmemberlist.begin();
+    BOOST_CHECK_EQUAL(it->ref(), 1);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 2);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 3);
+    ++it;
+    BOOST_CHECK_EQUAL(it == fix.relationmemberlist.end(), true);
+}
+
+BOOST_AUTO_TEST_CASE(RelationMemberList_constIterator_iteratesOverList) {
+    FilledRelationMemberListFixture fix;
+    const Osmium::OSM::RelationMemberList constrelationmemberlist = fix.relationmemberlist;
+    Osmium::OSM::RelationMemberList::const_iterator it;
+
+    it = constrelationmemberlist.begin();
+    BOOST_CHECK_EQUAL(it->ref(), 1);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 2);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 3);
+    ++it;
+    BOOST_CHECK_EQUAL(it == constrelationmemberlist.end(), true);
+}
+
+BOOST_AUTO_TEST_CASE(RelationMemberList_addMember_addsNewMember) {
+    Osmium::OSM::RelationMemberList relationmemberlist;
+
+    relationmemberlist.add_member('n', 1, "role1");
+    BOOST_CHECK_EQUAL(relationmemberlist[0].type(), 'n');
+    BOOST_CHECK_EQUAL(relationmemberlist[0].ref(), 1);
+    BOOST_CHECK_EQUAL(relationmemberlist[0].role(), "role1");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/osm/test_tag_list.cpp b/test/t/osm/test_tag_list.cpp
new file mode 100644
index 0000000..c72b7fb
--- /dev/null
+++ b/test/t/osm/test_tag_list.cpp
@@ -0,0 +1,121 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+#include <inttypes.h>
+
+#include <osmium/osm/tag_list.hpp>
+
+BOOST_AUTO_TEST_SUITE(TagList)
+
+BOOST_AUTO_TEST_CASE(TagList_constructor_createsEmptyList) {
+    Osmium::OSM::TagList taglist;
+
+    BOOST_CHECK_EQUAL(taglist.empty(), true);
+}
+
+BOOST_AUTO_TEST_CASE(TagList_size_returnsNumberOfElements) {
+    Osmium::OSM::TagList taglist;
+
+    BOOST_CHECK_EQUAL(taglist.size(), 0);
+
+    taglist.add("entry1", "value1");
+    BOOST_CHECK_EQUAL(taglist.size(), 1);
+
+    taglist.add("entry2", "value2");
+    BOOST_CHECK_EQUAL(taglist.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(TagList_empty_returnsTrueOnEmptyList) {
+    Osmium::OSM::TagList taglist;
+
+    BOOST_CHECK_EQUAL(taglist.empty(), true);
+
+    taglist.add("entry1", "value1");
+    BOOST_CHECK_EQUAL(taglist.empty(), false);
+}
+
+BOOST_AUTO_TEST_CASE(TagList_clear_clearsList) {
+    Osmium::OSM::TagList taglist;
+
+    taglist.add("entry1", "value1");
+    taglist.clear();
+
+    BOOST_CHECK_EQUAL(taglist.empty(), true);
+}
+
+BOOST_AUTO_TEST_CASE(TagList_bracketOperator_accessesTags) {
+    Osmium::OSM::TagList taglist;
+
+    taglist.add("entry1", "value1");
+    taglist.add("entry2", "value2");
+
+    BOOST_CHECK_EQUAL(taglist[0].key(), "entry1");
+    BOOST_CHECK_EQUAL(taglist[1].key(), "entry2");
+}
+
+BOOST_AUTO_TEST_CASE(TagList_constBracketOperator_accessesTags) {
+    Osmium::OSM::TagList taglist;
+
+    taglist.add("entry1", "value1");
+    taglist.add("entry2", "value2");
+
+    const Osmium::OSM::TagList const_taglist = taglist;
+
+    BOOST_CHECK_EQUAL(const_taglist[0].key(), "entry1");
+    BOOST_CHECK_EQUAL(const_taglist[1].key(), "entry2");
+}
+
+BOOST_AUTO_TEST_CASE(TagList_iterator_accesesTags) {
+    Osmium::OSM::TagList taglist;
+
+    taglist.add("entry1", "value1");
+    taglist.add("entry2", "value2");
+
+    Osmium::OSM::TagList::iterator it = taglist.begin();
+    BOOST_CHECK_EQUAL(it->key(), "entry1");
+
+    it++;
+    BOOST_CHECK_EQUAL(it->key(), "entry2");
+
+    it++;
+    BOOST_CHECK_EQUAL(it == taglist.end(), true);
+}
+
+BOOST_AUTO_TEST_CASE(TagList_constIterator_accesesTags) {
+    Osmium::OSM::TagList taglist;
+
+    taglist.add("entry1", "value1");
+    taglist.add("entry2", "value2");
+
+    const Osmium::OSM::TagList const_taglist = taglist;
+    Osmium::OSM::TagList::const_iterator it = const_taglist.begin();
+    BOOST_CHECK_EQUAL(it->key(), "entry1");
+
+    it++;
+    BOOST_CHECK_EQUAL(it->key(), "entry2");
+
+    it++;
+    BOOST_CHECK_EQUAL(it == const_taglist.end(), true);
+}
+
+BOOST_AUTO_TEST_CASE(TagList_getValueByKey_returnsRequestedValue) {
+    Osmium::OSM::TagList taglist;
+
+    taglist.add("entry1", "value1");
+    taglist.add("entry2", "value2");
+
+    BOOST_CHECK_EQUAL(taglist.get_value_by_key("entry1"), "value1");
+    BOOST_CHECK_EQUAL(taglist.get_value_by_key("entry2"), "value2");
+}
+
+BOOST_AUTO_TEST_CASE(TagList_getValueByKey_returns0IfKeyIsNotPresent) {
+    Osmium::OSM::TagList taglist;
+
+    taglist.add("entry1", "value1");
+    taglist.add("entry2", "value2");
+
+    BOOST_CHECK_EQUAL((uintptr_t)taglist.get_value_by_key("something_else"), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/osm/test_tag_ostream.cpp b/test/t/osm/test_tag_ostream.cpp
new file mode 100644
index 0000000..7b7488f
--- /dev/null
+++ b/test/t/osm/test_tag_ostream.cpp
@@ -0,0 +1,19 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+#include <boost/test/output_test_stream.hpp>
+
+#include <osmium/osm/tag_ostream.hpp>
+
+BOOST_AUTO_TEST_SUITE(TagOstream)
+
+BOOST_AUTO_TEST_CASE(Tag_serializedToOstream_givesTextualRepresentation) {
+    boost::test_tools::output_test_stream output;
+    Osmium::OSM::Tag tag("example", "value");
+
+    output << tag;
+    BOOST_CHECK(output.is_equal("example=value"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/osm/test_way.cpp b/test/t/osm/test_way.cpp
new file mode 100644
index 0000000..2eb549b
--- /dev/null
+++ b/test/t/osm/test_way.cpp
@@ -0,0 +1,118 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/way.hpp>
+
+BOOST_AUTO_TEST_SUITE(Way)
+
+struct FilledWayFixture {
+    FilledWayFixture() {
+        way.id(123);
+        way.add_node(1);
+        way.add_node(2);
+        way.add_node(3);
+    }
+
+    Osmium::OSM::Way way;
+};
+
+void check_wayNodeLists_for_equality(const Osmium::OSM::WayNodeList& wnl1, const Osmium::OSM::WayNodeList& wnl2) {
+    Osmium::OSM::WayNodeList::const_iterator it1 = wnl1.begin();
+    Osmium::OSM::WayNodeList::const_iterator it2 = wnl2.begin();
+
+    while ((it1 != wnl1.end()) && (it2 != wnl2.end())) {
+        BOOST_CHECK_EQUAL(it1->ref(), it2->ref());
+        ++it1;
+        ++it2;
+    }
+    BOOST_CHECK((it1 == wnl1.end()) && (it2 == wnl2.end()));
+}
+
+BOOST_AUTO_TEST_CASE(Way_constructor_createsEmptyWay) {
+    Osmium::OSM::Way way;
+
+    BOOST_CHECK_EQUAL(way.nodes().empty(), true);
+}
+
+BOOST_AUTO_TEST_CASE(Way_copyConstructor_copiesWayNodes) {
+    FilledWayFixture fix;
+    Osmium::OSM::Way copiedWay(fix.way);
+
+    check_wayNodeLists_for_equality(fix.way.nodes(), copiedWay.nodes());
+    BOOST_CHECK_EQUAL(fix.way.id(), copiedWay.id());
+}
+
+BOOST_AUTO_TEST_CASE(Way_nodes_returnsNodeList) {
+    FilledWayFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.way.nodes()[0].ref(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(Way_constNodes_returnsNodeList) {
+    FilledWayFixture fix;
+    const Osmium::OSM::Way const_way = fix.way;
+
+    BOOST_CHECK_EQUAL(const_way.nodes()[0].ref(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(Way_type_returnsWayType) {
+    Osmium::OSM::Way way;
+
+    BOOST_CHECK_EQUAL(way.type(), WAY);
+}
+
+BOOST_AUTO_TEST_CASE(Way_getNodeId_returnsIdOfNode) {
+    FilledWayFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.way.get_node_id(1), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Way_addNode_addsNode) {
+    Osmium::OSM::Way way;
+
+    way.add_node(12);
+    BOOST_CHECK_EQUAL(way.nodes()[0].ref(), 12);
+}
+
+BOOST_AUTO_TEST_CASE(Way_getFirstNodeId_returnsIdOfFirstNode) {
+    FilledWayFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.way.get_first_node_id(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(Way_getLastNodeId_returnsIdOfLastNode) {
+    FilledWayFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.way.get_last_node_id(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(Way_isClosed_detectsIfWayIsClosed) {
+    FilledWayFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.way.is_closed(), false);
+    fix.way.add_node(1);
+    BOOST_CHECK_EQUAL(fix.way.is_closed(), true);
+}
+
+BOOST_AUTO_TEST_CASE(Way_comparisonOperator_comparesByIdThenByVersion) {
+    Osmium::OSM::Way way1, way2;
+
+    BOOST_CHECK_EQUAL(way1 < way2, false);
+    BOOST_CHECK_EQUAL(way1 > way2, false);
+
+    way1.id(12);
+    way2.id(10);
+    BOOST_CHECK_EQUAL(way1 < way2, false);
+    BOOST_CHECK_EQUAL(way1 > way2, true);
+
+    way1.id(12);
+    way2.id(12);
+    way1.version(1);
+    way2.version(2);
+    BOOST_CHECK_EQUAL(way1 < way2, true);
+    BOOST_CHECK_EQUAL(way1 > way2, false);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/testgroup_plain/osm/test_way_node.cpp b/test/t/osm/test_way_node.cpp
similarity index 52%
rename from test/testgroup_plain/osm/test_way_node.cpp
rename to test/t/osm/test_way_node.cpp
index 38bb5a9..668ee1e 100644
--- a/test/testgroup_plain/osm/test_way_node.cpp
+++ b/test/t/osm/test_way_node.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -35,5 +34,38 @@ BOOST_AUTO_TEST_CASE(set_position) {
     BOOST_CHECK(wn.has_position());
 }
 
+BOOST_AUTO_TEST_CASE(WayNode_ref_setsReferenceNumber) {
+    Osmium::OSM::WayNode wn;
+
+    wn.ref(12);
+    BOOST_CHECK_EQUAL(wn.ref(), 12);
+}
+
+BOOST_AUTO_TEST_CASE(WayNode_constPosition_returnsPosition) {
+    Osmium::OSM::WayNode wn(2, Osmium::OSM::Position(13.5, -7.2));
+
+    const Osmium::OSM::WayNode const_wn = wn;
+
+    BOOST_CHECK_EQUAL(const_wn.position().lon(), 13.5);
+    BOOST_CHECK_EQUAL(const_wn.position().lat(), -7.2);
+}
+
+BOOST_AUTO_TEST_CASE(WayNode_comparisonOperator_comparesByRef) {
+    Osmium::OSM::WayNode wn1, wn2;
+
+    BOOST_CHECK_EQUAL(wn1 < wn2, false);
+//    BOOST_CHECK_EQUAL(wn1 > wn2, false);
+
+    wn1.ref(12);
+    wn2.ref(10);
+    BOOST_CHECK_EQUAL(wn1 < wn2, false);
+//    BOOST_CHECK_EQUAL(wn1 > wn2, true);
+
+    wn1.ref(25);
+    wn2.ref(300);
+    BOOST_CHECK_EQUAL(wn1 < wn2, true);
+//    BOOST_CHECK_EQUAL(wn1 > wn2, false);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
diff --git a/test/t/osm/test_way_node_list.cpp b/test/t/osm/test_way_node_list.cpp
new file mode 100644
index 0000000..cb33936
--- /dev/null
+++ b/test/t/osm/test_way_node_list.cpp
@@ -0,0 +1,199 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/way_node_list.hpp>
+
+BOOST_AUTO_TEST_SUITE(WayNodeList)
+
+struct FilledWayNodeListFixture {
+    FilledWayNodeListFixture() {
+        wnl.add(1);
+        wnl.add(2);
+        wnl.add(3);
+    }
+
+    Osmium::OSM::WayNodeList wnl;
+};
+
+BOOST_AUTO_TEST_CASE(set_position) {
+    Osmium::OSM::WayNodeList wnl;
+    BOOST_CHECK_EQUAL(wnl.size(), 0u);
+    BOOST_CHECK(!wnl.has_position());
+    Osmium::OSM::WayNode wn(5);
+    wnl.add(wn);
+    BOOST_CHECK_EQUAL(wnl.size(), 1u);
+    BOOST_CHECK(!wnl.has_position());
+    BOOST_CHECK_EQUAL(wnl[0].ref(), 5);
+    wnl.add(17);
+    BOOST_CHECK_EQUAL(wnl.size(), 2u);
+    BOOST_CHECK_EQUAL(wnl[1].ref(), 17);
+    wnl.clear();
+    BOOST_CHECK_EQUAL(wnl.size(), 0u);
+}
+
+BOOST_AUTO_TEST_CASE(closed_or_not) {
+    Osmium::OSM::WayNodeList wnl;
+    wnl.add(5);
+    wnl.add(7);
+    wnl.add(8);
+    BOOST_CHECK(!wnl.is_closed());
+    wnl.add(5);
+    BOOST_CHECK(wnl.is_closed());
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_constructor_createsEmptyList) {
+    Osmium::OSM::WayNodeList wnl;
+
+    BOOST_CHECK_EQUAL(wnl.empty(), true);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_size_returnsCorrectSize) {
+    Osmium::OSM::WayNodeList wnl;
+
+    BOOST_CHECK_EQUAL(wnl.size(), 0u);
+
+    wnl.add(1);
+    BOOST_CHECK_EQUAL(wnl.size(), 1u);
+
+    wnl.add(2);
+    BOOST_CHECK_EQUAL(wnl.size(), 2u);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_empty_returnsIfListIsEmpty) {
+    Osmium::OSM::WayNodeList wnl;
+
+    BOOST_CHECK_EQUAL(wnl.empty(), true);
+
+    wnl.add(1);
+    BOOST_CHECK_EQUAL(wnl.empty(), false);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_clear_clearsList) {
+    FilledWayNodeListFixture fix;
+
+    fix.wnl.clear();
+    BOOST_CHECK_EQUAL(fix.wnl.empty(), true);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_iterator_traversesList) {
+    FilledWayNodeListFixture fix;
+
+    Osmium::OSM::WayNodeList::iterator it = fix.wnl.begin();
+
+    BOOST_CHECK_EQUAL(it->ref(), 1);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 2);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 3);
+    ++it;
+    BOOST_CHECK_EQUAL(it == fix.wnl.end(), true);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_constiterator_traversesList) {
+    FilledWayNodeListFixture fix;
+
+    const Osmium::OSM::WayNodeList const_wnl = fix.wnl;
+    Osmium::OSM::WayNodeList::const_iterator it = const_wnl.begin();
+
+    BOOST_CHECK_EQUAL(it->ref(), 1);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 2);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 3);
+    ++it;
+    BOOST_CHECK_EQUAL(it == const_wnl.end(), true);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_reverseIterator_traversesList) {
+    FilledWayNodeListFixture fix;
+
+    Osmium::OSM::WayNodeList::reverse_iterator it = fix.wnl.rbegin();
+
+    BOOST_CHECK_EQUAL(it->ref(), 3);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 2);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 1);
+    ++it;
+    BOOST_CHECK_EQUAL(it == fix.wnl.rend(), true);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_constReverseIterator_traversesList) {
+    FilledWayNodeListFixture fix;
+
+    const Osmium::OSM::WayNodeList const_wnl = fix.wnl;
+    Osmium::OSM::WayNodeList::const_reverse_iterator it = const_wnl.rbegin();
+
+    BOOST_CHECK_EQUAL(it->ref(), 3);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 2);
+    ++it;
+    BOOST_CHECK_EQUAL(it->ref(), 1);
+    ++it;
+    BOOST_CHECK_EQUAL(it == const_wnl.rend(), true);
+}
+
+// XXX: don't know how insert should work
+
+BOOST_AUTO_TEST_CASE(WayNodeList_bracket_accessesItems) {
+    FilledWayNodeListFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.wnl[0].ref(), 1);
+    BOOST_CHECK_EQUAL(fix.wnl[1].ref(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_constBracket_accessesItems) {
+    FilledWayNodeListFixture fix;
+    const Osmium::OSM::WayNodeList const_wnl = fix.wnl;
+
+    BOOST_CHECK_EQUAL(const_wnl[0].ref(), 1);
+    BOOST_CHECK_EQUAL(const_wnl[1].ref(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_front_returnsFirstElement) {
+    FilledWayNodeListFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.wnl.front().ref(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_constFront_returnsFirstElement) {
+    FilledWayNodeListFixture fix;
+    const Osmium::OSM::WayNodeList const_wnl = fix.wnl;
+
+    BOOST_CHECK_EQUAL(const_wnl.front().ref(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_back_returnsLastElement) {
+    FilledWayNodeListFixture fix;
+
+    BOOST_CHECK_EQUAL(fix.wnl.back().ref(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_constBack_returnsLastElement) {
+    FilledWayNodeListFixture fix;
+    const Osmium::OSM::WayNodeList const_wnl = fix.wnl;
+
+
+    BOOST_CHECK_EQUAL(const_wnl.back().ref(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_pushBack_insertsAtTheEnd) {
+    FilledWayNodeListFixture fix;
+
+    fix.wnl.push_back(Osmium::OSM::WayNode(12));
+
+    BOOST_CHECK_EQUAL(fix.wnl.back().ref(), 12);
+}
+
+BOOST_AUTO_TEST_CASE(WayNodeList_pushBackWithRef_insertsAtTheEnd) {
+    FilledWayNodeListFixture fix;
+
+    fix.wnl.push_back(12);
+
+    BOOST_CHECK_EQUAL(fix.wnl.back().ref(), 12);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/test/t/osmfile/test_filename.cpp b/test/t/osmfile/test_filename.cpp
new file mode 100644
index 0000000..4801f87
--- /dev/null
+++ b/test/t/osmfile/test_filename.cpp
@@ -0,0 +1,203 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <osmium/osmfile.hpp>
+
+BOOST_AUTO_TEST_SUITE(OSMFile)
+
+BOOST_AUTO_TEST_CASE(filename_osm) {
+    Osmium::OSMFile file("test.osm");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XML());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osm_bz2) {
+    Osmium::OSMFile file("test.osm.bz2");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XMLbz2());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osm_gz) {
+    Osmium::OSMFile file("test.osm.gz");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XMLgz());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osm_pbf) {
+    Osmium::OSMFile file("test.osm.pbf");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+}
+
+BOOST_AUTO_TEST_CASE(filename_pbf) {
+    Osmium::OSMFile file("test.pbf");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osh_pbf) {
+    Osmium::OSMFile file("test.osh.pbf");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::History());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+}
+
+BOOST_AUTO_TEST_CASE(filename_with_dir) {
+    Osmium::OSMFile file("somedir/test.osm");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XML());
+}
+
+BOOST_AUTO_TEST_CASE(filename_with_parent_dir) {
+    Osmium::OSMFile file("../test.osm");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XML());
+}
+
+BOOST_AUTO_TEST_CASE(filename_no_suffix) {
+    Osmium::OSMFile file("test");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+}
+
+BOOST_AUTO_TEST_CASE(filename_unknown_suffix) {
+    Osmium::OSMFile file("test.test");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+}
+
+BOOST_AUTO_TEST_CASE(filename_empty) {
+    Osmium::OSMFile file("");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+}
+
+BOOST_AUTO_TEST_CASE(filename_minus) {
+    Osmium::OSMFile file("-");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+}
+
+
+BOOST_AUTO_TEST_CASE(filename_osh) {
+    Osmium::OSMFile file("test.osh");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::History());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XML());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osh_gz) {
+    Osmium::OSMFile file("test.osh.gz");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::History());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XMLgz());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osh_bz2) {
+    Osmium::OSMFile file("test.osh.bz2");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::History());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XMLbz2());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osc) {
+    Osmium::OSMFile file("test.osc");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::Change());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XML());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osc_gz) {
+    Osmium::OSMFile file("test.osc.gz");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::Change());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XMLgz());
+}
+
+BOOST_AUTO_TEST_CASE(filename_osc_bz2) {
+    Osmium::OSMFile file("test.osc.bz2");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::Change());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XMLbz2());
+}
+
+BOOST_AUTO_TEST_CASE(OSMFile_type_shouldChangeFileType) {
+    Osmium::OSMFile file("test.osm");
+
+    file.type(Osmium::OSMFile::FileType::Change());
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::Change());
+}
+
+BOOST_AUTO_TEST_CASE(OSMFile_typeCalledWithString_shouldChangeFileType) {
+    Osmium::OSMFile file("test.osm");
+
+    file.type("osm");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+
+    file.type("history");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::History());
+    file.type("osh");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::History());
+
+    file.type("change");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::Change());
+    file.type("osc");
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::Change());
+
+    try {
+        file.type("invalid_type");
+        BOOST_ERROR("file.type didn't throw ArgumentError");
+    } catch (Osmium::OSMFile::ArgumentError const& ex) {
+        BOOST_CHECK_EQUAL( ex.value(), "invalid_type");
+    }
+}
+
+BOOST_AUTO_TEST_CASE(OSMFile_encoding_shouldChangeEncoding) {
+    Osmium::OSMFile file("test.osm");
+
+    file.encoding(Osmium::OSMFile::FileEncoding::PBF());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+}
+
+BOOST_AUTO_TEST_CASE(OSMFile_encodingCalledWithString_shouldChangeEncoding) {
+    Osmium::OSMFile file("test.osm");
+
+    file.encoding("pbf");
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::PBF());
+
+    file.encoding("xml");
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XML());
+
+    file.encoding("xmlgz");
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XMLgz());
+
+    file.encoding("xmlbz2");
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XMLbz2());
+
+    try {
+        file.encoding("invalid_encoding");
+        BOOST_ERROR("file.encoding didn't throw ArgumentError");
+    } catch (Osmium::OSMFile::ArgumentError const& ex) {
+        BOOST_CHECK_EQUAL(ex.value(), "invalid_encoding");
+    }
+}
+
+BOOST_AUTO_TEST_CASE(OSMFile_filenameWithoutSuffix_shouldReturnFilenameWithoutSuffix) {
+    Osmium::OSMFile file1("test.osm");
+    BOOST_CHECK_EQUAL(file1.filename_without_suffix(), "test");
+
+    Osmium::OSMFile file2("something_else.osm.bz2");
+    BOOST_CHECK_EQUAL(file2.filename_without_suffix(), "something_else");
+}
+
+BOOST_AUTO_TEST_CASE(OSMFile_filenameWithDefaultSuffix_shouldReturnCorrectFilenames) {
+    Osmium::OSMFile file("test.osm");
+    BOOST_CHECK_EQUAL(file.filename_with_default_suffix(), "test.osm");
+
+    file.encoding(Osmium::OSMFile::FileEncoding::PBF());
+    BOOST_CHECK_EQUAL(file.filename_with_default_suffix(), "test.osm.pbf");
+
+    file.encoding(Osmium::OSMFile::FileEncoding::XMLgz());
+    BOOST_CHECK_EQUAL(file.filename_with_default_suffix(), "test.osm.gz");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/osmfile/test_read_and_write.cpp b/test/t/osmfile/test_read_and_write.cpp
new file mode 100644
index 0000000..75e84a6
--- /dev/null
+++ b/test/t/osmfile/test_read_and_write.cpp
@@ -0,0 +1,256 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <streambuf>
+
+#include <boost/iostreams/filtering_stream.hpp>
+#include <boost/iostreams/filter/gzip.hpp>
+#include <boost/iostreams/filter/bzip2.hpp>
+#include <boost/filesystem.hpp>
+
+#include <osmium/osmfile.hpp>
+
+// these tests work only if your boost library is new enough to have
+// boost_filesystem version 3
+// if that is not the case we disable the test and add a dummy test
+#if BOOST_FILESYSTEM_VERSION == 3
+
+std::string example_file_content("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At ver [...]
+
+/* Test scenarios for OSMFile objects
+ */
+
+#include <temp_file_fixture.hpp>
+
+// Disable the Boost.Test handling of SIGCHLD signals.
+#if defined(BOOST_POSIX_API)
+# define DISABLE_SIGCHLD() signal(SIGCHLD, SIG_IGN)
+#else
+# define DISABLE_SIGCHLD()
+#endif
+
+BOOST_AUTO_TEST_SUITE(OSMFile_Output)
+
+
+/* Helper function
+ * Verify if the istream <inputfile> contains exactly the text <expected_content>
+ */
+void compare_file_content(std::istream* inputfile, std::string& expected_content)
+{
+    std::string file_content((std::istreambuf_iterator<char>(*inputfile)),(std::istreambuf_iterator<char>()));
+
+    BOOST_CHECK_EQUAL(file_content, expected_content);
+}
+
+/* Test basic file operations:
+ * Open an output file and check if correct fd ist returned
+ */
+BOOST_AUTO_TEST_CASE( write_to_xml_output_file ) {
+    TempFileFixture test_osm("test.osm");
+
+    Osmium::OSMFile file(test_osm);
+    BOOST_REQUIRE_EQUAL(file.fd(), -1);
+
+    file.open_for_output();
+    BOOST_REQUIRE_GE(file.fd(), 0);
+
+    write(file.fd(), example_file_content.c_str(), example_file_content.size());
+    file.close();
+    BOOST_REQUIRE_EQUAL(file.fd(), -1);
+
+    std::ifstream in(test_osm, std::ios::binary);
+    compare_file_content(&in, example_file_content);
+}
+
+
+/* Test gz encoding of output file:
+ * Open output file with "osm.gz" extension 
+ * and check if file written is gzip encoded
+ */
+BOOST_AUTO_TEST_CASE( write_to_xml_gz_output_file ) {
+    TempFileFixture test_osm_gz("test.osm.gz");
+
+    Osmium::OSMFile file(test_osm_gz);
+    file.open_for_output();
+    write(file.fd(), example_file_content.c_str(), example_file_content.size());
+    file.close();
+
+    std::ifstream inputfile(test_osm_gz, std::ios::binary);
+    boost::iostreams::filtering_istream in;
+    in.push(boost::iostreams::gzip_decompressor());
+    in.push(inputfile);
+    compare_file_content(&in, example_file_content);
+}
+
+
+/* Test bzip encoding of output file:
+ * Open output file with "osm.bz2" extension
+ * and check if file written is bzip2 encoded
+ */
+BOOST_AUTO_TEST_CASE( write_to_xml_bz2_output_file ) {
+    TempFileFixture test_osm_bz2("test.osm.bz2");
+
+    Osmium::OSMFile file(test_osm_bz2);
+    file.open_for_output();
+    write(file.fd(), example_file_content.c_str(), example_file_content.size());
+    file.close();
+
+    std::ifstream inputfile(test_osm_bz2, std::ios::binary);
+    boost::iostreams::filtering_istream in;
+    in.push(boost::iostreams::bzip2_decompressor());
+    in.push(inputfile);
+
+    compare_file_content(&in, example_file_content);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+
+BOOST_AUTO_TEST_SUITE(OSMFile_Input)
+
+void read_from_fd_and_compare(int fd, std::string& expected_content) {
+    const int buf_length = 1000;
+    char buffer[buf_length];
+
+    int read_length = read(fd, buffer, buf_length-1);
+
+    BOOST_CHECK_EQUAL(std::string(buffer, read_length), expected_content);
+}
+
+/* Test basic file input operations:
+ * Open an input file and check if correct content ist returned
+ */
+BOOST_AUTO_TEST_CASE( read_from_xml_file ) {
+    TempFileFixture test_osm("test.osm");
+
+    // write content
+    std::ofstream outputfile(test_osm, std::ios::binary);
+    outputfile << example_file_content;
+    outputfile.close();
+
+    Osmium::OSMFile file(test_osm);
+    file.open_for_input();
+
+    read_from_fd_and_compare(file.fd(), example_file_content);
+    file.close();
+}
+
+/* Test gzip decoding of input file:
+ * Write gzip compressed data
+ * and read it back through OSMFile
+ */
+BOOST_AUTO_TEST_CASE( read_from_xml_gz_file ) {
+    TempFileFixture test_osm_gz("test.osm.gz");
+
+    // write content
+    std::ofstream outputfile(test_osm_gz, std::ios::binary);
+    boost::iostreams::filtering_ostream out;
+    out.push(boost::iostreams::gzip_compressor());
+    out.push(outputfile);
+    out << example_file_content;
+    boost::iostreams::close(out);
+
+    Osmium::OSMFile file(test_osm_gz);
+    file.open_for_input();
+    read_from_fd_and_compare(file.fd(), example_file_content);
+    file.close();
+}
+
+/* Test bzip2 decoding of input file:
+ * Write bzip2 compressed data
+ * and read it back through OSMFile
+ */
+BOOST_AUTO_TEST_CASE( read_from_xml_bz2_file ) {
+    TempFileFixture test_osm_bz2("test.osm.bz2");
+    // write content
+    std::ofstream outputfile(test_osm_bz2, std::ios::binary);
+    boost::iostreams::filtering_ostream out;
+    out.push(boost::iostreams::bzip2_compressor());
+    out.push(outputfile);
+    out << example_file_content;
+    boost::iostreams::close(out);
+
+    Osmium::OSMFile file(test_osm_bz2);
+    file.open_for_input();
+    read_from_fd_and_compare(file.fd(), example_file_content);
+    file.close();
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE(OSMFile_Errors)
+
+BOOST_AUTO_TEST_CASE( OSMFile_writingToReadonlyDirectory_shouldRaiseIOException ) {
+    TempDirFixture ro_dir("ro_dir");
+    ro_dir.create_ro();
+
+    Osmium::OSMFile file((ro_dir.path / "test.osm").c_str());
+    try {
+        file.open_for_output();
+        BOOST_ERROR( "open_for_output didn't raise IOError" );
+    } catch ( Osmium::OSMFile::IOError const& ex ) {
+        BOOST_CHECK_EQUAL( ex.filename(), (ro_dir.path / "test.osm").native() );
+        BOOST_CHECK_EQUAL( ex.system_errno(), 13 );  // errno 13: Permission denied
+    }
+}
+
+/* the following test case generates memory leaks, so it is commented out for the moment
+ */
+//BOOST_AUTO_TEST_CASE( OSMFile_writingToReadonlyDirectoryWithGzip_shouldRaiseIOException ) {
+//    DISABLE_SIGCHLD();
+//    TempDirFixture ro_dir("ro_dir");
+//    ro_dir.create_ro();
+//
+//    Osmium::OSMFile file((ro_dir.path / "test.osm.gz").c_str());
+//    file.open_for_output();
+//    try {
+//        file.close();
+//        BOOST_ERROR( "file.close() didn't raise IOError" );
+//    } catch ( Osmium::OSMFile::IOError const& ex ) {
+//        BOOST_CHECK_EQUAL( ex.filename(), (ro_dir.path / "test.osm.gz").native() );
+//        BOOST_CHECK_EQUAL( ex.system_errno(), 0 ); // subprocess error has no valid errno code
+//    }
+//}
+
+BOOST_AUTO_TEST_CASE( OSMFile_readingNonexistingFile_shouldRaiseException ) {
+    TempFileFixture nonexisting_osm("nonexisting.osm");
+    Osmium::OSMFile file(nonexisting_osm);
+
+    try {
+        file.open_for_input();
+        BOOST_ERROR( "open_for_input didn't raise IOError" );
+    } catch ( Osmium::OSMFile::IOError const& ex ) {
+        BOOST_CHECK_EQUAL( ex.filename(), (std::string&)nonexisting_osm );
+        BOOST_CHECK_EQUAL( ex.system_errno(), 2 );  // errno 2: No such file or directory
+    }
+
+}
+
+BOOST_AUTO_TEST_CASE( OSMFile_readingNonexistingFileWithGzip_shouldRaiseIOException ) {
+    DISABLE_SIGCHLD();
+    TempFileFixture nonexisting_osm_gz("nonexisting.osm.gz");
+    Osmium::OSMFile file(nonexisting_osm_gz);
+
+    file.open_for_input();
+    try {
+        file.close();
+    } catch ( Osmium::OSMFile::IOError const& ex ) {
+        BOOST_CHECK_EQUAL( ex.filename(), (std::string&)nonexisting_osm_gz );
+        BOOST_CHECK_EQUAL( ex.system_errno(), 0 );  // subprocess error has no valid errno code
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+#else
+BOOST_AUTO_TEST_SUITE(Dummy)
+BOOST_AUTO_TEST_CASE(DummyTest) {
+}
+BOOST_AUTO_TEST_SUITE_END()
+#endif
+
diff --git a/test/t/osmfile/test_url.cpp b/test/t/osmfile/test_url.cpp
new file mode 100644
index 0000000..ddeca56
--- /dev/null
+++ b/test/t/osmfile/test_url.cpp
@@ -0,0 +1,17 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include<osmium/osmfile.hpp>
+
+BOOST_AUTO_TEST_SUITE(OSMFile_URL_handling)
+
+BOOST_AUTO_TEST_CASE( OSMFile_ifInstantiatedWithURL_hasSaneDefaults ) {
+    Osmium::OSMFile file("http://example.com/test.php");
+
+    BOOST_CHECK_EQUAL(file.type(), Osmium::OSMFile::FileType::OSM());
+    BOOST_CHECK_EQUAL(file.encoding(), Osmium::OSMFile::FileEncoding::XML());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/t/tags/test_filter.cpp b/test/t/tags/test_filter.cpp
new file mode 100644
index 0000000..9fb635d
--- /dev/null
+++ b/test/t/tags/test_filter.cpp
@@ -0,0 +1,110 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/tag_ostream.hpp>
+#include <osmium/tags/key_filter.hpp>
+#include <osmium/tags/key_value_filter.hpp>
+
+BOOST_AUTO_TEST_SUITE(Filter)
+
+BOOST_AUTO_TEST_CASE(key_filter) {
+    Osmium::Tags::KeyFilter filter(false);
+    filter.add(true, "highway");
+    filter.add(true, "name");
+
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary")));
+    BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb")));
+
+    Osmium::OSM::TagList tags;
+    tags.add("highway", "residential");
+    tags.add("oneway", "yes");
+    tags.add("name", "Main Street");
+
+    Osmium::Tags::KeyFilter::iterator fi_begin(filter, tags.begin(), tags.end());
+    Osmium::Tags::KeyFilter::iterator fi_end(filter, tags.end(), tags.end());
+
+    BOOST_CHECK(fi_begin != fi_end);
+    BOOST_CHECK_EQUAL(Osmium::OSM::Tag("highway", "residential"), *fi_begin++);
+    BOOST_CHECK(fi_begin != fi_end);
+    BOOST_CHECK_EQUAL(Osmium::OSM::Tag("name", "Main Street"), *fi_begin++);
+    BOOST_CHECK(fi_begin == fi_end);
+}
+
+BOOST_AUTO_TEST_CASE(key_value_filter) {
+    Osmium::Tags::KeyValueFilter filter(false);
+    filter.add(true, "highway", "motorway");
+    filter.add(true, "highway", "trunk");
+    filter.add(true, "highway", "primary");
+
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary")));
+    BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb")));
+    BOOST_CHECK(!filter(Osmium::OSM::Tag("highway", "residential")));
+
+    {
+        Osmium::OSM::TagList tags;
+        tags.add("highway", "residential");
+        tags.add("name", "Main Street");
+
+        Osmium::Tags::KeyValueFilter::iterator fi_begin(filter, tags.begin(), tags.end());
+        Osmium::Tags::KeyValueFilter::iterator fi_end(filter, tags.end(), tags.end());
+
+        BOOST_CHECK(fi_begin == fi_end);
+    }
+
+    {
+        Osmium::OSM::TagList tags;
+        tags.add("highway", "primary");
+        tags.add("name", "Main Street");
+
+        Osmium::Tags::KeyValueFilter::iterator fi_begin(filter, tags.begin(), tags.end());
+        Osmium::Tags::KeyValueFilter::iterator fi_end(filter, tags.end(), tags.end());
+
+        BOOST_CHECK(fi_begin != fi_end);
+        BOOST_CHECK_EQUAL(Osmium::OSM::Tag("highway", "primary"), *fi_begin++);
+        BOOST_CHECK(fi_begin == fi_end);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(key_value_filter_empty) {
+    Osmium::Tags::KeyValueFilter filter(false);
+    filter.add(true, "highway", "");
+
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary")));
+    BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb")));
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "residential")));
+
+    Osmium::OSM::TagList tags;
+    tags.add("highway", "primary");
+    tags.add("name", "Main Street");
+
+    Osmium::Tags::KeyValueFilter::iterator fi_begin(filter, tags.begin(), tags.end());
+    Osmium::Tags::KeyValueFilter::iterator fi_end(filter, tags.end(), tags.end());
+
+    BOOST_CHECK(fi_begin != fi_end);
+    BOOST_CHECK_EQUAL(Osmium::OSM::Tag("highway", "primary"), *fi_begin++);
+    BOOST_CHECK(fi_begin == fi_end);
+}
+
+BOOST_AUTO_TEST_CASE(key_value_filter_null) {
+    Osmium::Tags::KeyValueFilter filter(false);
+    filter.add(true, "highway");
+
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary")));
+    BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb")));
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "residential")));
+}
+
+BOOST_AUTO_TEST_CASE(key_value_filter_tf) {
+    Osmium::Tags::KeyValueFilter filter(false);
+    filter.add(false, "highway", "residential");
+    filter.add(true, "highway");
+
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary")));
+    BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb")));
+    BOOST_CHECK(!filter(Osmium::OSM::Tag("highway", "residential")));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/test/t/tags/test_regex_filter.cpp b/test/t/tags/test_regex_filter.cpp
new file mode 100644
index 0000000..d65a825
--- /dev/null
+++ b/test/t/tags/test_regex_filter.cpp
@@ -0,0 +1,38 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/tag_ostream.hpp>
+#include <osmium/tags/regex_filter.hpp>
+
+BOOST_AUTO_TEST_SUITE(RegexFilter)
+
+BOOST_AUTO_TEST_CASE(regex_filter) {
+    Osmium::Tags::RegexFilter filter(false);
+    filter.add(true, "^highway$", "^(motorway|trunk|primary)(_link)?$");
+    filter.add(true, "^highway$", "^residential$");
+    filter.add(true, "^oneway$");
+
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary")));
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary_link")));
+    BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb")));
+    BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "residential")));
+    BOOST_CHECK(filter(Osmium::OSM::Tag("oneway", "yes")));
+
+    {
+        Osmium::OSM::TagList tags;
+        tags.add("highway", "residential");
+        tags.add("name", "Main Street");
+
+        Osmium::Tags::RegexFilter::iterator fi_begin(filter, tags.begin(), tags.end());
+        Osmium::Tags::RegexFilter::iterator fi_end(filter, tags.end(), tags.end());
+
+        BOOST_CHECK(fi_begin != fi_end);
+        BOOST_CHECK_EQUAL(Osmium::OSM::Tag("highway", "residential"), *fi_begin++);
+        BOOST_CHECK(fi_begin == fi_end);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/test/t/tags/test_tag.cpp b/test/t/tags/test_tag.cpp
new file mode 100644
index 0000000..78fd42d
--- /dev/null
+++ b/test/t/tags/test_tag.cpp
@@ -0,0 +1,25 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/tag_ostream.hpp>
+
+BOOST_AUTO_TEST_SUITE(Tag)
+
+BOOST_AUTO_TEST_CASE(tag) {
+    Osmium::OSM::Tag t1("foo", "bar");
+    Osmium::OSM::Tag t2("foo", "bar");
+    Osmium::OSM::Tag t3("foo", "baz");
+    Osmium::OSM::Tag t4("x", "y");
+
+    BOOST_CHECK_EQUAL(t1, t2);
+    BOOST_CHECK(t1 != t3);
+    BOOST_CHECK(t1 != t4);
+    BOOST_CHECK(!strcmp("foo", t2.key()));
+    BOOST_CHECK(!strcmp("bar", t2.value()));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/test/t/tags/test_to_string.cpp b/test/t/tags/test_to_string.cpp
new file mode 100644
index 0000000..5594ec4
--- /dev/null
+++ b/test/t/tags/test_to_string.cpp
@@ -0,0 +1,73 @@
+#ifdef STAND_ALONE
+# define BOOST_TEST_MODULE Main
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include <vector>
+
+#include <osmium/osm/tag_ostream.hpp>
+#include <osmium/tags/to_string.hpp>
+#include <osmium/utils/filter_and_accumulate.hpp>
+#include <osmium/tags/key_filter.hpp>
+
+BOOST_AUTO_TEST_SUITE(TagToString)
+
+BOOST_AUTO_TEST_CASE(tag_to_string) {
+    Osmium::OSM::Tag t1("highway", "primary");
+    Osmium::OSM::Tag t2("name", "Main Street");
+
+    {
+        std::string out;
+        Osmium::Tags::TagToStringOp op("", "PREFIX", "INFIX", "SUFFIX", "JOIN");
+        op(out, t1);
+        op(out, t2);
+        BOOST_CHECK_EQUAL("PREFIXhighwayINFIXprimarySUFFIXJOINPREFIXnameINFIXMain StreetSUFFIX", out);
+    }
+
+    {
+        std::string out;
+        Osmium::Tags::TagToKeyEqualsValueStringOp op(",");
+        op(out, t1);
+        op(out, t2);
+        BOOST_CHECK_EQUAL("highway=primary,name=Main Street", out);
+    }
+
+    {
+        std::string out;
+        Osmium::Tags::TagToHStoreStringOp op;
+        op(out, t1);
+        op(out, t2);
+        BOOST_CHECK_EQUAL("\"highway\"=>\"primary\",\"name\"=>\"Main Street\"", out);
+    }
+
+}
+
+BOOST_AUTO_TEST_CASE(escape) {
+    std::string out;
+    Osmium::OSM::Tag t1("name", "O'Rourke Street (\"Fool's Corner\")");
+
+    Osmium::Tags::TagToHStoreStringOp op;
+    op(out, t1);
+    BOOST_CHECK_EQUAL("\"name\"=>\"O'Rourke Street (\\\"Fool's Corner\\\")\"", out);
+}
+
+BOOST_AUTO_TEST_CASE(filter_and_accumulate) {
+    Osmium::OSM::Tag t1("highway", "primary");
+    Osmium::OSM::Tag t2("name", "Main Street");
+
+    Osmium::Tags::TagToKeyEqualsValueStringOp op(",");
+
+    std::vector<Osmium::OSM::Tag> tags;
+    tags.push_back(t1);
+    tags.push_back(t2);
+
+    Osmium::Tags::KeyFilter filter(true);
+    filter.add(true, "source");
+    filter.add(true, "odbl");
+
+    std::string out = Osmium::filter_and_accumulate(tags, filter, std::string(), op);
+    BOOST_CHECK_EQUAL("highway=primary,name=Main Street", out);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/test/testgroup_plain/utils/test_timestamp.cpp b/test/t/utils/test_timestamp.cpp
similarity index 60%
rename from test/testgroup_plain/utils/test_timestamp.cpp
rename to test/t/utils/test_timestamp.cpp
index 60e4562..68d335c 100644
--- a/test/testgroup_plain/utils/test_timestamp.cpp
+++ b/test/t/utils/test_timestamp.cpp
@@ -1,4 +1,3 @@
-#define BOOST_TEST_DYN_LINK
 #ifdef STAND_ALONE
 # define BOOST_TEST_MODULE Main
 #endif
@@ -9,17 +8,17 @@
 BOOST_AUTO_TEST_SUITE(Timestamp)
 
 BOOST_AUTO_TEST_CASE(zero) {
-    BOOST_CHECK_EQUAL(std::string(""), Osmium::Utils::Timestamp::to_iso(0));
+    BOOST_CHECK_EQUAL(std::string(""), Osmium::Timestamp::to_iso(0));
 }
 
 BOOST_AUTO_TEST_CASE(second_after_epoch) {
-    BOOST_CHECK_EQUAL(std::string("1970-01-01T00:00:01Z"), Osmium::Utils::Timestamp::to_iso(1));
+    BOOST_CHECK_EQUAL(std::string("1970-01-01T00:00:01Z"), Osmium::Timestamp::to_iso(1));
 }
 
 BOOST_AUTO_TEST_CASE(sometime) {
     const char* ts= "2011-10-28T09:12:00Z";
-    time_t t = Osmium::Utils::Timestamp::parse_iso(ts);
-    BOOST_CHECK_EQUAL(std::string(ts), Osmium::Utils::Timestamp::to_iso(t));
+    time_t t = Osmium::Timestamp::parse_iso(ts);
+    BOOST_CHECK_EQUAL(std::string(ts), Osmium::Timestamp::to_iso(t));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/temp_file_fixture.hpp b/test/temp_file_fixture.hpp
new file mode 100644
index 0000000..01b079b
--- /dev/null
+++ b/test/temp_file_fixture.hpp
@@ -0,0 +1,72 @@
+#ifndef TEMP_FILE_FIXTURE
+#define TEMP_FILE_FIXTURE
+
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+
+boost::filesystem::path tempdir_path;
+
+/* TempDirFixture:  Prepare a temp directory and clean up afterwards
+ */
+struct TempBaseDirFixture {
+    TempBaseDirFixture() {
+        tempdir_path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+        boost::filesystem::create_directory(tempdir_path);
+    }
+
+    ~TempBaseDirFixture() {
+        boost::filesystem::remove_all(tempdir_path);
+    }
+};
+
+struct TempDirFixture {
+    TempDirFixture(const std::string& name) {
+        path = tempdir_path / name;
+    }
+
+    ~TempDirFixture() {
+        boost::filesystem::permissions(path, boost::filesystem::owner_all | boost::filesystem::add_perms);
+        boost::filesystem::remove_all(path);
+    }
+
+
+    void create_ro() {
+        boost::filesystem::create_directory(path);
+        boost::filesystem::permissions(path, boost::filesystem::all_all | boost::filesystem::remove_perms);
+    }
+
+    operator const char*() const {
+        return path.c_str();
+    }
+
+    operator const std::string&() const {
+        return path.native();
+    }
+
+    boost::filesystem::path path;
+};
+
+struct TempFileFixture {
+    TempFileFixture(const std::string& name) {
+        path = tempdir_path / name;
+    }
+
+    ~TempFileFixture() {
+        boost::filesystem::remove(path);
+    }
+
+    operator const char*() const {
+        return path.c_str();
+    }
+
+    operator const std::string&() const {
+        return path.native();
+    }
+
+    boost::filesystem::path path;
+};
+
+
+BOOST_GLOBAL_FIXTURE(TempBaseDirFixture)
+
+#endif
diff --git a/test/test_main.cpp b/test/test_main.cpp
index 6b9a33b..8f58b1d 100644
--- a/test/test_main.cpp
+++ b/test/test_main.cpp
@@ -1,3 +1,2 @@
-#define BOOST_TEST_DYN_LINK
 #define BOOST_TEST_MODULE Main
 #include <boost/test/unit_test.hpp>
diff --git a/test/test_utils.cpp b/test/test_utils.cpp
index 0d2f26f..0710331 100644
--- a/test/test_utils.cpp
+++ b/test/test_utils.cpp
@@ -1,4 +1,4 @@
-#include <test_utils.hpp>
+#include "test_utils.hpp"
 
 namespace Osmium {
 
diff --git a/test/testgroup_geos/setup.sh b/test/testgroup_geos/setup.sh
deleted file mode 100644
index 2be4306..0000000
--- a/test/testgroup_geos/setup.sh
+++ /dev/null
@@ -1 +0,0 @@
-FLAGS="-DOSMIUM_WITH_GEOS `geos-config --cflags` `geos-config --libs`"
diff --git a/test/testgroup_ogr/setup.sh b/test/testgroup_ogr/setup.sh
deleted file mode 100644
index df473d3..0000000
--- a/test/testgroup_ogr/setup.sh
+++ /dev/null
@@ -1 +0,0 @@
-FLAGS="-DOSMIUM_WITH_OGR `gdal-config --cflags` `gdal-config --libs`"
diff --git a/test/testgroup_plain/osm/test_node.cpp b/test/testgroup_plain/osm/test_node.cpp
deleted file mode 100644
index ebc9bfe..0000000
--- a/test/testgroup_plain/osm/test_node.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#define BOOST_TEST_DYN_LINK
-#ifdef STAND_ALONE
-# define BOOST_TEST_MODULE Main
-#endif
-#include <boost/test/unit_test.hpp>
-
-#include <osmium/osm/node.hpp>
-
-BOOST_AUTO_TEST_SUITE(Node)
-
-BOOST_AUTO_TEST_CASE(instantiation_with_default_parameters) {
-    Osmium::OSM::Node n;
-    BOOST_CHECK_EQUAL(0, n.id());
-    BOOST_CHECK_EQUAL(-1, n.uid());
-}
-
-BOOST_AUTO_TEST_CASE(order) {
-    Osmium::OSM::Node n1;
-    Osmium::OSM::Node n2;
-    n1.id(10);
-    n1.version(1);
-    n2.id(15);
-    n2.version(2);
-    BOOST_CHECK_EQUAL(true, n1 < n2);
-    BOOST_CHECK_EQUAL(false, n1 > n2);
-    n1.id(20);
-    n1.version(1);
-    n2.id(20);
-    n2.version(2);
-    BOOST_CHECK_EQUAL(true, n1 < n2);
-    BOOST_CHECK_EQUAL(false, n1 > n2);
-    n1.id(-10);
-    n1.version(2);
-    n2.id(-15);
-    n2.version(1);
-    BOOST_CHECK_EQUAL(true, n1 < n2);
-    BOOST_CHECK_EQUAL(false, n1 > n2);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
diff --git a/test/testgroup_plain/osm/test_way_node_list.cpp b/test/testgroup_plain/osm/test_way_node_list.cpp
deleted file mode 100644
index f9ff223..0000000
--- a/test/testgroup_plain/osm/test_way_node_list.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#define BOOST_TEST_DYN_LINK
-#ifdef STAND_ALONE
-# define BOOST_TEST_MODULE Main
-#endif
-#include <boost/test/unit_test.hpp>
-
-#include <osmium/osm/way_node_list.hpp>
-
-BOOST_AUTO_TEST_SUITE(WayNodeList)
-
-BOOST_AUTO_TEST_CASE(set_position) {
-    Osmium::OSM::WayNodeList wnl;
-    BOOST_CHECK_EQUAL(wnl.size(), 0);
-    BOOST_CHECK(!wnl.has_position());
-    Osmium::OSM::WayNode wn(5);
-    wnl.add(wn);
-    BOOST_CHECK_EQUAL(wnl.size(), 1);
-    BOOST_CHECK(!wnl.has_position());
-    BOOST_CHECK_EQUAL(wnl[0].ref(), 5);
-    wnl.add(17);
-    BOOST_CHECK_EQUAL(wnl.size(), 2);
-    BOOST_CHECK_EQUAL(wnl[1].ref(), 17);
-    wnl.clear();
-    BOOST_CHECK_EQUAL(wnl.size(), 0);
-}
-
-BOOST_AUTO_TEST_CASE(closed_or_not) {
-    Osmium::OSM::WayNodeList wnl;
-    wnl.add(5);
-    wnl.add(7);
-    wnl.add(8);
-    BOOST_CHECK(!wnl.is_closed());
-    wnl.add(5);
-    BOOST_CHECK( wnl.is_closed());
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
diff --git a/test/testgroup_plain/osmfile/test_filename.cpp b/test/testgroup_plain/osmfile/test_filename.cpp
deleted file mode 100644
index 66aa29b..0000000
--- a/test/testgroup_plain/osmfile/test_filename.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#define BOOST_TEST_DYN_LINK
-#ifdef STAND_ALONE
-# define BOOST_TEST_MODULE Main
-#endif
-#include <boost/test/unit_test.hpp>
-
-#include <iostream>
-#include <sstream>
-#include <string>
-
-#include <osmium/osmfile.hpp>
-
-BOOST_AUTO_TEST_SUITE(OSMFile)
-
-BOOST_AUTO_TEST_CASE(filename_osm) {
-    Osmium::OSMFile file("test.osm");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::XML());
-}
-
-BOOST_AUTO_TEST_CASE(filename_osm_bz2) {
-    Osmium::OSMFile file("test.osm.bz2");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::XMLbz2());
-}
-
-BOOST_AUTO_TEST_CASE(filename_osm_gz) {
-    Osmium::OSMFile file("test.osm.gz");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::XMLgz());
-}
-
-BOOST_AUTO_TEST_CASE(filename_osm_pbf) {
-    Osmium::OSMFile file("test.osm.pbf");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::PBF());
-}
-
-BOOST_AUTO_TEST_CASE(filename_pbf) {
-    Osmium::OSMFile file("test.pbf");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::PBF());
-}
-
-BOOST_AUTO_TEST_CASE(filename_osh_pbf) {
-    Osmium::OSMFile file("test.osh.pbf");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::History());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::PBF());
-}
-
-BOOST_AUTO_TEST_CASE(filename_with_dir) {
-    Osmium::OSMFile file("somedir/test.osm");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::XML());
-}
-
-BOOST_AUTO_TEST_CASE(filename_with_parent_dir) {
-    Osmium::OSMFile file("../test.osm");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::XML());
-}
-
-BOOST_AUTO_TEST_CASE(filename_no_suffix) {
-    Osmium::OSMFile file("test");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::PBF());
-}
-
-BOOST_AUTO_TEST_CASE(filename_unknown_suffix) {
-    Osmium::OSMFile file("test.test");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::PBF());
-}
-
-BOOST_AUTO_TEST_CASE(filename_empty) {
-    Osmium::OSMFile file("");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::PBF());
-}
-
-BOOST_AUTO_TEST_CASE(filename_minus) {
-    Osmium::OSMFile file("-");
-    BOOST_CHECK_EQUAL(file.get_type(), Osmium::OSMFile::FileType::OSM());
-    BOOST_CHECK_EQUAL(file.get_encoding(), Osmium::OSMFile::FileEncoding::PBF());
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
diff --git a/test/testgroup_plain/setup.sh b/test/testgroup_plain/setup.sh
deleted file mode 100644
index c6a21af..0000000
--- a/test/testgroup_plain/setup.sh
+++ /dev/null
@@ -1 +0,0 @@
-FLAGS=""
diff --git a/valgrind.supp b/valgrind.supp
new file mode 100644
index 0000000..7f5a1af
--- /dev/null
+++ b/valgrind.supp
@@ -0,0 +1,38 @@
+#
+#  Use this with valgrind when checking for memory leaks. This suppresses
+#  reporting of some problems outside of our control.
+#
+#  For example:
+#
+#  valgrind --suppressions=valgrind.supp --leak-check=full --show-reachable=yes osmium_mpdump foo.osm.pbf
+#
+{
+   libz_unitialised_value
+   Memcheck:Cond
+   fun:inflateReset2
+   fun:inflateInit2_
+   fun:uncompress
+}
+{
+   libc_stdio_buffer_c
+   Memcheck:Leak
+   fun:_Znam
+   fun:_ZNSt13basic_filebufIcSt11char_traitsIcEE27_M_allocate_internal_bufferEv
+   obj:*libstdc++.so.*
+   fun:_ZNSt8ios_base15sync_with_stdioEb
+}
+{
+   libc_stdio_buffer_w
+   Memcheck:Leak
+   fun:_Znam
+   fun:_ZNSt13basic_filebufIwSt11char_traitsIwEE27_M_allocate_internal_bufferEv
+   obj:*libstdc++.so.*
+   fun:_ZNSt8ios_base15sync_with_stdioEb
+}
+{
+   tmpfile
+   Memcheck:Leak
+   fun:malloc
+   fun:fdopen@@GLIBC_*
+   fun:tmpfile@@GLIBC_*
+}

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



More information about the Pkg-grass-devel mailing list