[node-osmium] 07/10: Imported Upstream version 0.2.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Fri Mar 6 17:09:13 UTC 2015


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

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

commit c7d49bdc623109a23a3d1511ca7c2e20378c96c0
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Mar 6 15:16:39 2015 +0100

    Imported Upstream version 0.2.0
---
 .gitignore                                         |  11 +-
 .travis.yml                                        |  84 +++--
 Makefile                                           | 119 +++++-
 README.md                                          | 103 +++---
 binding.gyp                                        |  28 +-
 common.gypi                                        |   6 +-
 demo/README.md                                     |   4 +-
 demo/csv-output/README.md                          |  12 +
 demo/csv-output/index.js                           |  43 +++
 demo/{geojson-stream => csv-output}/package.json   |   3 +-
 demo/geojson-stream/README.md                      |   7 +-
 demo/geojson-stream/index.js                       |   7 +-
 demo/geojson-stream/package.json                   |   2 +-
 demo/public-transport/README.md                    |  16 +
 demo/public-transport/index.js                     |  88 +++++
 .../package.json                                   |   1 -
 demo/relation-tree/README.md                       |  14 +
 demo/relation-tree/index.js                        |  68 ++++
 .../{geojson-stream => relation-tree}/package.json |   3 +-
 demo/spatialite-output/README.md                   |  14 +
 demo/spatialite-output/index.js                    |  78 ++++
 .../package.json                                   |   3 +-
 doc/tutorial.md                                    | 378 +++++++++++++++++++
 lib/osmium.js                                      |  57 ++-
 package.json                                       |  19 +-
 scripts/locally_package.sh                         |  19 +
 scripts/shared_build.sh                            |  21 ++
 scripts/static_build.sh                            |  17 +
 scripts/validate_tag.sh                            |  24 ++
 src/apply.cpp                                      | 195 ++++++++++
 src/apply.hpp                                      |  17 +
 src/buffer_wrap.cpp                                |  79 ++++
 src/buffer_wrap.hpp                                |  55 +++
 src/file_wrap.cpp                                  |  73 ++--
 src/file_wrap.hpp                                  |  32 +-
 src/handler.cpp                                    | 403 ++++++++-------------
 src/handler.hpp                                    |  93 +++--
 src/location_handler_wrap.cpp                      |  56 ++-
 src/location_handler_wrap.hpp                      |  93 +++--
 src/node_osmium.cpp                                |  35 +-
 src/osm_changeset_wrap.cpp                         |  89 +++++
 src/osm_changeset_wrap.hpp                         |  54 +++
 src/osm_entity_wrap.cpp                            |  26 ++
 src/osm_entity_wrap.hpp                            |  78 ++++
 src/osm_node_wrap.cpp                              | 119 +++---
 src/osm_node_wrap.hpp                              |  57 +--
 src/osm_object_wrap.cpp                            |  91 +++--
 src/osm_object_wrap.hpp                            |  59 ++-
 src/osm_relation_wrap.cpp                          | 117 +++---
 src/osm_relation_wrap.hpp                          |  57 +--
 src/osm_way_wrap.cpp                               | 171 +++++----
 src/osm_way_wrap.hpp                               |  55 +--
 src/reader_wrap.cpp                                | 247 ++++---------
 src/reader_wrap.hpp                                |  42 +--
 src/utils.cpp                                      |  33 ++
 src/utils.hpp                                      |  30 ++
 test/apply.test.js                                 |  34 ++
 test/buffer.test.js                                |  97 +++++
 test/changesets.test.js                            |  70 ++++
 test/coordinates.test.js                           |  44 +++
 test/data/changesets.osm                           |  14 +
 test/data/missing-node.osm                         |   8 +
 test/data/winthrop.osm.ser                         | Bin 0 -> 144088 bytes
 test/file.test.js                                  |  38 ++
 test/geojson.test.js                               |  43 +++
 test/handler.test.js                               | 256 +++++++++++++
 test/location_handler.test.js                      |  83 +++++
 test/osm-objects.test.js                           | 177 +++++++++
 test/osm_object_creation.test.js                   |  59 +++
 test/osmium.test.js                                | 150 --------
 test/reader.test.js                                |  69 ++++
 71 files changed, 3656 insertions(+), 1191 deletions(-)

diff --git a/.gitignore b/.gitignore
index b13eb67..ef77c8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
-node_modules
-lib/binding
+.*.swp
 .DS_Store
-build
 berlin-latest.osm.pbf
-.*.swp
\ No newline at end of file
+build
+check_reports
+includes.log
+iwyu.log
+lib/binding
+node_modules
diff --git a/.travis.yml b/.travis.yml
index eb695f4..fa14202 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,30 +1,72 @@
-language: node_js
+language: cpp
 
-node_js:
- - "0.10"
+os:
+  - linux
+  - osx
 
+compiler:
+ - clang
+
+env:
+  matrix:
+   # static_build == portable, statically linked node-osmium and deps
+   # shared_build == non-portable, shared libraries via package manager
+   - NODE_NVM_VERSION="0.10" RUN_BUILD="static_build"
+   - NODE_NVM_VERSION="0.10" RUN_BUILD="shared_build"
+  global:
+   - secure: Ac0sNxaMmm/4NLJwjB5W74wNNrXH2cFz4qADJSJwGr+p78NVrp+3B70Nlffg6sf6sq57KeoQi5EbUNSa4fjLS2FqJJDr5U8cOA4nt5tPGm8cucdAyr4VCREEddtV4jfIcNN0/I/DKIK4d744SmpqomVYqTUFOVK8LOT4vAX3tTo=
+   - secure: bsD0VyN6F6+ratG/C2AB1GrbmaPGyvaAgjZXd7tsQPYl+TR3BT643T9GikgN5IJdbjVBOJDfSqk76g/UlfLl9bRnoybP8tJsZJhCyMYKyRNGJZBIOyEVe6j5FrcT5ejBO0EIGaw2fXwSkG92pg7CFQOAPkVNgcl/H0XNdMa3Q/8=
 before_install:
- # we need c++11 compatible g++
- - sudo apt-add-repository --yes ppa:ubuntu-toolchain-r/test
- - sudo apt-get -y update
- - sudo apt-get -y install gcc-4.7 g++-4.7
- # first test install of binary
-# - npm install --verbose
-# - make test
+# check if tag exists and matches package.json
+- scripts/validate_tag.sh
+# get commit message
+- COMMIT_MESSAGE=$(git show -s --format=%B $TRAVIS_COMMIT | tr -d '\n')
+# put node-pre-gyp on path
+- export PATH=./node_modules/.bin/:$PATH
+# here we set up the node version on the fly based on the matrix value.
+# This is done manually so that it is easy to flip the 'language' to
+# objective-c in another branch (to run the same travis.yml on OS X)
+- git clone https://github.com/creationix/nvm.git ../.nvm
+- source ../.nvm/nvm.sh
+- nvm install $NODE_NVM_VERSION
+- nvm use $NODE_NVM_VERSION
+- node --version
+- npm --version
 
 install:
- # next test source compile, so get dependencies
- - sudo apt-add-repository --yes ppa:mapnik/boost
- - sudo apt-get -y update -qq
- - sudo apt-get -y install git build-essential libboost-dev zlib1g-dev protobuf-compiler libprotobuf-lite7 libprotobuf-dev libexpat1-dev libsparsehash-dev
- - git clone https://github.com/scrosby/OSM-binary.git
- - cd OSM-binary/src
- - make && sudo make install
- - cd ../../
+# set a few env settings for the static build script
+- NODE_MODULE_ROOT=$(pwd)
+- TMP_DEPS_DIR=$(mktemp -d -t XXXXXXXXXXX)
+# WARNING: this script modifies the environment
+- source ./scripts/${RUN_BUILD}.sh
+- cd ${NODE_MODULE_ROOT}
 
 before_script:
- - git clone https://github.com/osmcode/libosmium.git ../libosmium
- - CXX=g++-4.7 npm install --build-from-source
+# install from source, ensure this works first
+- git clone https://github.com/osmcode/libosmium.git ../libosmium
+- npm install --build-from-source --clang=1
+- if [[ `uname -s` == 'Darwin' ]]; then otool -L ./lib/binding/* || true; fi
+- if [[ `uname -s` == 'Linux' ]]; then readelf -d ./lib/binding/* || true; fi
+- npm test
+- node-pre-gyp package
+- node-pre-gyp testpackage
 
 script:
- - make test
+# now we publish the binary, if needed
+- PUBLISH_BINARY=false
+# or check if we are manually requesting binary publishing via a commit keyword
+- if [[ $RUN_BUILD == "static_build" ]] && test "${COMMIT_MESSAGE#*'[publish binary]'}" != "$COMMIT_MESSAGE"; then PUBLISH_BINARY=true; fi;
+# if either are true, then publish the binary
+# Note: this publishing is done here, in the 'script' section, instead of the 'after_success'
+# since we want any failure here to stop the build immediately
+- if [[ $PUBLISH_BINARY == true ]]; then node-pre-gyp publish; fi
+# now clean up to prepare to re-install from remote binary
+- node-pre-gyp clean
+# now install from published binary
+# Note: we capture the error here so that if the install fails we can unpublish
+- INSTALL_RESULT=0
+- if [[ $PUBLISH_BINARY == true ]]; then INSTALL_RESULT=$(npm install --fallback-to-build=false > /dev/null)$? || true; fi
+# if install returned non zero (errored) then we first unpublish and then call false so travis will bail at this line
+- if [[ $INSTALL_RESULT != 0 ]]; then echo "returned $INSTALL_RESULT";node-pre-gyp unpublish;false; fi
+# If success then we arrive here so let's test again
+- if [[ $PUBLISH_BINARY == true ]]; then npm test; fi
diff --git a/Makefile b/Makefile
old mode 100755
new mode 100644
index 645cb37..f5e5d06
--- a/Makefile
+++ b/Makefile
@@ -1,28 +1,125 @@
-all: osmium.node
 
-./node_modules/.bin/node-gyp:
-	npm install node-gyp
+CXXFLAGS := $(CXXFLAGS) -std=c++11 -I/usr/local/include/node -I../libosmium/include
+CXXFLAGS_WARNINGS := -Wall -Wextra -pedantic -Wredundant-decls -Wdisabled-optimization -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wold-style-cast
 
-./build: binding.gyp ./node_modules/.bin/node-gyp
-	./node_modules/.bin/node-gyp configure
+INCLUDE_FILES := $(shell find src -name \*.hpp | sort)
+IWYU_REPORT_FILES := $(subst src,check_reports,$(INCLUDE_FILES:.hpp=.iwyu))
+INCLUDES_REPORT_FILES := $(subst src,check_reports,$(INCLUDE_FILES:.hpp=.compile))
 
-osmium.node: Makefile ./build
-	./node_modules/.bin/node-gyp build
+DEMOS := $(shell find demo -mindepth 1 -maxdepth 1 -type d)
+
+all: build
+
+./node_modules:
+	npm install --build-from-source
+
+build: ./node_modules
+	./node_modules/.bin/node-pre-gyp build --loglevel=silent
+
+debug:
+	./node_modules/.bin/node-pre-gyp rebuild --debug
+
+verbose:
+	./node_modules/.bin/node-pre-gyp rebuild --loglevel=verbose
 
 clean:
-	rm -rf ./build
-	rm -f lib/osmium.node
+	rm -rf ./build ./check_reports lib/binding
+	rm -f includes.log iwyu.log
+	rm -rf lib/binding/
+
+distclean:
+	rm -rf node_modules demo/*/node_modules
 
 rebuild:
 	@make clean
 	@make
 
-test:
-	@PATH="./node_modules/mocha/bin:${PATH}" && NODE_PATH="./lib:$(NODE_PATH)" mocha -R spec --timeout 10000
+test: build
+	@PATH="./node_modules/mocha/bin:${PATH}" && NODE_PATH="./lib:$(NODE_PATH)" mocha -R spec --timeout 10s
 
 indent:
 	astyle --style=java --indent-namespaces --indent-switches --pad-header --lineend=linux --suffix=none src/\*pp
 
 check: test
 
+# Install all dependencies and run the demos. Because the demos usually need
+# one or more arguments that are not supplied, they dont't do much though.
+demos:
+	for demo in $(DEMOS); do \
+	    cd $${demo}; \
+	    npm install; \
+        ./index.js; \
+        cd ../..; \
+    done
+
+# This will try to compile include files on their own to detect missing
+# include directives and other dependency-related problems. Note that if this
+# reports OK, it is not enough to be sure it will compile in production code.
+# But if it reports an error we know we are missing something.
+check_reports/%.compile: src/%.hpp
+	mkdir -p `dirname $@`; \
+	echo "$<\n===========================================" >$@; \
+    cmdline="$(CXX) $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -I include $<"; \
+	echo "$${cmdline}\n===========================================" >>$@; \
+	if $${cmdline} >>$@ 2>&1; then \
+        echo "\n===========================================\nOK" >>$@; \
+    else \
+        echo "\n===========================================\nERR" >>$@; \
+    fi; \
+    rm -f $<.gch;
+
+# This will run IWYU (Include What You Use) on includes files. The iwyu
+# program isn't very reliable and crashes often, but is still useful.
+check_reports/%.iwyu: src/%.hpp
+	mkdir -p `dirname $@`; \
+	echo "$<\n===========================================" >$@; \
+    cmdline="iwyu -Xiwyu -- $(CXXFLAGS) $(CXXFLAGS_WARNINGS) -I include $<"; \
+	echo "$${cmdline}\n===========================================" >>$@; \
+	$${cmdline} >>$@ 2>&1; \
+	true
+
+.PHONY: iwyu_reports iwyu includes_reports check-includes
+
+iwyu_reports: $(IWYU_REPORT_FILES)
+
+includes_reports: $(INCLUDES_REPORT_FILES)
+
+iwyu: iwyu_reports
+	@echo "INCLUDE WHAT YOU USE REPORT:" >iwyu.log; \
+    allok=yes; \
+	for FILE in $(IWYU_REPORT_FILES); do \
+	    INCL=`echo $${FILE%.iwyu}.hpp | sed -e 's at check_reports@src@'`; \
+        if grep -q 'has correct #includes/fwd-decls' $${FILE}; then \
+	        echo "\n\033[1m\033[32m========\033[0m \033[1m$${INCL}\033[0m" >>iwyu.log; \
+            echo "[OK] $${INCL}"; \
+        elif grep -q 'Assertion failed' $${FILE}; then \
+	        echo "\n\033[1m======== $${INCL}\033[0m" >>iwyu.log; \
+            echo "[--] $${INCL}"; \
+            allok=no; \
+        else \
+	        echo "\n\033[1m\033[31m========\033[0m \033[1m$${INCL}\033[0m" >>iwyu.log; \
+            echo "[  ] $${INCL}"; \
+            allok=no; \
+        fi; \
+	    cat $${FILE} >>iwyu.log; \
+	done; \
+    if test $${allok} = "yes"; then echo "All files OK"; else echo "There were errors"; fi;
+
+check-includes: includes_reports
+	@echo "INCLUDES REPORT:" >includes.log; \
+    allok=yes; \
+	for FILE in $(INCLUDES_REPORT_FILES); do \
+	    INCL=`echo $${FILE%.compile}.hpp | sed -e 's at check_reports@src@'`; \
+        if tail -1 $${FILE} | grep OK >/dev/null; then \
+	        echo "\n\033[1m\033[32m========\033[0m \033[1m$${INCL}\033[0m" >>includes.log; \
+            echo "[OK] $${INCL}"; \
+        else \
+	        echo "\n\033[1m\033[31m========\033[0m \033[1m$${INCL}\033[0m" >>includes.log; \
+            echo "[  ] $${INCL}"; \
+            allok=no; \
+        fi; \
+	    cat $${FILE} >>includes.log; \
+	done; \
+    if test $${allok} = "yes"; then echo "All files OK"; else echo "There were errors"; fi;
+
 .PHONY: test indent
diff --git a/README.md b/README.md
index a69b503..7778e63 100644
--- a/README.md
+++ b/README.md
@@ -2,97 +2,108 @@
 
 Fast and flexible Javascript library for working with OpenStreetMap data.
 
-Provides a bindings to the [libosmium](https://github.com/osmcode/libosmium) C++ library.
+Provides bindings to the [libosmium](https://github.com/osmcode/libosmium)
+C++ library.
+
+[![NPM](https://nodei.co/npm/osmium.png?downloads=true&downloadRank=true)](https://nodei.co/npm/osmium/)
 
 [![Build Status](https://secure.travis-ci.org/osmcode/node-osmium.png)](http://travis-ci.org/osmcode/node-osmium)
 
-# Depends
+## Depends
 
  - Node.js v0.10.x
+ - libosmium (https://github.com/osmcode/libosmium)
+ - Mocha (http://visionmedia.github.io/mocha/, for tests)
 
-# Installing
+## Installing
 
-By default, binaries are provided and no external depedencies and no compile is needed.
+By default, binaries are provided and no external dependencies or compile is
+needed.
 
 Just do:
 
     npm install osmium
 
-We currently provide binaries for 64 bit OS X and 64 bit Linux. Running `npm install` on other
-platforms will fall back to a source compile (see `Developing` below for build details).
-
-# Usage
+We currently provide binaries for 64 bit OS X and 64 bit Linux. Running `npm
+install` on other platforms will fall back to a source compile (see
+`Developing` below for build details).
 
-## Get the bounds of an `.osm` file
 
-```js
-var osmium = require('osmium');
-var file = new osmium.File("test/data/winthrop.osm");
-var reader = new osmium.Reader(file);
-console.log(reader.header())
-{ generator: 'CGImap 0.2.0',
-  bounds: [ -120.2024, 48.4636, -120.1569, 48.4869 ] }
-```
+## Usage
 
-## Parse a `.pbf` file and create a node handler callback to count total nodes
+See [the tutorial](doc/tutorial.md) for an introduction. There are some demo
+applications in the 'demo' directory. See the [README.md](demo/README.md)
+there. You can also have a look at the tests in the `test` directory.
 
-```js
-var osmium = require('osmium');
-var file = new osmium.File("test/data/winthrop.osm");
-var reader = new osmium.Reader(file);
-var handler = new osmium.Handler();
-var nodes = 0;
-handler.on('node',function(node) {
-    ++nodes;
-});
-reader.apply(handler);
-console.log(nodes);
-1525
-```
 
-# Developing
+## Developing
 
-If you wish to develop on `node-osmium` you can check out the code and then build like:
+If you wish to develop on `node-osmium` you can check out the code and then
+build like:
 
     git clone https://github.com/osmcode/node-osmium.git
     cd node-osmium
     make
+
+## Testing
+
+    npm install mocha
     make test
 
-## Source build dependencies
+### Source build dependencies
 
  - Compiler that supports `-std=c++11` (>= clang++ 3.2 || >= g++ 4.8)
- - Boost >= 1.49 with development headers
+ - Boost >= 1.46 with development headers
  - OSM-Binary
  - Protocol buffers
  - zlib
 
-Set depedencies up on Ubuntu Precise (12.04) like:
+See also the dependency information for the Osmium library.
+
+Set dependencies up on Ubuntu Precise (12.04) like:
 
     sudo apt-add-repository --yes ppa:chris-lea/node.js
-    sudo apt-add-repository --yes ppa:mapnik/boost
     sudo apt-add-repository --yes ppa:ubuntu-toolchain-r/test
     sudo apt-get -y update
-    sudo apt-get -y install git gcc-4.7 g++-4.7 build-essential nodejs libboost-dev zlib1g-dev protobuf-compiler libprotobuf-lite7 libprotobuf-dev libexpat1-dev libsparsehash-dev
-    export CC=gcc-4.7
-    export CXX=g++-4.7
+    sudo apt-get -y install git gcc-4.8 g++-4.8 build-essential nodejs
+    sudo apt-get -y install libboost-dev zlib1g-dev protobuf-compiler
+    sudo apt-get -y install libprotobuf-lite7 libprotobuf-dev libexpat1-dev
+    sudo apt-get -y install libsparsehash-dev
+    export CC=gcc-4.8
+    export CXX=g++-4.8
     git clone https://github.com/scrosby/OSM-binary.git
     cd OSM-binary/src
     make && sudo make install
 
-Set depedencies up on OS X like:
+Set dependencies up on OS X like:
 
     git clone https://github.com/mapnik/mapnik-packaging.git
     cd mapnik-packaging/osx
-    # open the settings and make `export CXX11=true`
     export CXX11=true
     source MacOSX.sh
-    ./scripts/download_deps.sh
+    ./scripts/build_bzip2.sh
+    ./scripts/build_expat.sh
     ./scripts/build_google_sparsetable.sh
-    ./scripts/build_icu.sh
-    ./scripts/build_boost.sh
+    ./scripts/build_boost.sh --with-test --with-program_options
     ./scripts/build_protobuf.sh
-    ./scripts/build_node.sh
     ./scripts/build_osm-pbf.sh
     # NOTE: in the same terminal then run the build commands
     # Or from a different terminal re-run `source MacOSX.sh`
+
+## License
+
+node-osmium is available under the Boost Software License. See LICENSE.txt for
+details.
+
+## Contact
+
+Please open bug reports on https://github.com/osmcode/node-osmium/issues. You
+can ask questions on the
+[OSM developer mailing list](https://lists.openstreetmap.org/listinfo/dev)
+or on [OFTC net IRC channel #osm-dev](https://wiki.openstreetmap.org/wiki/Irc).
+
+## Authors
+
+ - Dane Springmeyer (dane at mapbox.com)
+ - Jochen Topf (jochen at topf.org)
+
diff --git a/binding.gyp b/binding.gyp
index 740c262..fd4eb2b 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -2,21 +2,26 @@
   "includes": [ "common.gypi" ],
   "variables": {
       "module_name":"osmium",
-      "module_path":"./lib/"
+      "module_path":"./lib/binding"
   },
   "targets": [
     {
       "target_name": "<(module_name)",
       "sources": [
-        "src/node_osmium.cpp",
+        "src/apply.cpp",
+        "src/buffer_wrap.cpp",
+        "src/file_wrap.cpp",
         "src/handler.cpp",
         "src/location_handler_wrap.cpp",
-        "src/file_wrap.cpp",
-        "src/reader_wrap.cpp",
-        "src/osm_object_wrap.cpp",
+        "src/node_osmium.cpp",
+        "src/osm_changeset_wrap.cpp",
+        "src/osm_entity_wrap.cpp",
         "src/osm_node_wrap.cpp",
+        "src/osm_object_wrap.cpp",
+        "src/osm_relation_wrap.cpp",
         "src/osm_way_wrap.cpp",
-        "src/osm_relation_wrap.cpp"
+        "src/reader_wrap.cpp",
+        "src/utils.cpp"
       ],
       "include_dirs": [
           "../libosmium/include/",
@@ -24,19 +29,18 @@
       ],
       "defines": [
         "_LARGEFILE_SOURCE",
-        "_FILE_OFFSET_BITS=64",
-        "OSMIUM_WITH_DEBUG"
+        "_FILE_OFFSET_BITS=64"
       ],
       "xcode_settings": {
         "GCC_ENABLE_CPP_RTTI": "YES",
         "GCC_ENABLE_CPP_EXCEPTIONS": "YES",
-        "OTHER_CPLUSPLUSFLAGS":["-stdlib=libc++"],
-        "OTHER_LDFLAGS":["-stdlib=libc++"],
+        'CLANG_CXX_LIBRARY': 'libc++',
         "CLANG_CXX_LANGUAGE_STANDARD":"c++11",
-        "MACOSX_DEPLOYMENT_TARGET":"10.7"
+        'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+        "MACOSX_DEPLOYMENT_TARGET":"10.9"
       },
       "cflags_cc!": ["-fno-rtti", "-fno-exceptions"],
-      "cflags_cc" : ["-std=c++11"],
+      "cflags_cc" : ["-std=c++11", "-Wno-return-type"],
       "libraries": [
           "-losmpbf",
           "-lprotobuf-lite",
diff --git a/common.gypi b/common.gypi
index 3e0d5de..fb38c34 100644
--- a/common.gypi
+++ b/common.gypi
@@ -12,8 +12,10 @@
           },
           'Release': {
               'xcode_settings': {
-                'GCC_OPTIMIZATION_LEVEL': 's',
-                'OTHER_CPLUSPLUSFLAGS':['-gline-tables-only','-fno-omit-frame-pointer'],
+                'GCC_OPTIMIZATION_LEVEL': '3',
+                'GCC_GENERATE_DEBUGGING_SYMBOLS': 'NO',
+                'DEAD_CODE_STRIPPING': 'YES',
+                'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES'
               },
               'ldflags': [
                     '-Wl,-s'
diff --git a/demo/README.md b/demo/README.md
index ae3847b..15e3551 100644
--- a/demo/README.md
+++ b/demo/README.md
@@ -4,10 +4,10 @@ To try these demos just move into a demo directory and run:
 
     npm install
 
-See the `Readme.md` for details.
+See the `README.md` for details.
 
 Then run the `index.js` for usage help:
 
     ./index.js
 
-    
\ No newline at end of file
+    
diff --git a/demo/csv-output/README.md b/demo/csv-output/README.md
new file mode 100644
index 0000000..5c386af
--- /dev/null
+++ b/demo/csv-output/README.md
@@ -0,0 +1,12 @@
+# csv-output
+
+Reads OSM file and outputs some data for schools and highways.
+
+## Install
+
+    npm install
+
+## Usage
+
+    ./index.js ../../test/data/winthrop.osm winthrop.csv
+
diff --git a/demo/csv-output/index.js b/demo/csv-output/index.js
new file mode 100755
index 0000000..865e973
--- /dev/null
+++ b/demo/csv-output/index.js
@@ -0,0 +1,43 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+var buffered_writer = require('buffered-writer');
+
+if (process.argv.length != 4) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE OUTFILE");
+    process.exit(1);
+}
+
+var input_filename = process.argv[2];
+var output_filename = process.argv[3];
+
+var stream = buffered_writer.open(output_filename);
+
+// =====================================
+
+var handler = new osmium.Handler();
+handler.options({ "tagged_nodes_only": true });
+
+handler.on('node', function(node) {
+    if (node.tags('amenity') == 'school') {
+        stream.write('n' + node.id + ' ' + (node.tags('name') || '') + ' ' +  node.wkt() + "\n"); 
+    }
+});
+
+handler.on('way', function(way) {
+    if (way.tags('highway')) {
+        if (way.wkt) {
+            stream.write('w' + way.id + ' '  + way.tags('highway') + ' ' + way.wkt() + "\n"); 
+        }
+    }
+});
+
+handler.on('done', function() {
+    stream.close();
+});
+
+var reader = new osmium.Reader(input_filename);
+var location_handler = new osmium.LocationHandler();
+osmium.apply(reader, location_handler, handler);
+reader.close();
+
diff --git a/demo/geojson-stream/package.json b/demo/csv-output/package.json
similarity index 59%
copy from demo/geojson-stream/package.json
copy to demo/csv-output/package.json
index 4e3beeb..2635237 100644
--- a/demo/geojson-stream/package.json
+++ b/demo/csv-output/package.json
@@ -2,7 +2,6 @@
   "name": "test",
   "version": "0.0.0",
   "dependencies": {
-    "minimist":"*",
-    "geojson-stream": "*"
+    "buffered-writer": "*"
   }
 }
diff --git a/demo/geojson-stream/README.md b/demo/geojson-stream/README.md
index a888f19..67f81c2 100644
--- a/demo/geojson-stream/README.md
+++ b/demo/geojson-stream/README.md
@@ -1,8 +1,10 @@
-# Install
+# geojson-stream
+
+## Install
 
     npm install
 
-# Usage
+## Usage
 
 Convert osm data to geojson file:
 
@@ -12,3 +14,4 @@ You can also pipe geojson to stdout and push to http://geojson.io:
 
     npm install -g geojsonio-cli
     ./index.js --output stdout --input ../../test/data/winthrop.osm | geojsonio
+
diff --git a/demo/geojson-stream/index.js b/demo/geojson-stream/index.js
index b1da769..746e6c2 100755
--- a/demo/geojson-stream/index.js
+++ b/demo/geojson-stream/index.js
@@ -38,10 +38,7 @@ var handler = new osmium.Handler();
 handler.on('node',function(node) {
     geojsonOut.write({
         type: 'Feature',
-        geometry: {
-            type: 'Point',
-            coordinates: [node.lon, node.lat]
-        },
+        geometry: node.geojson(),
         properties: {}
     });
 });
@@ -50,4 +47,4 @@ handler.on('done',function() {
     geojsonOut.end();
 });
  
-reader.apply(handler);
+osmium.apply(reader, handler);
diff --git a/demo/geojson-stream/package.json b/demo/geojson-stream/package.json
index 4e3beeb..4e3ea2b 100644
--- a/demo/geojson-stream/package.json
+++ b/demo/geojson-stream/package.json
@@ -2,7 +2,7 @@
   "name": "test",
   "version": "0.0.0",
   "dependencies": {
-    "minimist":"*",
+    "minimist": "*",
     "geojson-stream": "*"
   }
 }
diff --git a/demo/public-transport/README.md b/demo/public-transport/README.md
new file mode 100644
index 0000000..2bea3b8
--- /dev/null
+++ b/demo/public-transport/README.md
@@ -0,0 +1,16 @@
+# public-transport
+
+Reads OSM file and outputs public transport route into a GeoJSON file.
+
+Public transport is often tagged differently in different places, this one
+works well in Berlin, Germany.
+
+## Install
+
+    npm install
+
+## Usage
+
+    wget http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf
+    ./index.js berlin-latest.osm.pbf berlin.geojson
+
diff --git a/demo/public-transport/index.js b/demo/public-transport/index.js
new file mode 100755
index 0000000..75df624
--- /dev/null
+++ b/demo/public-transport/index.js
@@ -0,0 +1,88 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+var fs = require('fs');
+var geojsonStream = require('geojson-stream');
+
+if (process.argv.length != 4) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE DBFILE");
+    process.exit(1);
+}
+
+var input_filename = process.argv[2];
+var output_filename = process.argv[3];
+
+var route_relations = {},
+    ways = {};
+
+// FIRST PASS
+
+var handler = new osmium.Handler();
+
+handler.on('relation', function(relation) {
+    if (relation.tags('type') == 'route_master') {
+        var route_info = {
+            ref: relation.tags('ref'),
+            colour: relation.tags('colour'),
+            rtype: relation.tags('route_master')
+        };
+        relation.members().forEach(function(member) {
+            if (member.type == 'r') {
+                route_relations[member.ref] = route_info;
+            }
+        });
+    }
+});
+
+var reader = new osmium.Reader(input_filename);
+osmium.apply(reader, handler);
+console.log("first pass done");
+
+// SECOND PASS
+
+handler.on('relation', function(relation) {
+    var info = route_relations[relation.id];
+    if (info) {
+        relation.members().forEach(function(member) {
+            if (member.type == 'w') {
+                ways[member.ref] = info;
+            }
+        });
+    }
+});
+
+reader = new osmium.Reader(input_filename);
+osmium.apply(reader, handler);
+console.log("second pass done");
+
+// THIRD PASS
+
+handler = new osmium.Handler();
+
+handler.on('way', function(way) {
+    var info = ways[way.id];
+    if (info) {
+        geojsonOut.write({
+            type: 'Feature',
+            geometry: way.geojson(),
+            properties: {
+                way_id: way.id,
+                ref:    info.ref,
+                colour: info.colour,
+                rtype:  info.rtype
+            }
+        });
+    }
+});
+
+var fileOut = fs.createWriteStream(output_filename);
+var geojsonOut = geojsonStream.stringify();
+geojsonOut.pipe(fileOut);
+
+var location_handler = new osmium.LocationHandler();
+
+reader = new osmium.Reader(input_filename);
+osmium.apply(reader, location_handler, handler);
+geojsonOut.end();
+console.log("third pass done");
+
diff --git a/demo/geojson-stream/package.json b/demo/public-transport/package.json
similarity index 82%
copy from demo/geojson-stream/package.json
copy to demo/public-transport/package.json
index 4e3beeb..8140b6f 100644
--- a/demo/geojson-stream/package.json
+++ b/demo/public-transport/package.json
@@ -2,7 +2,6 @@
   "name": "test",
   "version": "0.0.0",
   "dependencies": {
-    "minimist":"*",
     "geojson-stream": "*"
   }
 }
diff --git a/demo/relation-tree/README.md b/demo/relation-tree/README.md
new file mode 100644
index 0000000..f301d2b
--- /dev/null
+++ b/demo/relation-tree/README.md
@@ -0,0 +1,14 @@
+# relation-tree
+
+Output recursive structure of relations in an OSM file.
+
+## Install
+
+    npm install
+
+## Usage
+
+    ./index.js ../../test/data/winthrop.osm winthrop.reltree
+
+The output for winthrop.osm is rather boring. Use a larger extract to see something.
+
diff --git a/demo/relation-tree/index.js b/demo/relation-tree/index.js
new file mode 100755
index 0000000..32aa72c
--- /dev/null
+++ b/demo/relation-tree/index.js
@@ -0,0 +1,68 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+var buffered_writer = require('buffered-writer');
+
+if (process.argv.length != 4) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE OUTFILE");
+    process.exit(1);
+}
+
+var input_filename = process.argv[2];
+var output_filename = process.argv[3];
+
+var stream = buffered_writer.open(output_filename);
+
+// =====================================
+
+var handler = new osmium.Handler();
+
+var relations = {};
+
+handler.on('relation', function(relation) {
+    relations[relation.id] = [ relation.tags('type') || '(undefined)', relation.members().filter(function(member) {
+        return member.type == 'r';
+    }).map(function(member) {
+        return member.ref;
+    })];
+});
+
+function indent(n) {
+    return '                                                  '.substr(0, n*2);
+}
+
+function print_recurse(level, id) {
+    stream.write(indent(level) + id);
+
+    if (relations[id]) {
+        stream.write(' type=' + relations[id][0] + "\n");
+        if (relations[id].length > 2) {
+            if (relations[id][1].length > 0) {
+                stream.write(indent(level+1) + "LOOP!\n");
+            }
+        } else {
+            relations[id][1].forEach(function(child) {
+                print_recurse(level+1, child);
+            });
+            relations[id][2] = 'done';
+        }
+    } else {
+        stream.write(" (incomplete)\n");
+    }
+}
+
+handler.on('done', function() {
+    var ids = Object.keys(relations).sort(function(a, b) {
+        return a - b;
+    });
+    ids.forEach(function(id) {
+        if (relations[id].length == 2) {
+            print_recurse(0, id);
+        }
+    });
+    stream.close();
+});
+
+var reader = new osmium.Reader(input_filename);
+osmium.apply(reader, handler);
+
diff --git a/demo/geojson-stream/package.json b/demo/relation-tree/package.json
similarity index 59%
copy from demo/geojson-stream/package.json
copy to demo/relation-tree/package.json
index 4e3beeb..2635237 100644
--- a/demo/geojson-stream/package.json
+++ b/demo/relation-tree/package.json
@@ -2,7 +2,6 @@
   "name": "test",
   "version": "0.0.0",
   "dependencies": {
-    "minimist":"*",
-    "geojson-stream": "*"
+    "buffered-writer": "*"
   }
 }
diff --git a/demo/spatialite-output/README.md b/demo/spatialite-output/README.md
new file mode 100644
index 0000000..1d359f7
--- /dev/null
+++ b/demo/spatialite-output/README.md
@@ -0,0 +1,14 @@
+# spatialite-output
+
+Reads OSM file and outputs some data into spatialite file.
+
+## Install
+
+    npm install
+
+## Usage
+
+    ./index.js ../../test/data/winthrop.osm winthrop.db
+
+You will get a spatialite database with tables `amenities` and `highways`.
+
diff --git a/demo/spatialite-output/index.js b/demo/spatialite-output/index.js
new file mode 100755
index 0000000..9af6eea
--- /dev/null
+++ b/demo/spatialite-output/index.js
@@ -0,0 +1,78 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+var fs = require('fs');
+var sqlite3 = require('spatialite');
+var exec = require('child_process').exec;
+
+if (process.argv.length != 4) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE DBFILE");
+    process.exit(1);
+}
+
+var input_filename = process.argv[2];
+var output_filename = process.argv[3];
+
+// =====================================
+
+if (fs.existsSync(output_filename)) {
+    console.log("Output database file '" + output_filename + "' exists.");
+    process.exit(1);
+}
+
+/* This is a horrible workaround: We initialize the database
+   outside of node using the "spatialite" program. This way we
+   get a version of the database that fits the spatialite version
+   installed on the system instead of the compiled in version 4 of
+   the node-spatialite module. Otherwise other programs on the system
+   might not be able to open this database. Ugh. */
+var child = exec("spatialite " + output_filename + " \"SELECT InitSpatialMetaData('WGS84');\"");
+child.on('exit', function(code, signal) {
+
+    var db = new sqlite3.Database(output_filename);
+    db.serialize();
+
+    db.spatialite();
+    db.run("PRAGMA synchronous = OFF;"); // otherwise it is very slow
+
+    // we don't need this here because of the workaround above
+    //db.run("SELECT InitSpatialMetaData('WGS84');");
+
+    db.run("CREATE TABLE amenities (osm_id INT, name TEXT);");
+    db.run("SELECT AddGeometryColumn('amenities', 'geom', 4326, 'POINT', 2);");
+
+    db.run("CREATE TABLE highways (osm_id INT, htype TEXT, name TEXT);");
+    db.run("SELECT AddGeometryColumn('highways', 'geom', 4326, 'LINESTRING', 2);");
+
+    var add_amenity = db.prepare("INSERT INTO amenities (osm_id, name, geom) VALUES (?, ?, GeomFromWKB(?, 4326))");
+    var add_highway = db.prepare("INSERT INTO highways (osm_id, htype, name, geom) VALUES (?, ?, ?, GeomFromWKB(?, 4326))");
+
+    var handler = new osmium.Handler();
+
+    handler.on('node', function(node) {
+        if (node.tags('amenity') == 'school') {
+            add_amenity.run(node.id, node.tags('name'), node.wkb()); 
+        }
+    });
+
+    handler.on('way', function(way) {
+        if (way.tags('highway')) {
+            if (way.wkt) {
+                add_highway.run(way.id, way.tags('highway'), way.tags('name'), way.wkb());
+            }
+        }
+    });
+
+    handler.on('done', function() {
+        add_highway.finalize();
+        add_amenity.finalize();
+        db.close();
+    });
+
+    var location_handler = new osmium.LocationHandler();
+    var reader = new osmium.Reader(input_filename);
+    osmium.apply(reader, location_handler, handler);
+    reader.close();
+
+});
+
diff --git a/demo/geojson-stream/package.json b/demo/spatialite-output/package.json
similarity index 59%
copy from demo/geojson-stream/package.json
copy to demo/spatialite-output/package.json
index 4e3beeb..1339b30 100644
--- a/demo/geojson-stream/package.json
+++ b/demo/spatialite-output/package.json
@@ -2,7 +2,6 @@
   "name": "test",
   "version": "0.0.0",
   "dependencies": {
-    "minimist":"*",
-    "geojson-stream": "*"
+    "spatialite": "*"
   }
 }
diff --git a/doc/tutorial.md b/doc/tutorial.md
new file mode 100644
index 0000000..32931c9
--- /dev/null
+++ b/doc/tutorial.md
@@ -0,0 +1,378 @@
+
+# Osmium Node Module Tutorial
+
+## Using the node-osmium Library
+
+Install with
+
+    npm install osmium
+
+and add
+
+    var osmium = require('osmium');
+
+in your Javascript file to access it.
+
+## OSM File Access
+
+`node-osmium` can read all the popular types of OSM files. It can read the XML
+format (without compression (`.osm`) or with gzip (`.osm.gz`) or bzip2
+(`.osm.bz2`) compression), the PBF format (`.osm.pbf`), files with (`.osh`) or
+without (`.osm`) historic data and change files (`.osc`). It can also read OSM
+files with changesets.
+
+To open such a file you need a `File` object:
+
+    var file = new osmium.File("some_file.osm");
+
+Osmium will detect the file format from the filename suffix. It understands all
+the usual suffixes. If it doesn't understand the suffix, you can add a second
+parameter with the format. For instance the following example will force the
+uncompressed XML format:
+
+    var file = new osmium.File("some_file", "osm");
+
+This will force PBF:
+
+    var file = new osmium.File("some_file", "pbf");
+
+You can read from STDIN, but you have to set the format then:
+
+    var file = new osmium.File("-", "pbf");
+
+You can also read directly from a URL like this:
+
+    var file = new osmium.File("http://example.com/data.osm", "osm");
+
+### Accessing OSM Data From a Node Buffer
+
+Sometimes you have OSM data not in a file on disk but somewhere else. If you
+can get it into a `node.Buffer` you can get it into Osmium. Simply give the
+buffer to the `osmium.File` constructor instead of a file name:
+
+    var buffer = new node.Buffer();
+    // fill buffer with data
+    var file = osmium.File(buffer, "pbf"); // buffer contents in PBF format
+
+## Creating a Reader
+
+Once you have defined a `File` you can open a `Reader` to access its data:
+
+    var file = new osmium.File("data.osm");
+    var reader = new osmium.Reader(file);
+
+In this simple case you can also directly open the `Reader` with a file name:
+
+    var reader = new osmium.Reader("data.osm");
+
+But if you need to specify a format or want to read from a `node.Buffer` you
+have to initialize the `osmium.File` first.
+
+### Defining Which Object Types to Read
+
+OSM files usually contain nodes, ways, and/or relations. Some special OSM files
+can contain changelog entries. If you only want to read some of those object
+types, you can specify which by adding an extra object parameter when
+initializing the Reader.
+
+In case you are only interested in nodes and ways it would look like this:
+
+    var reader = new osmium.Reader("data.osm", { node: true, way: true });
+
+The default is to read all the different object types. But to improve
+performance it is recommened to only read the object types you really need.
+That way other object types are discarded on the C++ side avoiding the costly
+C++-to-Javascript conversion.
+
+### Getting the File Header
+
+The `Reader` object gives you access to the OSM file header:
+
+    var reader = new osmium.Reader("data.osm");
+    var header = reader.header();
+
+The `header` object contains the `generator`, the software that created this
+file:
+
+    header.generator; // ===> 'CGImap 0.2.0'
+
+And it contains the bounding box(es) of the data. (OSM files can have zero
+or more bounding boxes, usually they have one.)
+
+    var bounds = header.bound[0];
+    var min_lon = bounds.left();
+    var max_lon = bounds.right();
+    var min_lat = bounds.bottom();
+    var max_lat = bounds.top();
+
+## Defining a Handler
+
+Osmium will generate events for each object it reads. Those events can be
+handled by your code. For this you need to define a handler and define some
+callbacks on it:
+
+    var handler = new osmium.Handler();
+    handler.on('node', function(node) {
+        console.log("got a node with id ", node.id);
+    });
+
+There are callbacks like the above for each type of OSM object: `node`, `way`,
+`relation`, and `changeset`.
+
+Sometimes you need to run initialization or finalization code. Use the `init`
+and `done` callbacks:
+
+    handler.on('init', function() { ... });
+    handler.on('done', function() { ... });
+
+There are also "before" and "after" callbacks called between objects of
+different types:
+
+    handler.on('before_nodes', function() { ... });
+    handler.on('after_nodes', function() { ... });
+    handler.on('before_ways', function() { ... });
+    ...
+
+### Handler Options
+
+Sometimes you are only interested in nodes with tags. But the majority of
+nodes do not have any tags at all. As an optimization you can tell the
+handler to only give you tagged nodes:
+
+    var handler = new osmium.Handler();
+    handler.options({ 'tagged_nodes_only': true });
+
+## Accessing OSM Objects
+
+Through the `node`, `way` and `relation` callbacks you get access to the
+OSM objects.
+
+OSM objects are read-only and you can not create them yourself. Think of
+them not as real objects, but convenient accessors to the data in the OSM
+file. You can not keep the objects around outside the handler callback.
+If you need to retain some data from the objects, extract the data you need
+and put it in your own data structure. OSM files can be quite large, so
+make sure you'll store the data efficiently.
+
+### Accessing OSM Object Attributes
+
+Each object has the usual attributes:
+
+    var handler = new osmium.Handler();
+    handler.on('node', function(node) {
+        console.log(node.id);        // unique id of this object
+        console.log(node.version);   // version of this object
+        console.log(node.changeset); // changeset id of this object
+        console.log(node.uid);       // user id
+        console.log(node.user);      // name of user
+    });
+
+Accessing the timestamp when this object version was created is a bit more
+complex. To get a `Date` object with this information call the `timestamp()`
+function:
+
+        console.log(node.timestamp());
+
+Because this is an expensive operation and often a full `Date` object is not
+needed, you can also get the timestamp as a simple number counting the seconds
+since midnight January 1, 1970:
+
+        console.log(node.timestamp_seconds_since_epoch);
+
+The `node.visible` attribute tells you whether an object is visible or has
+been deleted. For normal OSM files it is always true, but if the file contains
+old versions of the OSM data ("history file") or is a "change file" (`.osc`),
+this attribute can be true or false.
+
+The same works for ways and relations. Changelog objects are different and not
+documented here.
+
+### Accessing the Tags
+
+Of course you can also get access to the tags:
+
+    way.tags(); // ==> { "highway": "residential", "maxspeed": "50" }
+
+or ask for a specific key:
+
+    way.tags("highway"); // ==> "residential"
+
+Use the second form if you are only interested in a few tags, because it is
+faster.
+
+### Accessing Node Locations And the Coordinates Property
+
+OSM node objects contain the location of the node, the coordinates. You can
+access them in several ways:
+
+    node.lon;           // ==> Number between -180.0 and 180.0
+    node.lat;           // ==> Number between -90.0 and 90.0
+    node.coordinates;   // ==> Coordinates object
+
+The `Coordinates` object returned by the `coordinates` property has `lon`
+and `lat` properties:
+
+    var c = node.coordinates;
+    console.log(c.lon);
+    console.log(c.lat);
+
+Unlike the `Node` object, the `Coordinates` object can created by you and it
+can be copied around and used like any normal Javascript object.
+
+### Accessing Way Nodes
+
+Ways have a reference to the IDs of the nodes they use:
+
+    way.node_refs();  // ==> [12345, 629375, 273054] (Array with node IDs)
+    way.node_refs(1); // ==> 629375  (ID of 2nd node)
+
+You can not access a node object this way, only the ID. See below for accessing
+the node locations.
+
+If all you need is the number of referenced nodes, use the `nodes_count`
+property of the way object:
+
+    way.nodes_count;  // ==> number of nodes in this way
+
+### Accessing Relation Members
+
+Relations have data about their members. To access them use the `members()`
+function:
+
+    relation.members();   // ==> Array of members
+    relation.members(2);  // ==> Third member
+
+A member is currently an Array with three values: The type of the member
+('n', 'w', or 'r'), the ID of the member, and the role of the member. (This
+interface will probably change at some point.)
+
+If all you need is the number of members, use the `members_count` property of
+the relation object:
+
+    relation.members_count;  // ==> number of members in this way
+
+## The LocationHandler
+
+OSM ways contain only references to the nodes, but in most cases you don't need
+the ID but the location. This is where the `LocationHandler` comes into play.
+Initialize and call the `LocationHandler` like this:
+
+    var reader = new osmium.Reader("some_file.osm");
+    var location_handler = new osmium.LocationHandler();
+    var handler = new osmium.Handler();
+    // set up your handler callbacks
+    osmium.apply(reader, location_handler, handler);
+
+This will call the location handler before your handler. When the location
+handler encounters nodes, it will store their location. Later, reading the
+same file it will "add" the locations to the ways so that your handler will
+see ways with added node locations. You can access them like this:
+
+    way.node_coordinates();
+
+This will return an `Array` with `osmium.Coordinates` objects. It is also
+the basis for the geometry functions described below.
+
+The `LocationHandler` can use different strategies for storing the node
+locations. Which strategy is the best depends on the size of the input file and
+the amount of main memory you have. Here are the available options:
+
+ * "sparsetable" (default) - use this for small (city) to medium (country) sized
+   data files.
+ * "stlmap" - memory efficient for very small datasets, use for very small data
+   files (small cities) or when "sparsetable" is not available.
+ * "array" - best memory efficiency for large countries and planet sized data
+   files. You will need main memory of size (8 bytes times the highest node ID),
+   for a planet thats currently on the order of 25 GByte main memory! Not
+   available on OSX!
+ * "disk" - best memory efficiency for large countries and planet sized data
+   files, but uses hard disk instead of memory. Use for very large data files
+   if you are on OSX (and therefore can't use the "array" type) or if you don't
+   have enough main memory. Will, of course, be slow compared to the other
+   strategies.
+
+To set the strategy initialize the `LocationHandler` with its name:
+
+    var location_handler = new osmium.LocationHandler("array");
+
+## Geometry Functions
+
+Usually you don't want (arrays of) raw coordinates, but you want geometries in
+some standard format. Osmium supports WKT (Well Known Text), WKB (Well Known
+Binary) and GeoJSON representations of geometries for nodes and ways. You have
+to use a LocationHandler as described above for these functions to work for
+ways.
+
+### WKT
+
+The `wkt()` function called on a node or way returns a `String` with a
+representation of the geometry:
+
+    node.wkt(); // ===> "POINT(1.56 9.20)"
+    way.wkt();  // ===> "LINESTRING(1.56 9.20, 4.56 10.29)"
+
+See http://en.wikipedia.org/wiki/Well-known_text for a description of the
+WKT and WKB format.
+
+### WKB
+
+The `wkb()` function called on a node or way returns a `node.Buffer` with a
+binary representation of the geometry.
+
+    node.wkb();
+    way.wkb();
+
+See http://en.wikipedia.org/wiki/Well-known_text for a description of the
+WKT and WKB format.
+
+### GeoJSON
+
+The `geojson` function called on a node or way returns an Object with a
+representation of the geometry according to the GeoJSON spec:
+
+    node.geojson();
+
+will result in something like this:
+
+    {
+        type: 'Point',
+        coordinates: [-120.1891610, 48.4655800]
+    }
+
+and
+
+    way.geojson();
+
+will result in something like this:
+
+    {
+        type: 'LineString',
+        coordinates: [
+            [-120.1796227, 48.4798110],
+            [-120.1787663, 48.4802976]
+        ]
+    }
+
+This is, of course, only the "geometry part" of a full feature, you have to
+add the rest yourself. See the `demo/geojson-stream` example for a complete
+program using the GeoJSON function.
+
+## A Complete Example
+
+Finally here is a complete example to get you started: This parses an OSM file
+and creates a node handler callback to count the total number of nodes:
+
+    var osmium = require('osmium');
+    var reader = new osmium.Reader("test/data/winthrop.osm");
+    var handler = new osmium.Handler();
+    var nodes = 0;
+    handler.on('node', function(node) {
+        ++nodes;
+    });
+    osmium.apply(reader, handler);
+    console.log(nodes);
+
+Result:
+
+    1525
+
diff --git a/lib/osmium.js b/lib/osmium.js
index 1722bfb..f31365c 100644
--- a/lib/osmium.js
+++ b/lib/osmium.js
@@ -2,6 +2,57 @@ var osmium = require('./binding/osmium.node');
 exports = module.exports = osmium;
 exports.version = require('../package').version;
 
-exports.Node.prototype.date = function() {
-    return new Date(1000*this.timestamp);
-}
\ No newline at end of file
+exports.OSMObject.prototype.timestamp = function() {
+    return new Date(1000 * this.timestamp_seconds_since_epoch);
+}
+
+exports.Changeset.prototype.created_at = function() {
+    return new Date(1000 * this.created_at_seconds_since_epoch);
+}
+
+exports.Changeset.prototype.closed_at = function() {
+    if (this.closed_at_seconds_since_epoch == 0) {
+        return undefined;
+    }
+    return new Date(1000 * this.closed_at_seconds_since_epoch);
+}
+
+exports.Coordinates = function(lon, lat) {
+    this.lon = lon;
+    this.lat = lat;
+    this.valid = function() {
+        return this.lon !== undefined &&
+               this.lat !== undefined &&
+               this.lon >= -180.0 &&
+               this.lon <= 180.0 &&
+               this.lat >= -90.0 &&
+               this.lat <= 90.0;
+    };
+}
+
+exports.Box = function(bottom_left, top_right) {
+    this.bottom_left = bottom_left;
+    this.top_right = top_right;
+
+    this.left   = function() { return this.bottom_left === undefined ? undefined : this.bottom_left.lon; };
+    this.bottom = function() { return this.bottom_left === undefined ? undefined : this.bottom_left.lat; };
+    this.right  = function() { return this.top_right   === undefined ? undefined : this.top_right.lon; };
+    this.top    = function() { return this.top_right   === undefined ? undefined : this.top_right.lat; };
+}
+
+exports.Node.prototype.geojson = function() {
+    return {
+        type: 'Point',
+        coordinates: [this.lon, this.lat]
+    };
+}
+
+exports.Way.prototype.geojson = function() {
+    return {
+        type: 'LineString',
+        coordinates: this.node_coordinates().map(function(c) {
+            return [ c.lon, c.lat ];
+        })
+    };
+}
+
diff --git a/package.json b/package.json
index d9509ba..28707e4 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,6 @@
 {
     "name": "osmium",
+    "version": "0.2.0",
     "description": "Node.js bindings to Osmium",
     "url": "https://github.com/osmcode/node-osmium",
     "homepage": "http://osmcode.org/node-osmium",
@@ -7,7 +8,6 @@
     "contributors": [
         "Jochen Topf <joto>"
     ],
-    "version": "0.1.2",
     "main": "./lib/osmium.js",
     "bugs": {
         "email": "dane at mapbox.com",
@@ -24,23 +24,26 @@
         "url": "git://github.com/osmcode/node-osmium.git"
     },
     "binary": {
-        "module_name": "osmium",
-        "module_path": "./lib/binding/",
-        "host": "https://node-osmium.s3.amazonaws.com"
+        "module_name" : "osmium",
+        "module_path" : "./lib/binding/",
+        "host"        : "https://mapbox-node-binary.s3.amazonaws.com",
+        "remote_path" : "./{module_name}/v{version}",
+        "package_name": "{node_abi}-{platform}-{arch}.tar.gz"
     },
     "dependencies": {
-        "node-pre-gyp": "~0.5.10"
+        "node-pre-gyp": "0.5.27"
     },
     "bundledDependencies":["node-pre-gyp"],
     "devDependencies": {
-        "mocha": "*"
+        "mocha": "*",
+        "aws-sdk": "2.0.12"
     },
     "licenses": [ { "type": "Boost" } ],
     "engines": {
-        "node": ">= 0.6.13 < 0.11.0"
+        "node": ">= 0.10.0 < 0.11.0"
     },
     "scripts": {
         "install": "node-pre-gyp install --fallback-to-build",
-        "test": "mocha -R spec"
+        "test": "mocha -R spec --timeout 10000"
     }
 }
diff --git a/scripts/locally_package.sh b/scripts/locally_package.sh
new file mode 100755
index 0000000..953c554
--- /dev/null
+++ b/scripts/locally_package.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -u
+make clean
+rm -rf node_modules/
+export CXX11=true
+source ~/projects/mapnik-packaging/osx/MacOSX.sh
+npm install --build-from-source
+make test
+npm install aws-sdk
+export PATH=$(pwd)/lib/binding:${PATH}
+npm test
+./node_modules/.bin/node-pre-gyp package publish
+# node v0.8.x
+npm install --build-from-source --target=0.8.26
+nvm use 0.8
+npm test
+./node_modules/.bin/node-pre-gyp package publish --target=0.8.26
+
diff --git a/scripts/shared_build.sh b/scripts/shared_build.sh
new file mode 100755
index 0000000..86a9ed8
--- /dev/null
+++ b/scripts/shared_build.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+UNAME=$(uname -s);
+if [ ${UNAME} = 'Darwin' ]; then
+    brew install boost protobuf osm-pbf expat google-sparsehash
+else
+    # install packages
+    sudo apt-add-repository --yes ppa:ubuntu-toolchain-r/test
+    sudo apt-get -qq update
+    sudo apt-get -y install gcc-4.8 g++-4.8
+    export CXX=g++-4.8
+    export CC=gcc-4.8
+    sudo apt-get -y install git build-essential libboost-dev zlib1g-dev protobuf-compiler libprotobuf-lite7 libprotobuf-dev libexpat1-dev libsparsehash-dev
+
+    # install osmpbf
+    git clone --depth=1 https://github.com/scrosby/OSM-binary.git
+    cd OSM-binary/src
+    make
+    sudo make install
+    cd ../../
+fi
\ No newline at end of file
diff --git a/scripts/static_build.sh b/scripts/static_build.sh
new file mode 100755
index 0000000..25deb40
--- /dev/null
+++ b/scripts/static_build.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -u
+if [[ ${TMP_DEPS_DIR:-false} == false ]]; then
+    TMP_DEPS_DIR=/tmp/osrm-build
+    rm -rf ${TMP_DEPS_DIR}
+    mkdir -p ${TMP_DEPS_DIR}
+fi
+if [[ ${NODE_MODULE_ROOT:-false} == false ]]; then
+    NODE_MODULE_ROOT=$(pwd)
+fi
+git clone --depth=1 https://github.com/mapnik/mapnik-packaging.git ${TMP_DEPS_DIR}/mapnik-packaging
+cd ${TMP_DEPS_DIR}/mapnik-packaging
+export CXX11=true
+source build.sh
+build_osmium
+cd ${NODE_MODULE_ROOT}
\ No newline at end of file
diff --git a/scripts/validate_tag.sh b/scripts/validate_tag.sh
new file mode 100755
index 0000000..67d039b
--- /dev/null
+++ b/scripts/validate_tag.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -u
+
+# let's catch the case where we tag but
+# forget to increment the package.json version
+
+# check if we are on a tag
+if [ `git describe --tags --always HEAD` ]; then
+    echo 'looks like we are on a tag'
+    if [[ $TRAVIS_BRANCH == `git describe --tags --always HEAD` ]]; then
+        echo 'git reports the same tag as travis'
+        # now check to make sure package.json `version` matches
+        MODULE_VERSION=$(node -e "console.log(require('./package.json').version)")
+        if [[ $MODULE_VERSION != $TRAVIS_BRANCH ]] && [[ v$MODULE_VERSION != $TRAVIS_BRANCH ]]; then
+            echo "package.json version ($MODULE_VERSION) does not match tag ($TRAVIS_BRANCH)"
+            exit 1
+        else
+            echo "Validation success: package.json ($MODULE_VERSION) matches tag ($TRAVIS_BRANCH)"
+        fi
+    else
+        echo "warning: travis thinks the tag ($TRAVIS_BRANCH) differs from git (`git describe --tags --always HEAD`)"
+    fi
+fi
diff --git a/src/apply.cpp b/src/apply.cpp
new file mode 100644
index 0000000..9c5f8ff
--- /dev/null
+++ b/src/apply.cpp
@@ -0,0 +1,195 @@
+
+// boost
+#include <boost/variant.hpp>
+
+// node
+#include <node.h>
+#include <node_buffer.h>
+
+// osmium
+#include <osmium/io/input_iterator.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/visitor.hpp>
+
+// node-osmium
+#include "apply.hpp"
+#include "buffer_wrap.hpp"
+#include "handler.hpp"
+#include "location_handler_wrap.hpp"
+#include "reader_wrap.hpp"
+#include "utils.hpp"
+
+namespace node_osmium {
+
+    struct visitor_type : public boost::static_visitor<> {
+
+        osmium::OSMEntity& m_entity;
+
+        visitor_type(osmium::OSMEntity& entity) :
+            m_entity(entity) {
+        }
+
+        void operator()(JSHandler& handler) const {
+            handler.dispatch_entity(m_entity);
+        }
+
+        void operator()(location_handler_type& handler) const {
+            osmium::apply_item(m_entity, handler);
+        }
+
+    }; // struct visitor_type
+
+    struct visitor_before_after_type : public boost::static_visitor<> {
+
+        osmium::item_type m_last;
+        osmium::item_type m_current;
+
+        visitor_before_after_type(osmium::item_type last, osmium::item_type current) :
+            m_last(last),
+            m_current(current) {
+        }
+
+        // The operator() is overloaded just for location_handler and
+        // for JSHandler. Currently these are the only handlers allowed
+        // anyways. But this needs to be fixed at some point to allow
+        // any handler. Unfortunately a template function is not allowed
+        // here.
+        void operator()(location_handler_type& visitor) const {
+        }
+
+        void operator()(JSHandler& visitor) const {
+            switch (m_last) {
+                case osmium::item_type::undefined:
+                    visitor.init();
+                    break;
+                case osmium::item_type::node:
+                    visitor.after_nodes();
+                    break;
+                case osmium::item_type::way:
+                    visitor.after_ways();
+                    break;
+                case osmium::item_type::relation:
+                    visitor.after_relations();
+                    break;
+                case osmium::item_type::changeset:
+                    visitor.after_changesets();
+                    break;
+                default:
+                    break;
+            }
+            switch (m_current) {
+                case osmium::item_type::undefined:
+                    visitor.done();
+                    break;
+                case osmium::item_type::node:
+                    visitor.before_nodes();
+                    break;
+                case osmium::item_type::way:
+                    visitor.before_ways();
+                    break;
+                case osmium::item_type::relation:
+                    visitor.before_relations();
+                    break;
+                case osmium::item_type::changeset:
+                    visitor.before_changesets();
+                    break;
+                default:
+                    break;
+            }
+        }
+
+    }; // struct visitor_before_after
+
+    typedef boost::variant<location_handler_type&, JSHandler&> some_handler_type;
+
+    template <class TIter>
+    v8::Handle<v8::Value> apply_iterator(TIter it, TIter end, std::vector<some_handler_type>& handlers) {
+        struct javascript_error {};
+
+        v8::HandleScope scope;
+        v8::TryCatch trycatch;
+        try {
+            osmium::item_type last_type = osmium::item_type::undefined;
+
+            for (; it != end; ++it) {
+                visitor_before_after_type visitor_before_after(last_type, it->type());
+                visitor_type visitor(*it);
+
+                for (auto& handler : handlers) {
+                    if (last_type != it->type()) {
+                        boost::apply_visitor(visitor_before_after, handler);
+                        if (trycatch.HasCaught()) {
+                            throw javascript_error();
+                        }
+                    }
+                    boost::apply_visitor(visitor, handler);
+                    if (trycatch.HasCaught()) {
+                        throw javascript_error();
+                    }
+                }
+
+                if (last_type != it->type()) {
+                    last_type = it->type();
+                }
+            }
+
+            visitor_before_after_type visitor_before_after(last_type, osmium::item_type::undefined);
+            for (auto& handler : handlers) {
+                boost::apply_visitor(visitor_before_after, handler);
+                if (trycatch.HasCaught()) {
+                    throw javascript_error();
+                }
+            }
+        } catch (const javascript_error& e) {
+            trycatch.ReThrow();
+        } catch (const std::exception& e) {
+            std::string msg("osmium error: ");
+            msg += e.what();
+            return ThrowException(v8::Exception::Error(v8::String::New(msg.c_str())));
+        }
+        return scope.Close(v8::Undefined());
+    }
+
+    v8::Handle<v8::Value> apply(const v8::Arguments& args) {
+        v8::HandleScope scope;
+
+        if (args.Length() > 0 && args[0]->IsObject()) {
+            std::vector<some_handler_type> handlers;
+
+            for (int i=1; i != args.Length(); ++i) {
+                if (!args[i]->IsObject()) {
+                    return ThrowException(v8::Exception::TypeError(v8::String::New("please provide handler objects as second and further parameters to apply()")));
+                }
+                auto obj = args[i]->ToObject();
+                if (JSHandler::constructor->HasInstance(obj)) {
+                    handlers.push_back(unwrap<JSHandler>(obj));
+                } else if (LocationHandlerWrap::constructor->HasInstance(obj)) {
+                    handlers.push_back(unwrap<LocationHandlerWrap>(obj));
+                }
+            }
+
+            auto source = args[0]->ToObject();
+            if (ReaderWrap::constructor->HasInstance(source)) {
+                osmium::io::Reader& reader = unwrap<ReaderWrap>(source);
+
+                if (reader.eof()) {
+                    return ThrowException(v8::Exception::Error(v8::String::New("apply() called on a reader that has reached EOF")));
+                }
+
+                typedef osmium::io::InputIterator<osmium::io::Reader, osmium::OSMEntity> input_iterator;
+
+                return scope.Close(apply_iterator(input_iterator{reader}, input_iterator{}, handlers));
+            } else if (BufferWrap::constructor->HasInstance(source)) {
+                osmium::memory::Buffer& buffer = unwrap<BufferWrap>(source);
+                return scope.Close(apply_iterator(buffer.begin(), buffer.end(), handlers));
+            } else if (node::Buffer::HasInstance(source)) {
+                osmium::memory::Buffer buffer(reinterpret_cast<unsigned char*>(node::Buffer::Data(source)), node::Buffer::Length(source));
+
+                return scope.Close(apply_iterator(buffer.begin<osmium::OSMEntity>(), buffer.end<osmium::OSMEntity>(), handlers));
+            }
+        }
+
+        return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a Reader or Buffer object as first parameter")));
+    }
+
+} // namespace node_osmium
diff --git a/src/apply.hpp b/src/apply.hpp
new file mode 100644
index 0000000..903327f
--- /dev/null
+++ b/src/apply.hpp
@@ -0,0 +1,17 @@
+#ifndef APPLY_HPP
+#define APPLY_HPP
+
+// v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <v8.h>
+#pragma GCC diagnostic pop
+
+namespace node_osmium {
+
+    v8::Handle<v8::Value> apply(const v8::Arguments& args);
+
+} // namespace node_osmium
+
+#endif // APPLY_HPP
diff --git a/src/buffer_wrap.cpp b/src/buffer_wrap.cpp
new file mode 100644
index 0000000..6f38cb2
--- /dev/null
+++ b/src/buffer_wrap.cpp
@@ -0,0 +1,79 @@
+
+// node
+#include <node_buffer.h>
+
+// node-osmium
+#include "buffer_wrap.hpp"
+#include "osm_node_wrap.hpp"
+#include "osm_way_wrap.hpp"
+#include "osm_relation_wrap.hpp"
+#include "osm_changeset_wrap.hpp"
+#include "utils.hpp"
+
+namespace node_osmium {
+
+    v8::Persistent<v8::FunctionTemplate> BufferWrap::constructor;
+
+    void BufferWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(BufferWrap::New));
+        constructor->InstanceTemplate()->SetInternalFieldCount(1);
+        constructor->SetClassName(v8::String::NewSymbol("Buffer"));
+        node::SetPrototypeMethod(constructor, "next", next);
+        target->Set(v8::String::NewSymbol("Buffer"), constructor->GetFunction());
+    }
+
+    v8::Handle<v8::Value> BufferWrap::New(const v8::Arguments& args) {
+        if (args.Length() == 1 && args[0]->IsExternal()) {
+            v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]);
+            static_cast<BufferWrap*>(ext->Value())->Wrap(args.This());
+            return args.This();
+        } else if (args.Length() == 1 && args[0]->IsObject()) {
+            auto obj = args[0]->ToObject();
+            if (node::Buffer::HasInstance(obj)) {
+                osmium::memory::Buffer buffer(reinterpret_cast<unsigned char*>(node::Buffer::Data(obj)), node::Buffer::Length(obj));
+                BufferWrap* bw = new BufferWrap(std::move(buffer));
+                bw->Wrap(args.This());
+                return args.This();
+            }
+        }
+        return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.Buffer takes a single argument, a node::Buffer")));
+    }
+
+    v8::Handle<v8::Value> BufferWrap::next(const v8::Arguments& args) {
+        BufferWrap* buffer_wrap = node::ObjectWrap::Unwrap<BufferWrap>(args.This());
+        v8::HandleScope scope;
+        if (buffer_wrap->m_iterator == buffer_wrap->m_this->end()) {
+            return scope.Close(v8::Undefined());
+        }
+        osmium::OSMEntity& entity = *buffer_wrap->m_iterator;
+        ++buffer_wrap->m_iterator;
+        switch (entity.type()) {
+            case osmium::item_type::node: {
+                v8::Handle<v8::Value> ext = v8::External::New(new OSMNodeWrap(entity));
+                v8::Local<v8::Object> obj = OSMNodeWrap::constructor->GetFunction()->NewInstance(1, &ext);
+                return scope.Close(obj);
+            }
+            case osmium::item_type::way: {
+                v8::Handle<v8::Value> ext = v8::External::New(new OSMWayWrap(entity));
+                v8::Local<v8::Object> obj = OSMWayWrap::constructor->GetFunction()->NewInstance(1, &ext);
+                return scope.Close(obj);
+            }
+            case osmium::item_type::relation: {
+                v8::Handle<v8::Value> ext = v8::External::New(new OSMRelationWrap(entity));
+                v8::Local<v8::Object> obj = OSMRelationWrap::constructor->GetFunction()->NewInstance(1, &ext);
+                return scope.Close(obj);
+            }
+            case osmium::item_type::changeset: {
+                v8::Handle<v8::Value> ext = v8::External::New(new OSMChangesetWrap(entity));
+                v8::Local<v8::Object> obj = OSMChangesetWrap::constructor->GetFunction()->NewInstance(1, &ext);
+                return scope.Close(obj);
+            }
+            default:
+                break;
+        }
+        return scope.Close(v8::Undefined());
+    }
+
+} // namespace node_osmium
+
diff --git a/src/buffer_wrap.hpp b/src/buffer_wrap.hpp
new file mode 100644
index 0000000..c2db592
--- /dev/null
+++ b/src/buffer_wrap.hpp
@@ -0,0 +1,55 @@
+#ifndef BUFFER_WRAP_HPP
+#define BUFFER_WRAP_HPP
+
+// c++
+#include <memory>
+
+// v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <v8.h>
+#pragma GCC diagnostic pop
+
+// node
+#include <node.h>
+#include <node_version.h>
+#include <node_object_wrap.h>
+
+// osmium
+#include <osmium/memory/buffer.hpp>
+
+namespace node_osmium {
+
+    class BufferWrap : public node::ObjectWrap {
+
+        std::shared_ptr<osmium::memory::Buffer> m_this;
+        osmium::memory::Buffer::iterator m_iterator;
+
+        static v8::Handle<v8::Value> next(const v8::Arguments& args);
+
+    public:
+
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
+        BufferWrap(osmium::memory::Buffer&& buffer) :
+            ObjectWrap(),
+            m_this(std::make_shared<osmium::memory::Buffer>(std::move(buffer))),
+            m_iterator(m_this->begin()) {
+        }
+
+        osmium::memory::Buffer& get() {
+            return *m_this;
+        }
+
+    private:
+
+        ~BufferWrap() {
+        }
+
+    }; // class BufferWrap
+
+} // namespace node_osmium
+
+#endif //  BUFFER_WRAP_HPP
diff --git a/src/file_wrap.cpp b/src/file_wrap.cpp
index 64ea8e0..0bacd01 100644
--- a/src/file_wrap.cpp
+++ b/src/file_wrap.cpp
@@ -5,51 +5,64 @@
 // v8
 #include <v8.h>
 
+// node
+#include <node_buffer.h>
+
 #include "file_wrap.hpp"
 
 namespace node_osmium {
 
-    Persistent<FunctionTemplate> FileWrap::constructor;
+    v8::Persistent<v8::FunctionTemplate> FileWrap::constructor;
 
-    void FileWrap::Initialize(Handle<Object> target) {
-        HandleScope scope;
-        constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(FileWrap::New));
+    void FileWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(FileWrap::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(String::NewSymbol("File"));
-        target->Set(String::NewSymbol("File"), constructor->GetFunction());
+        constructor->SetClassName(v8::String::NewSymbol("File"));
+        target->Set(v8::String::NewSymbol("File"), constructor->GetFunction());
     }
 
-    Handle<Value> FileWrap::New(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> FileWrap::New(const v8::Arguments& args) {
+        v8::HandleScope scope;
+
         if (!args.IsConstructCall()) {
-            return ThrowException(Exception::Error(String::New("Cannot call constructor as function, you need to use 'new' keyword")));
+            return ThrowException(v8::Exception::Error(v8::String::New("Cannot call constructor as function, you need to use 'new' keyword")));
+        }
+
+        if (args.Length() == 0 || args.Length() > 2) {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("File is constructed with one or two arguments: filename or node.Buffer and optional format")));
         }
-        try {
-            if (args.Length() > 2) {
-                return ThrowException(Exception::TypeError(String::New("File is constructed with a maximum of two arguments")));
-            }
 
-            std::string filename;
-            std::string format;
-            if (args.Length() > 0) {
-                if (!args[0]->IsString()) {
-                    return ThrowException(Exception::TypeError(String::New("first argument to File constructor must be a string")));
-                }
-                filename = *String::Utf8Value(args[0]);
+        std::string format;
+        if (args.Length() == 2) {
+            if (!args[1]->IsString()) {
+                return ThrowException(v8::Exception::TypeError(v8::String::New("second argument to File constructor (format) must be a string")));
             }
-            if (args.Length() > 1) {
-                if (!args[1]->IsString()) {
-                    return ThrowException(Exception::TypeError(String::New("second argument to File constructor must be a string")));
-                }
-                format = *String::Utf8Value(args[1]);
+            format = *v8::String::Utf8Value(args[1]);
+        }
+
+        try {
+            osmium::io::File file;
+
+            if (args[0]->IsString()) {
+                std::string filename = *v8::String::Utf8Value(args[0]);
+                file = osmium::io::File(filename, format);
+            } else if (args[0]->IsObject() && node::Buffer::HasInstance(args[0]->ToObject())) {
+                auto source = args[0]->ToObject();
+                file = osmium::io::File(node::Buffer::Data(source), node::Buffer::Length(source), format);
+            } else {
+                return ThrowException(v8::Exception::TypeError(v8::String::New("first argument to File constructor must be a string (filename) or node.Buffer")));
             }
-            FileWrap* q = new FileWrap(filename, format);
-            q->Wrap(args.This());
+
+            file.check();
+            FileWrap* file_wrap = new FileWrap(std::move(file));
+            file_wrap->Wrap(args.This());
             return args.This();
-        } catch (const std::exception& ex) {
-            return ThrowException(Exception::TypeError(String::New(ex.what())));
+        } catch (const std::exception& e) {
+            return ThrowException(v8::Exception::TypeError(v8::String::New(e.what())));
         }
-        return Undefined();
+
+        return scope.Close(v8::Undefined());
     }
 
 } // namespace node_osmium
diff --git a/src/file_wrap.hpp b/src/file_wrap.hpp
index 70ff142..2020d5e 100644
--- a/src/file_wrap.hpp
+++ b/src/file_wrap.hpp
@@ -6,35 +6,37 @@
 #include <string>
 
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
+#pragma GCC diagnostic pop
 
-// node.js
+// node
 #include <node_object_wrap.h>
 
 // osmium
 #include <osmium/io/file.hpp>
 
-using namespace v8;
-
 namespace node_osmium {
 
-    typedef std::shared_ptr<osmium::io::File> file_ptr;
-
     class FileWrap : public node::ObjectWrap {
 
+        std::shared_ptr<osmium::io::File> m_this;
+
     public:
 
-        static Persistent<FunctionTemplate> constructor;
-        static void Initialize(Handle<Object> target);
-        static Handle<Value> New(const Arguments& args);
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
 
-        FileWrap(const std::string& filename, const std::string& format) :
-            ObjectWrap(),
-            m_this(std::make_shared<osmium::io::File>(filename, format)) {
+        FileWrap(osmium::io::File&& file) :
+            node::ObjectWrap(),
+            m_this(std::make_shared<osmium::io::File>(file)) {
         }
 
-        file_ptr get() {
-            return m_this;
+        osmium::io::File& get() {
+            return *m_this;
         }
 
     private:
@@ -42,9 +44,7 @@ namespace node_osmium {
         ~FileWrap() {
         }
 
-        file_ptr m_this;
-
-    };
+    }; // class FileWrap
 
 } // namespace node_osmium
 
diff --git a/src/handler.cpp b/src/handler.cpp
index e842c17..5482fd1 100644
--- a/src/handler.cpp
+++ b/src/handler.cpp
@@ -17,47 +17,60 @@
 #include "osm_node_wrap.hpp"
 #include "osm_way_wrap.hpp"
 #include "osm_relation_wrap.hpp"
+#include "osm_changeset_wrap.hpp"
+#include "utils.hpp"
 
 namespace node_osmium {
 
-    Persistent<FunctionTemplate> JSHandler::constructor;
+    v8::Persistent<v8::FunctionTemplate> JSHandler::constructor;
+    v8::Persistent<v8::String> JSHandler::symbol_tagged_nodes_only;
 
-    void JSHandler::Initialize(Handle<Object> target) {
-        HandleScope scope;
-        constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(JSHandler::New));
+    void JSHandler::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(JSHandler::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(String::NewSymbol("Handler"));
-        NODE_SET_PROTOTYPE_METHOD(constructor, "on", on);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "options", options);
-        target->Set(String::NewSymbol("Handler"), constructor->GetFunction());
+        constructor->SetClassName(v8::String::NewSymbol("Handler"));
+        node::SetPrototypeMethod(constructor, "on", on);
+        node::SetPrototypeMethod(constructor, "options", options);
+        target->Set(v8::String::NewSymbol("Handler"), constructor->GetFunction());
+
+        symbol_tagged_nodes_only = NODE_PSYMBOL("tagged_nodes_only");
     }
 
     JSHandler::JSHandler() :
         ObjectWrap(),
-        node_callback_for_tagged_only(false),
-        done_cb() {
-        done_cb.Clear();
+        node_callback_for_tagged_only(false) {
     }
 
     JSHandler::~JSHandler() {
-        if (!done_cb.IsEmpty()) {
-            done_cb.Dispose();
-        }
-        if (!node_cb.IsEmpty()) {
-            node_cb.Dispose();
-        }
-        if (!way_cb.IsEmpty()) {
-            way_cb.Dispose();
-        }
-        if (!relation_cb.IsEmpty()) {
-            relation_cb.Dispose();
-        }
+        init_cb.Dispose();
+
+        before_nodes_cb.Dispose();
+        node_cb.Dispose();
+        after_nodes_cb.Dispose();
+
+        before_ways_cb.Dispose();
+        way_cb.Dispose();
+        after_ways_cb.Dispose();
+
+        before_relations_cb.Dispose();
+        relation_cb.Dispose();
+        after_relations_cb.Dispose();
+
+        before_changesets_cb.Dispose();
+        changeset_cb.Dispose();
+        after_changesets_cb.Dispose();
+
+        done_cb.Dispose();
     }
 
-    Handle<Value> JSHandler::New(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> JSHandler::New(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        if (!args.IsConstructCall()) {
+            return ThrowException(v8::Exception::Error(v8::String::New("Cannot call constructor as function, you need to use 'new' keyword")));
+        }
         if (args[0]->IsExternal()) {
-            Local<External> ext = Local<External>::Cast(args[0]);
+            v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]);
             void* ptr = ext->Value();
             JSHandler* b =  static_cast<JSHandler*>(ptr);
             b->Wrap(args.This());
@@ -67,147 +80,127 @@ namespace node_osmium {
             h->Wrap(args.This());
             return args.This();
         }
-        return Undefined();
+        return scope.Close(v8::Undefined());
     }
 
-    Handle<Value> JSHandler::options(const Arguments& args) {
-        if (args.Length() == 1) {
-            if (args[0]->IsObject()) {
-                Local<Value> tagged_nodes_only = args[0]->ToObject()->Get(String::New("tagged_nodes_only"));
-                if (tagged_nodes_only->IsBoolean()) {
-                    JSHandler* handler = node::ObjectWrap::Unwrap<JSHandler>(args.This());
-                    handler->node_callback_for_tagged_only = tagged_nodes_only->BooleanValue();
-                }
-            }
+    v8::Handle<v8::Value> JSHandler::options(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        if (args.Length() != 1 || !args[0]->IsObject()) {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a single object as parameter")));
         }
-        return Undefined();
+
+        v8::Local<v8::Value> tagged_nodes_only = args[0]->ToObject()->Get(symbol_tagged_nodes_only);
+        if (tagged_nodes_only->IsBoolean()) {
+            unwrap<JSHandler>(args.This()).node_callback_for_tagged_only = tagged_nodes_only->BooleanValue();
+        }
+
+        return scope.Close(v8::Undefined());
     }
 
-    Handle<Value> JSHandler::on(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> JSHandler::on(const v8::Arguments& args) {
+        v8::HandleScope scope;
         if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsFunction()) {
-            return ThrowException(Exception::TypeError(String::New("please provide an event name and callback function")));
+            return ThrowException(v8::Exception::TypeError(v8::String::New("please provide an event name and callback function")));
         }
-        std::string callback_name = *String::Utf8Value(args[0]->ToString());
-        Local<Function> callback = Local<Function>::Cast(args[1]);
+        std::string callback_name = *v8::String::Utf8Value(args[0]->ToString());
+        v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[1]);
         if (callback->IsNull() || callback->IsUndefined()) {
-            return ThrowException(Exception::TypeError(String::New("please provide a valid callback function for second arg")));
+            return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a valid callback function for second arg")));
         }
-        JSHandler* handler = node::ObjectWrap::Unwrap<JSHandler>(args.This());
+
+        JSHandler& handler = unwrap<JSHandler>(args.This());
         if (callback_name == "node") {
-            if (!handler->node_cb.IsEmpty()) {
-                handler->node_cb.Dispose();
-            }
-            handler->node_cb = Persistent<Function>::New(callback);
+            handler.node_cb.Dispose();
+            handler.node_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "way") {
-            if (!handler->way_cb.IsEmpty()) {
-                handler->way_cb.Dispose();
-            }
-            handler->way_cb = Persistent<Function>::New(callback);
+            handler.way_cb.Dispose();
+            handler.way_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "relation") {
-            if (!handler->relation_cb.IsEmpty()) {
-                handler->relation_cb.Dispose();
-            }
-            handler->relation_cb = Persistent<Function>::New(callback);
+            handler.relation_cb.Dispose();
+            handler.relation_cb = v8::Persistent<v8::Function>::New(callback);
+        } else if (callback_name == "changeset") {
+            handler.changeset_cb.Dispose();
+            handler.changeset_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "init") {
-            if (!handler->init_cb.IsEmpty()) {
-                handler->init_cb.Dispose();
-            }
-            handler->init_cb = Persistent<Function>::New(callback);
+            handler.init_cb.Dispose();
+            handler.init_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "before_nodes") {
-            if (!handler->before_nodes_cb.IsEmpty()) {
-                handler->before_nodes_cb.Dispose();
-            }
-            handler->before_nodes_cb = Persistent<Function>::New(callback);
+            handler.before_nodes_cb.Dispose();
+            handler.before_nodes_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "after_nodes") {
-            if (!handler->after_nodes_cb.IsEmpty()) {
-                handler->after_nodes_cb.Dispose();
-            }
-            handler->after_nodes_cb = Persistent<Function>::New(callback);
+            handler.after_nodes_cb.Dispose();
+            handler.after_nodes_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "before_ways") {
-            if (!handler->before_ways_cb.IsEmpty()) {
-                handler->before_ways_cb.Dispose();
-            }
-            handler->before_ways_cb = Persistent<Function>::New(callback);
+            handler.before_ways_cb.Dispose();
+            handler.before_ways_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "after_ways") {
-            if (!handler->after_ways_cb.IsEmpty()) {
-                handler->after_ways_cb.Dispose();
-            }
-            handler->after_ways_cb = Persistent<Function>::New(callback);
+            handler.after_ways_cb.Dispose();
+            handler.after_ways_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "before_relations") {
-            if (!handler->before_relations_cb.IsEmpty()) {
-                handler->before_relations_cb.Dispose();
-            }
-            handler->before_relations_cb = Persistent<Function>::New(callback);
+            handler.before_relations_cb.Dispose();
+            handler.before_relations_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "after_relations") {
-            if (!handler->after_relations_cb.IsEmpty()) {
-                handler->after_relations_cb.Dispose();
-            }
-            handler->after_relations_cb = Persistent<Function>::New(callback);
+            handler.after_relations_cb.Dispose();
+            handler.after_relations_cb = v8::Persistent<v8::Function>::New(callback);
+        } else if (callback_name == "before_changesets") {
+            handler.before_changesets_cb.Dispose();
+            handler.before_changesets_cb = v8::Persistent<v8::Function>::New(callback);
+        } else if (callback_name == "after_changesets") {
+            handler.after_changesets_cb.Dispose();
+            handler.after_changesets_cb = v8::Persistent<v8::Function>::New(callback);
         } else if (callback_name == "done") {
-            if (!handler->done_cb.IsEmpty()) {
-                handler->done_cb.Dispose();
-            }
-            handler->done_cb = Persistent<Function>::New(callback);
+            handler.done_cb.Dispose();
+            handler.done_cb = v8::Persistent<v8::Function>::New(callback);
+        } else {
+            return ThrowException(v8::Exception::RangeError(v8::String::New("unknown callback name as first argument")));
+        }
+
+        return scope.Close(v8::Undefined());
+    }
+
+    template <class TWrapped>
+    void call_callback_with_entity(const v8::Persistent<v8::Function>& function, const osmium::OSMEntity& entity) {
+        if (function.IsEmpty()) {
+            return;
+        }
+
+        v8::HandleScope scope;
+        v8::Handle<v8::Value> ext = v8::External::New(new TWrapped(entity));
+        v8::Local<v8::Object> obj = TWrapped::constructor->GetFunction()->NewInstance(1, &ext);
+        v8::Local<v8::Value> argv[1] = { obj };
+        function->Call(v8::Context::GetCurrent()->Global(), 1, argv);
+    }
+
+    void call_callback(const v8::Persistent<v8::Function>& function) {
+        if (function.IsEmpty()) {
+            return;
         }
-        return scope.Close(Undefined());
+
+        v8::HandleScope scope;
+        v8::Local<v8::Value> argv[0] = { };
+        function->Call(v8::Context::GetCurrent()->Global(), 0, argv);
     }
 
-    void JSHandler::dispatch_object(const input_iterator& it) {
-        HandleScope scope;
-        switch (it->type()) {
+    void JSHandler::dispatch_entity(const osmium::OSMEntity& entity) const {
+        switch (entity.type()) {
             case osmium::item_type::node:
-                if (!node_cb.IsEmpty() && (!node_callback_for_tagged_only || !it->tags().empty())) {
-                    const int argc = 1;
-
-                    Handle<Value> ext = External::New(new OSMNodeWrap(it));
-                    Local<Object> obj = OSMNodeWrap::constructor->GetFunction()->NewInstance(1, &ext);
-                    Local<Value> argv[argc] = { obj };
-
-                    TryCatch trycatch;
-                    Handle<Value> v = node_cb->Call(Context::GetCurrent()->Global(), argc, argv);
-                    if (v.IsEmpty()) {
-                        Handle<Value> exception = trycatch.Exception();
-                        String::AsciiValue exception_str(exception);
-                        printf("Exception: %s\n", *exception_str);
-                        exit(1);
-                    }
+                if (!node_cb.IsEmpty() && (!node_callback_for_tagged_only || !static_cast<const osmium::Node&>(entity).tags().empty())) {
+                    call_callback_with_entity<OSMNodeWrap>(node_cb, entity);
                 }
                 break;
             case osmium::item_type::way:
                 if (!way_cb.IsEmpty()) {
-                    const int argc = 1;
-
-                    Handle<Value> ext = External::New(new OSMWayWrap(it));
-                    Local<Object> obj = OSMWayWrap::constructor->GetFunction()->NewInstance(1, &ext);
-                    Local<Value> argv[argc] = { obj };
-
-                    TryCatch trycatch;
-                    Handle<Value> v = way_cb->Call(Context::GetCurrent()->Global(), argc, argv);
-                    if (v.IsEmpty()) {
-                        Handle<Value> exception = trycatch.Exception();
-                        String::AsciiValue exception_str(exception);
-                        printf("Exception: %s\n", *exception_str);
-                        exit(1);
-                    }
+                    call_callback_with_entity<OSMWayWrap>(way_cb, entity);
                 }
                 break;
             case osmium::item_type::relation:
                 if (!relation_cb.IsEmpty()) {
-                    const int argc = 1;
-
-                    Handle<Value> ext = External::New(new OSMRelationWrap(it));
-                    Local<Object> obj = OSMRelationWrap::constructor->GetFunction()->NewInstance(1, &ext);
-                    Local<Value> argv[argc] = { obj };
-
-                    TryCatch trycatch;
-                    Handle<Value> v = relation_cb->Call(Context::GetCurrent()->Global(), argc, argv);
-                    if (v.IsEmpty()) {
-                        Handle<Value> exception = trycatch.Exception();
-                        String::AsciiValue exception_str(exception);
-                        printf("Exception: %s\n", *exception_str);
-                        exit(1);
-                    }
+                    call_callback_with_entity<OSMRelationWrap>(relation_cb, entity);
+                }
+                break;
+            case osmium::item_type::changeset:
+                if (!changeset_cb.IsEmpty()) {
+                    call_callback_with_entity<OSMChangesetWrap>(changeset_cb, entity);
                 }
                 break;
             default:
@@ -215,144 +208,44 @@ namespace node_osmium {
         }
     }
 
-    void JSHandler::init() {
-        if (!init_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = init_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::init() const {
+        call_callback(init_cb);
     }
 
-    void JSHandler::before_nodes() {
-        if (!before_nodes_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = before_nodes_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::before_nodes() const {
+        call_callback(before_nodes_cb);
     }
 
-    void JSHandler::after_nodes() {
-        if (!after_nodes_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = after_nodes_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::after_nodes() const {
+        call_callback(after_nodes_cb);
     }
 
-    void JSHandler::before_ways() {
-        if (!before_ways_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = before_ways_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::before_ways() const {
+        call_callback(before_ways_cb);
     }
 
-    void JSHandler::after_ways() {
-        if (!after_ways_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = after_ways_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::after_ways() const {
+        call_callback(after_ways_cb);
     }
 
-    void JSHandler::before_relations() {
-        if (!before_relations_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = before_relations_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::before_relations() const {
+        call_callback(before_relations_cb);
     }
 
-    void JSHandler::after_relations() {
-        if (!after_relations_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = after_relations_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::after_relations() const {
+        call_callback(after_relations_cb);
     }
 
-    void JSHandler::before_changesets() {
-        if (!before_changesets_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = before_changesets_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::before_changesets() const {
+        call_callback(before_changesets_cb);
     }
 
-    void JSHandler::after_changesets() {
-        if (!after_changesets_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = after_changesets_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::after_changesets() const {
+        call_callback(after_changesets_cb);
     }
 
-    void JSHandler::done() {
-        if (!done_cb.IsEmpty()) {
-            Local<Value> argv[0] = { };
-            TryCatch trycatch;
-            Handle<Value> v = done_cb->Call(Context::GetCurrent()->Global(), 0, argv);
-            if (v.IsEmpty()) {
-                Handle<Value> exception = trycatch.Exception();
-                String::AsciiValue exception_str(exception);
-                printf("Exception: %s\n", *exception_str);
-                exit(1);
-            }
-        }
+    void JSHandler::done() const {
+        call_callback(done_cb);
     }
 
 } // namespace node_osmium
diff --git a/src/handler.hpp b/src/handler.hpp
index d529bc7..843874e 100644
--- a/src/handler.hpp
+++ b/src/handler.hpp
@@ -1,68 +1,83 @@
+#ifndef HANDLER_HPP
+#define HANDLER_HPP
 
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
+#pragma GCC diagnostic pop
 
-// node.js
+// node
 #include <node_object_wrap.h>
 
-// node-osmium
-#include "osm_object_wrap.hpp"
+// osmium
+namespace osmium {
+    class OSMEntity;
+}
 
 namespace node_osmium {
 
-    using namespace v8;
-
     class JSHandler : public node::ObjectWrap {
 
-    public:
+        static v8::Persistent<v8::String> symbol_tagged_nodes_only;
 
-        static Persistent<FunctionTemplate> constructor;
-        static void Initialize(Handle<Object> target);
-        static Handle<Value> New(const Arguments& args);
-        static Handle<Value> on(const Arguments& args);
-        static Handle<Value> options(const Arguments& args);
-        JSHandler();
+        bool node_callback_for_tagged_only;
 
-        void dispatch_object(const input_iterator& it);
+        v8::Persistent<v8::Function> init_cb;
 
-        void init();
-        void before_nodes();
-        void after_nodes();
-        void before_ways();
-        void after_ways();
-        void before_relations();
-        void after_relations();
-        void before_changesets();
-        void after_changesets();
-        void done();
+        v8::Persistent<v8::Function> before_nodes_cb;
+        v8::Persistent<v8::Function> node_cb;
+        v8::Persistent<v8::Function> after_nodes_cb;
 
-        bool node_callback_for_tagged_only;
+        v8::Persistent<v8::Function> before_ways_cb;
+        v8::Persistent<v8::Function> way_cb;
+        v8::Persistent<v8::Function> after_ways_cb;
+
+        v8::Persistent<v8::Function> before_relations_cb;
+        v8::Persistent<v8::Function> relation_cb;
+        v8::Persistent<v8::Function> after_relations_cb;
 
-        Persistent<Function> init_cb;
+        v8::Persistent<v8::Function> before_changesets_cb;
+        v8::Persistent<v8::Function> changeset_cb;
+        v8::Persistent<v8::Function> after_changesets_cb;
 
-        Persistent<Function> before_nodes_cb;
-        Persistent<Function> node_cb;
-        Persistent<Function> after_nodes_cb;
+        v8::Persistent<v8::Function> done_cb;
 
-        Persistent<Function> before_ways_cb;
-        Persistent<Function> way_cb;
-        Persistent<Function> after_ways_cb;
+        static v8::Handle<v8::Value> on(const v8::Arguments& args);
+        static v8::Handle<v8::Value> options(const v8::Arguments& args);
+
+    public:
+
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
+
+        JSHandler();
 
-        Persistent<Function> before_relations_cb;
-        Persistent<Function> relation_cb;
-        Persistent<Function> after_relations_cb;
+        JSHandler& get() {
+            return *this;
+        }
 
-        Persistent<Function> before_changesets_cb;
-        Persistent<Function> changeset_cb;
-        Persistent<Function> after_changesets_cb;
+        void dispatch_entity(const osmium::OSMEntity& entity) const;
 
-        Persistent<Function> done_cb;
+        void init() const;
+        void before_nodes() const;
+        void after_nodes() const;
+        void before_ways() const;
+        void after_ways() const;
+        void before_relations() const;
+        void after_relations() const;
+        void before_changesets() const;
+        void after_changesets() const;
+        void done() const;
 
     private:
 
         ~JSHandler();
 
-    };
+    }; // class JSHandler
 
 } // namespace node_osmium
 
+#endif // HANDLER_HPP
diff --git a/src/location_handler_wrap.cpp b/src/location_handler_wrap.cpp
index 617df2c..63a2e10 100644
--- a/src/location_handler_wrap.cpp
+++ b/src/location_handler_wrap.cpp
@@ -1,37 +1,57 @@
 
 #include "location_handler_wrap.hpp"
+#include "utils.hpp"
 
 namespace node_osmium {
 
-    Persistent<FunctionTemplate> LocationHandlerWrap::constructor;
+    v8::Persistent<v8::FunctionTemplate> LocationHandlerWrap::constructor;
 
-    void LocationHandlerWrap::Initialize(Handle<Object> target) {
-        HandleScope scope;
-        constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(LocationHandlerWrap::New));
+    void LocationHandlerWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(LocationHandlerWrap::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(String::NewSymbol("LocationHandler"));
-        NODE_SET_PROTOTYPE_METHOD(constructor, "clear", clear);
-        target->Set(String::NewSymbol("LocationHandler"), constructor->GetFunction());
+        constructor->SetClassName(v8::String::NewSymbol("LocationHandler"));
+        node::SetPrototypeMethod(constructor, "clear", clear);
+        node::SetPrototypeMethod(constructor, "ignoreErrors", ignoreErrors);
+        target->Set(v8::String::NewSymbol("LocationHandler"), constructor->GetFunction());
     }
 
-    Handle<Value> LocationHandlerWrap::New(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> LocationHandlerWrap::New(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        if (!args.IsConstructCall()) {
+            return ThrowException(v8::Exception::Error(v8::String::New("Cannot call constructor as function, you need to use 'new' keyword")));
+        }
+
         try {
-            if (!args.IsConstructCall()) {
-                return ThrowException(Exception::Error(String::New("Cannot call constructor as function, you need to use 'new' keyword")));
+            LocationHandlerWrap* location_handler_wrap;
+            if (args.Length() == 0) {
+                location_handler_wrap = new LocationHandlerWrap("sparsetable");
+            } else {
+                if (args.Length() != 1) {
+                    return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a node cache type as string when creating a LocationHandler")));
+                }
+                if (!args[0]->IsString()) {
+                    return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a node cache type as string when creating a LocationHandler")));
+                }
+                location_handler_wrap = new LocationHandlerWrap(*v8::String::Utf8Value(args[0]));
             }
-            LocationHandlerWrap* q = new LocationHandlerWrap();
-            q->Wrap(args.This());
+            location_handler_wrap->Wrap(args.This());
             return args.This();
         } catch (const std::exception& ex) {
-            return ThrowException(Exception::TypeError(String::New(ex.what())));
+            return ThrowException(v8::Exception::TypeError(v8::String::New(ex.what())));
         }
     }
 
-    Handle<Value> LocationHandlerWrap::clear(const Arguments& args) {
-        HandleScope scope;
-        // XXX do something here
-        return scope.Close(Undefined());
+    v8::Handle<v8::Value> LocationHandlerWrap::ignoreErrors(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        unwrap<LocationHandlerWrap>(args.This()).ignore_errors();
+        return scope.Close(v8::Undefined());
+    }
+
+    v8::Handle<v8::Value> LocationHandlerWrap::clear(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        unwrap<LocationHandlerWrap>(args.This()).clear();
+        return scope.Close(v8::Undefined());
     }
 
 } // namespace node_osmium
diff --git a/src/location_handler_wrap.hpp b/src/location_handler_wrap.hpp
index 4d058ad..1d47a6e 100644
--- a/src/location_handler_wrap.hpp
+++ b/src/location_handler_wrap.hpp
@@ -5,9 +5,13 @@
 #include <memory>
 
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
+#pragma GCC diagnostic pop
 
-// node.js
+// node
 #include <node_object_wrap.h>
 
 // osmium
@@ -15,48 +19,77 @@
 #include <osmium/index/map/dummy.hpp>
 #include <osmium/index/map/stl_map.hpp>
 #include <osmium/index/map/sparse_table.hpp>
-
-using namespace v8;
+#include <osmium/index/map/mmap_vector_anon.hpp>
+#include <osmium/index/map/mmap_vector_file.hpp>
 
 namespace node_osmium {
 
     typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_neg_type;
-    typedef osmium::index::map::SparseTable<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+    typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+    typedef osmium::index::map::SparseTable<osmium::unsigned_object_id_type, osmium::Location> index_sparsetable_type;
+    typedef osmium::index::map::StlMap<osmium::unsigned_object_id_type, osmium::Location> index_stlmap_type;
+    typedef osmium::index::map::DenseMapFile<osmium::unsigned_object_id_type, osmium::Location> index_disk_type;
+
+#ifdef __linux__
+    typedef osmium::index::map::DenseMapMmap<osmium::unsigned_object_id_type, osmium::Location> index_array_type;
+#endif
+
     typedef osmium::handler::NodeLocationsForWays<index_pos_type, index_neg_type> location_handler_type;
 
-    typedef std::shared_ptr<location_handler_type> location_handler_ptr;
+    inline index_pos_type* node_cache_factory(const std::string& cache_options) {
+        size_t comma = cache_options.find_first_of(',');
+        std::string cache_type = comma == std::string::npos ? cache_options : cache_options.substr(0, comma);
+        if (cache_type == "sparsetable") {
+            return new index_sparsetable_type;
+        } else if (cache_type == "stlmap") {
+            return new index_stlmap_type;
+#ifdef __linux__
+        } else if (cache_type == "array") {
+            return new index_array_type;
+#endif
+        } else if (cache_type == "disk") {
+            if (comma != std::string::npos) {
+                std::string filename = cache_options.substr(comma + 1);
+                int fd = ::open(filename.c_str(), O_RDWR);
+                if (fd == -1) {
+                    throw std::runtime_error(std::string("can't open file '") + filename + "': " + strerror(errno));
+                }
+                return new index_disk_type(fd);
+            } else {
+                return new index_disk_type;
+            }
+        }
+        return nullptr;
+    }
 
     class LocationHandlerWrap : public node::ObjectWrap {
 
-    public:
-
-        static Persistent<FunctionTemplate> constructor;
+        std::unique_ptr<index_pos_type> m_index_pos;
+        std::unique_ptr<index_neg_type> m_index_neg;
 
-        static void Initialize(Handle<Object> target);
-        static Handle<Value> New(const Arguments& args);
-        static Handle<Value> clear(const Arguments& args);
+        std::shared_ptr<location_handler_type> m_this;
 
-        static location_handler_type& wrapped(Local<Object> object) {
-            return *(node::ObjectWrap::Unwrap<LocationHandlerWrap>(object)->get());
-        }
+        static v8::Handle<v8::Value> clear(const v8::Arguments& args);
+        static v8::Handle<v8::Value> ignoreErrors(const v8::Arguments& args);
 
-        LocationHandlerWrap() :
-            ObjectWrap(),
-            m_index_pos(),
-            m_index_neg(),
-            m_this(std::make_shared<location_handler_type>(m_index_pos, m_index_neg)) {
-        }
+    public:
 
-        void _ref() {
-            Ref();
-        }
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
 
-        void _unref() {
-            Unref();
+        LocationHandlerWrap(const std::string& cache_type) :
+            ObjectWrap(),
+            m_index_pos(node_cache_factory(cache_type)),
+            m_index_neg(new index_neg_type),
+            m_this(std::make_shared<location_handler_type>(*m_index_pos, *m_index_neg)) {
+            if (!m_index_pos) {
+                throw std::runtime_error("unknown node cache type");
+            }
         }
 
-        location_handler_ptr get() {
-            return m_this;
+        location_handler_type& get() {
+            return *m_this;
         }
 
     private:
@@ -64,11 +97,7 @@ namespace node_osmium {
         ~LocationHandlerWrap() {
         }
 
-        index_pos_type m_index_pos;
-        index_neg_type m_index_neg;
-        location_handler_ptr m_this;
-
-    };
+    }; // class LocationHandlerWrap
 
 } // namespace node_osmium
 
diff --git a/src/node_osmium.cpp b/src/node_osmium.cpp
index 5d6b456..16b6e47 100644
--- a/src/node_osmium.cpp
+++ b/src/node_osmium.cpp
@@ -1,40 +1,59 @@
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
+#pragma GCC diagnostic pop
 
-// node.js
+// node
 #include <node.h>
 
 // osmium
 #include <osmium/geom/wkb.hpp>
 #include <osmium/geom/wkt.hpp>
+#include <osmium/io/input_iterator.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/visitor.hpp>
 
 // node-osmium
-#include "osm_node_wrap.hpp"
-#include "osm_way_wrap.hpp"
-#include "osm_relation_wrap.hpp"
+#include "apply.hpp"
+#include "buffer_wrap.hpp"
+#include "file_wrap.hpp"
 #include "handler.hpp"
 #include "location_handler_wrap.hpp"
-#include "file_wrap.hpp"
+#include "osm_changeset_wrap.hpp"
+#include "osm_node_wrap.hpp"
+#include "osm_relation_wrap.hpp"
+#include "osm_way_wrap.hpp"
 #include "reader_wrap.hpp"
 
 namespace node_osmium {
 
+    v8::Persistent<v8::Object> module;
+    osmium::geom::WKBFactory<> wkb_factory;
+    osmium::geom::WKTFactory<> wkt_factory;
+
     extern "C" {
         static void start(v8::Handle<v8::Object> target) {
             v8::HandleScope scope;
+            module = v8::Persistent<v8::Object>::New(target);
+
+            node::SetMethod(target, "apply", node_osmium::apply);
+
+            node_osmium::OSMEntityWrap::Initialize(target);
+            node_osmium::OSMObjectWrap::Initialize(target);
             node_osmium::OSMNodeWrap::Initialize(target);
             node_osmium::OSMWayWrap::Initialize(target);
             node_osmium::OSMRelationWrap::Initialize(target);
+            node_osmium::OSMChangesetWrap::Initialize(target);
             node_osmium::LocationHandlerWrap::Initialize(target);
             node_osmium::JSHandler::Initialize(target);
             node_osmium::FileWrap::Initialize(target);
             node_osmium::ReaderWrap::Initialize(target);
+            node_osmium::BufferWrap::Initialize(target);
         }
     }
 
-    osmium::geom::WKBFactory wkb_factory;
-    osmium::geom::WKTFactory wkt_factory;
-
 } // namespace node_osmium
 
 NODE_MODULE(osmium, node_osmium::start)
diff --git a/src/osm_changeset_wrap.cpp b/src/osm_changeset_wrap.cpp
new file mode 100644
index 0000000..f8a02ca
--- /dev/null
+++ b/src/osm_changeset_wrap.cpp
@@ -0,0 +1,89 @@
+
+#include "osm_changeset_wrap.hpp"
+#include "utils.hpp"
+
+namespace node_osmium {
+
+    extern v8::Persistent<v8::Object> module;
+
+    v8::Persistent<v8::FunctionTemplate> OSMChangesetWrap::constructor;
+
+    void OSMChangesetWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMChangesetWrap::New));
+        constructor->Inherit(OSMEntityWrap::constructor);
+        constructor->InstanceTemplate()->SetInternalFieldCount(1);
+        constructor->SetClassName(v8::String::NewSymbol("Changeset"));
+        auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "id", get_id, attributes);
+        set_accessor(constructor, "uid", get_uid, attributes);
+        set_accessor(constructor, "user", get_user, attributes);
+        set_accessor(constructor, "num_changes", get_num_changes, attributes);
+        set_accessor(constructor, "created_at_seconds_since_epoch", get_created_at, attributes);
+        set_accessor(constructor, "closed_at_seconds_since_epoch", get_closed_at, attributes);
+        set_accessor(constructor, "open", get_open, attributes);
+        set_accessor(constructor, "closed", get_closed, attributes);
+        set_accessor(constructor, "bounds", get_bounds, attributes);
+        node::SetPrototypeMethod(constructor, "tags", tags);
+        target->Set(v8::String::NewSymbol("Changeset"), constructor->GetFunction());
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::New(const v8::Arguments& args) {
+        if (args.Length() == 1 && args[0]->IsExternal()) {
+            v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]);
+            static_cast<OSMChangesetWrap*>(ext->Value())->Wrap(args.This());
+            return args.This();
+        } else {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.OSMChangeset cannot be created in Javascript")));
+        }
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::tags(const v8::Arguments& args) {
+        return OSMEntityWrap::tags_impl<osmium::Changeset>(args);
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_id(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).id()));
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_uid(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).uid()));
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_user(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::String::New(wrapped(info.This()).user()));
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_num_changes(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).num_changes()));
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_created_at(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).created_at()));
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_closed_at(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).closed_at()));
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_open(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Boolean::New(wrapped(info.This()).open()));
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_closed(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Boolean::New(wrapped(info.This()).closed()));
+    }
+
+    v8::Handle<v8::Value> OSMChangesetWrap::get_bounds(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        return create_js_box(wrapped(info.This()).bounds());
+    }
+
+} // namespace node_osmium
diff --git a/src/osm_changeset_wrap.hpp b/src/osm_changeset_wrap.hpp
new file mode 100644
index 0000000..ca605f9
--- /dev/null
+++ b/src/osm_changeset_wrap.hpp
@@ -0,0 +1,54 @@
+#ifndef OSM_CHANGESET_WRAP_HPP
+#define OSM_CHANGESET_WRAP_HPP
+
+// v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <v8.h>
+#pragma GCC diagnostic pop
+
+// osmium
+#include <osmium/osm/changeset.hpp>
+namespace osmium {
+    class OSMEntity;
+}
+
+// node-osmium
+#include "osm_entity_wrap.hpp"
+#include "utils.hpp"
+
+namespace node_osmium {
+
+    class OSMChangesetWrap : public OSMEntityWrap {
+
+        static v8::Handle<v8::Value> get_id(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_uid(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_user(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_num_changes(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_created_at(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_closed_at(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_open(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_closed(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_bounds(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> tags(const v8::Arguments& args);
+
+    public:
+
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
+
+        static const osmium::Changeset& wrapped(const v8::Local<v8::Object>& object) {
+            return static_cast<const osmium::Changeset&>(unwrap<OSMEntityWrap>(object));
+        }
+
+        OSMChangesetWrap(const osmium::OSMEntity& entity) :
+            OSMEntityWrap(entity) {
+        }
+
+    }; // class OSMChangesetWrap
+
+} // namespace node_osmium
+
+#endif // OSM_CHANGESET_WRAP_HPP
diff --git a/src/osm_entity_wrap.cpp b/src/osm_entity_wrap.cpp
new file mode 100644
index 0000000..91191f0
--- /dev/null
+++ b/src/osm_entity_wrap.cpp
@@ -0,0 +1,26 @@
+
+#include "osm_entity_wrap.hpp"
+
+namespace node_osmium {
+
+    v8::Persistent<v8::FunctionTemplate> OSMEntityWrap::constructor;
+
+    void OSMEntityWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMEntityWrap::New));
+        constructor->InstanceTemplate()->SetInternalFieldCount(1);
+        constructor->SetClassName(v8::String::NewSymbol("OSMEntity"));
+        target->Set(v8::String::NewSymbol("OSMEntity"), constructor->GetFunction());
+    }
+
+    v8::Handle<v8::Value> OSMEntityWrap::New(const v8::Arguments& args) {
+        if (args.Length() == 1 && args[0]->IsExternal()) {
+            v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]);
+            static_cast<OSMEntityWrap*>(ext->Value())->Wrap(args.This());
+            return args.This();
+        } else {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.OSMEntity cannot be created in Javascript")));
+        }
+    }
+
+} // namespace node_osmium
diff --git a/src/osm_entity_wrap.hpp b/src/osm_entity_wrap.hpp
new file mode 100644
index 0000000..bf25136
--- /dev/null
+++ b/src/osm_entity_wrap.hpp
@@ -0,0 +1,78 @@
+#ifndef OSM_ENTITY_WRAP_HPP
+#define OSM_ENTITY_WRAP_HPP
+
+// v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <v8.h>
+#pragma GCC diagnostic pop
+
+// node
+#include <node_object_wrap.h>
+
+// osmium
+namespace osmium {
+    class OSMEntity;
+}
+
+#include "utils.hpp"
+
+namespace node_osmium {
+
+    class OSMEntityWrap : public node::ObjectWrap {
+
+        const osmium::OSMEntity* m_entity;
+
+    protected:
+
+        typedef v8::Handle<v8::Value> accessor_type(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+
+        static void set_accessor(v8::Persistent<v8::FunctionTemplate> t, const char* name, accessor_type getter, v8::PropertyAttribute attributes) {
+            t->InstanceTemplate()->SetAccessor(v8::String::NewSymbol(name), getter, nullptr, v8::Handle<v8::Value>(), v8::DEFAULT, attributes);
+        }
+
+        template<class T>
+        static v8::Handle<v8::Value> tags_impl(const v8::Arguments& args) {
+            v8::HandleScope scope;
+
+            const T& object = static_cast<const T&>(unwrap<OSMEntityWrap>(args.This()));
+            switch (args.Length()) {
+                case 0: {
+                    v8::Local<v8::Object> tags = v8::Object::New();
+                    for (const auto& tag : object.tags()) {
+                        tags->Set(v8::String::New(tag.key()), v8::String::New(tag.value()));
+                    }
+                    return scope.Close(tags);
+                }
+                case 1: {
+                    if (!args[0]->IsString()) {
+                        break;
+                    }
+                    const char* value = object.tags().get_value_by_key(*v8::String::Utf8Value(args[0]));
+                    return scope.Close(value ? v8::String::New(value) : v8::Undefined());
+                }
+            }
+
+            return ThrowException(v8::Exception::TypeError(v8::String::New("call tags() without parameters or with a string (the key)")));
+        }
+
+    public:
+
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
+
+        OSMEntityWrap(const osmium::OSMEntity& entity) :
+            m_entity(&entity) {
+        }
+
+        const osmium::OSMEntity& get() {
+            return *m_entity;
+        }
+
+    }; // class OSMEntityWrap
+
+} // namespace node_osmium
+
+#endif // OSM_ENTITY_WRAP_HPP
diff --git a/src/osm_node_wrap.cpp b/src/osm_node_wrap.cpp
index 82ba6bc..57a0f2c 100644
--- a/src/osm_node_wrap.cpp
+++ b/src/osm_node_wrap.cpp
@@ -1,85 +1,96 @@
 
+// node
+#include <node_buffer.h>
+#include <node_version.h>
+
+// osmium
 #include <osmium/geom/wkb.hpp>
 #include <osmium/geom/wkt.hpp>
 
+// node-osmium
 #include "osm_node_wrap.hpp"
 
 namespace node_osmium {
 
-    extern osmium::geom::WKBFactory wkb_factory;
-    extern osmium::geom::WKTFactory wkt_factory;
+    extern v8::Persistent<v8::Object> module;
+    extern osmium::geom::WKBFactory<> wkb_factory;
+    extern osmium::geom::WKTFactory<> wkt_factory;
 
-    Persistent<FunctionTemplate> OSMNodeWrap::constructor;
+    v8::Persistent<v8::FunctionTemplate> OSMNodeWrap::constructor;
 
-    void OSMNodeWrap::Initialize(Handle<Object> target) {
-        HandleScope scope;
-        constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(OSMNodeWrap::New));
+    void OSMNodeWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMNodeWrap::New));
+        constructor->Inherit(OSMObjectWrap::constructor);
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(String::NewSymbol("Node"));
-        NODE_SET_PROTOTYPE_METHOD(constructor, "tags", tags);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "wkb", wkb);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "wkt", wkt);
-        enum PropertyAttribute attributes =
-            static_cast<PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
-        SET_ACCESSOR(constructor, "id", get_id, attributes);
-        SET_ACCESSOR(constructor, "version", get_version, attributes);
-        SET_ACCESSOR(constructor, "changeset", get_changeset, attributes);
-        SET_ACCESSOR(constructor, "visible", get_visible, attributes);
-        SET_ACCESSOR(constructor, "timestamp", get_timestamp, attributes);
-        SET_ACCESSOR(constructor, "uid", get_uid, attributes);
-        SET_ACCESSOR(constructor, "user", get_user, attributes);
-        SET_ACCESSOR(constructor, "lon", get_lon, attributes);
-        SET_ACCESSOR(constructor, "lat", get_lat, attributes);
-        target->Set(String::NewSymbol("Node"), constructor->GetFunction());
-    }
-
-    OSMNodeWrap::OSMNodeWrap(const input_iterator& it) :
-        OSMObjectWrap(it) {
+        constructor->SetClassName(v8::String::NewSymbol("Node"));
+        node::SetPrototypeMethod(constructor, "wkb", wkb);
+        node::SetPrototypeMethod(constructor, "wkt", wkt);
+        auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "location", get_coordinates, attributes);
+        set_accessor(constructor, "coordinates", get_coordinates, attributes);
+        set_accessor(constructor, "lon", get_lon, attributes);
+        set_accessor(constructor, "lat", get_lat, attributes);
+        target->Set(v8::String::NewSymbol("Node"), constructor->GetFunction());
     }
 
-    OSMNodeWrap::~OSMNodeWrap() {
-    }
-
-    Handle<Value> OSMNodeWrap::New(const Arguments& args) {
-        HandleScope scope;
-        if (args[0]->IsExternal()) {
-            Local<External> ext = Local<External>::Cast(args[0]);
-            void* ptr = ext->Value();
-            OSMNodeWrap* node = static_cast<OSMNodeWrap*>(ptr);
-            node->Wrap(args.This());
+    v8::Handle<v8::Value> OSMNodeWrap::New(const v8::Arguments& args) {
+        if (args.Length() == 1 && args[0]->IsExternal()) {
+            v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]);
+            static_cast<OSMNodeWrap*>(ext->Value())->Wrap(args.This());
             return args.This();
         } else {
-            return ThrowException(Exception::TypeError(String::New("osmium.Node cannot be created in Javascript")));
+            return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.Node cannot be created in Javascript")));
         }
-        return scope.Close(Undefined());
     }
 
-    Handle<Value> OSMNodeWrap::get_lon(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(Number::New(wrapped(info.This()).lon()));
+    v8::Handle<v8::Value> OSMNodeWrap::get_coordinates(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+
+        auto lon = v8::Number::New(wrapped(info.This()).location().lon());
+        auto lat = v8::Number::New(wrapped(info.This()).location().lat());
+
+        auto cf = module->Get(v8::String::NewSymbol("Coordinates"));
+        assert(cf->IsFunction());
+
+        v8::Local<v8::Value> argv[2] = { lon, lat };
+        return scope.Close(v8::Local<v8::Function>::Cast(cf)->NewInstance(2, argv));
+    }
+
+    v8::Handle<v8::Value> OSMNodeWrap::get_lon(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).location().lon()));
     }
 
-    Handle<Value> OSMNodeWrap::get_lat(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(Number::New(wrapped(info.This()).lat()));
+    v8::Handle<v8::Value> OSMNodeWrap::get_lat(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).location().lat()));
     }
 
-    Handle<Value> OSMNodeWrap::wkb(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> OSMNodeWrap::wkb(const v8::Arguments& args) {
+        v8::HandleScope scope;
 
-        std::string wkb { wkb_factory.create_point(wrapped(args.This())) };
+        try {
+            std::string wkb { wkb_factory.create_point(wrapped(args.This())) };
 #if NODE_VERSION_AT_LEAST(0, 10, 0)
-        return scope.Close(node::Buffer::New(wkb.data(), wkb.size())->handle_);
+            return scope.Close(node::Buffer::New(wkb.data(), wkb.size())->handle_);
 #else
-        return scope.Close(node::Buffer::New(const_cast<char*>(wkb.data()), wkb.size())->handle_);
+            return scope.Close(node::Buffer::New(const_cast<char*>(wkb.data()), wkb.size())->handle_);
 #endif
+        } catch (std::runtime_error& e) {
+            return ThrowException(v8::Exception::Error(v8::String::New(e.what())));
+        }
     }
 
-    Handle<Value> OSMNodeWrap::wkt(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> OSMNodeWrap::wkt(const v8::Arguments& args) {
+        v8::HandleScope scope;
 
-        std::string wkt { wkt_factory.create_point(wrapped(args.This())) };
-
-        return scope.Close(String::New(wkt.c_str()));
+        try {
+            std::string wkt { wkt_factory.create_point(wrapped(args.This())) };
+            return scope.Close(v8::String::New(wkt.c_str()));
+        } catch (std::runtime_error& e) {
+            return ThrowException(v8::Exception::Error(v8::String::New(e.what())));
+        }
     }
+
 } // namespace node_osmium
diff --git a/src/osm_node_wrap.hpp b/src/osm_node_wrap.hpp
index 0a92d4f..157df91 100644
--- a/src/osm_node_wrap.hpp
+++ b/src/osm_node_wrap.hpp
@@ -1,54 +1,55 @@
-#ifndef NODE_WRAP_HPP
-#define NODE_WRAP_HPP
+#ifndef OSM_NODE_WRAP_HPP
+#define OSM_NODE_WRAP_HPP
 
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
-
-// node
-#include <node.h>
-#include <node_version.h>
-#include <node_object_wrap.h>
-#include <node_buffer.h>
+#pragma GCC diagnostic pop
 
 // osmium
 #include <osmium/osm/node.hpp>
-#include <osmium/io/input_iterator.hpp>
-#include <osmium/io/reader.hpp>
+namespace osmium {
+    class OSMEntity;
+}
 
+// node-osmium
+#include "osm_entity_wrap.hpp"
 #include "osm_object_wrap.hpp"
-
-using namespace v8;
+#include "utils.hpp"
 
 namespace node_osmium {
 
     class OSMNodeWrap : public OSMObjectWrap {
 
+        static v8::Handle<v8::Value> get_coordinates(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_lon(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_lat(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> wkb(const v8::Arguments& args);
+        static v8::Handle<v8::Value> wkt(const v8::Arguments& args);
+
     public:
 
-        static Persistent<FunctionTemplate> constructor;
-        static void Initialize(Handle<Object> target);
-        static Handle<Value> New(const Arguments& args);
-        static Handle<Value> wkb(const Arguments& args);
-        static Handle<Value> wkt(const Arguments& args);
-        static Handle<Value> get_lon(Local<String> property,const AccessorInfo& info);
-        static Handle<Value> get_lat(Local<String> property,const AccessorInfo& info);
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
 
-        static osmium::Node& wrapped(Local<Object> object) {
-            return static_cast<osmium::Node&>(OSMObjectWrap::wrapped(object));
+        static const osmium::Node& wrapped(const v8::Local<v8::Object>& object) {
+            return static_cast<const osmium::Node&>(unwrap<OSMEntityWrap>(object));
         }
 
-        OSMNodeWrap(const input_iterator&);
-
-        osmium::Node& object() {
-            return static_cast<osmium::Node&>(*get());
+        OSMNodeWrap(const osmium::OSMEntity& entity) :
+            OSMObjectWrap(entity) {
         }
 
     private:
 
-        ~OSMNodeWrap();
+        ~OSMNodeWrap() {
+        }
 
-    };
+    }; // class OSMNodeWrap
 
 } // namespace node_osmium
 
-#endif // NODE_WRAP_HPP
+#endif // OSM_NODE_WRAP_HPP
diff --git a/src/osm_object_wrap.cpp b/src/osm_object_wrap.cpp
index f5801b4..710018a 100644
--- a/src/osm_object_wrap.cpp
+++ b/src/osm_object_wrap.cpp
@@ -3,61 +3,72 @@
 
 namespace node_osmium {
 
-    Handle<Value> OSMObjectWrap::tags(const Arguments& args) {
-        HandleScope scope;
-
-        osmium::Object& object = *(node::ObjectWrap::Unwrap<OSMObjectWrap>(args.This())->m_it);
-
-        if (args.Length() == 0) {
-            Local<Object> tags = Object::New();
-            for (auto& tag : object.tags()) {
-                tags->Set(String::New(tag.key()), String::New(tag.value()));
-            }
-            return scope.Close(tags);
-        } else if (args.Length() == 1) {
-            const char* value = object.tags().get_value_by_key(*String::Utf8Value(args[0]));
-            if (value) {
-                return scope.Close(String::New(value));
-            } else {
-                return Undefined();
-            }
+    v8::Persistent<v8::FunctionTemplate> OSMObjectWrap::constructor;
+
+    void OSMObjectWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMObjectWrap::New));
+        constructor->InstanceTemplate()->SetInternalFieldCount(1);
+        constructor->SetClassName(v8::String::NewSymbol("OSMObject"));
+        auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "id", get_id, attributes);
+        set_accessor(constructor, "version", get_version, attributes);
+        set_accessor(constructor, "changeset", get_changeset, attributes);
+        set_accessor(constructor, "visible", get_visible, attributes);
+        set_accessor(constructor, "timestamp_seconds_since_epoch", get_timestamp, attributes);
+        set_accessor(constructor, "uid", get_uid, attributes);
+        set_accessor(constructor, "user", get_user, attributes);
+        node::SetPrototypeMethod(constructor, "tags", tags);
+        target->Set(v8::String::NewSymbol("OSMObject"), constructor->GetFunction());
+    }
+
+    v8::Handle<v8::Value> OSMObjectWrap::New(const v8::Arguments& args) {
+        if (args.Length() == 1 && args[0]->IsExternal()) {
+            v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]);
+            static_cast<OSMObjectWrap*>(ext->Value())->Wrap(args.This());
+            return args.This();
+        } else {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.OSMObject cannot be created in Javascript")));
         }
-        return Undefined();
     }
 
-    Handle<Value> OSMObjectWrap::get_id(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(Number::New(wrapped(info.This()).id()));
+    v8::Handle<v8::Value> OSMObjectWrap::tags(const v8::Arguments& args) {
+        return OSMEntityWrap::tags_impl<osmium::OSMObject>(args);
+    }
+
+    v8::Handle<v8::Value> OSMObjectWrap::get_id(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).id()));
     }
 
-    Handle<Value> OSMObjectWrap::get_version(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(Number::New(wrapped(info.This()).version()));
+    v8::Handle<v8::Value> OSMObjectWrap::get_version(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).version()));
     }
 
-    Handle<Value> OSMObjectWrap::get_changeset(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(Number::New(wrapped(info.This()).changeset()));
+    v8::Handle<v8::Value> OSMObjectWrap::get_changeset(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).changeset()));
     }
 
-    Handle<Value> OSMObjectWrap::get_visible(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(Boolean::New(wrapped(info.This()).visible()));
+    v8::Handle<v8::Value> OSMObjectWrap::get_visible(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Boolean::New(wrapped(info.This()).visible()));
     }
 
-    Handle<Value> OSMObjectWrap::get_timestamp(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(Number::New(wrapped(info.This()).timestamp()));
+    v8::Handle<v8::Value> OSMObjectWrap::get_timestamp(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).timestamp()));
     }
 
-    Handle<Value> OSMObjectWrap::get_uid(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(Number::New(wrapped(info.This()).uid()));
+    v8::Handle<v8::Value> OSMObjectWrap::get_uid(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).uid()));
     }
 
-    Handle<Value> OSMObjectWrap::get_user(Local<String> property,const AccessorInfo& info) {
-        HandleScope scope;
-        return scope.Close(String::New(wrapped(info.This()).user()));
+    v8::Handle<v8::Value> OSMObjectWrap::get_user(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::String::New(wrapped(info.This()).user()));
     }
 
 } // namespace node_osmium
diff --git a/src/osm_object_wrap.hpp b/src/osm_object_wrap.hpp
index e5fd50a..a735d93 100644
--- a/src/osm_object_wrap.hpp
+++ b/src/osm_object_wrap.hpp
@@ -2,54 +2,47 @@
 #define OSM_OBJECT_WRAP_HPP
 
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
-
-// node
-#include <node.h>
-#include <node_version.h>
-#include <node_object_wrap.h>
-#include <node_buffer.h>
+#pragma GCC diagnostic pop
 
 // osmium
-#include <osmium/io/reader.hpp>
-#include <osmium/io/input_iterator.hpp>
 #include <osmium/osm/object.hpp>
+namespace osmium {
+    class OSMEntity;
+}
 
-using namespace v8;
-
-#define SET_ACCESSOR(t, name, getter,attributes)                                         \
-    t->InstanceTemplate()->SetAccessor(String::NewSymbol(name),getter,NULL,Handle<Value>(),v8::DEFAULT,attributes); \
- 
+// node-osmium
+#include "osm_entity_wrap.hpp"
+#include "utils.hpp"
 
 namespace node_osmium {
 
-    typedef osmium::io::InputIterator<osmium::io::Reader, osmium::Object> input_iterator;
+    class OSMObjectWrap : public OSMEntityWrap {
 
-    class OSMObjectWrap : public node::ObjectWrap {
-
-        input_iterator m_it;
+        static v8::Handle<v8::Value> get_id(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_version(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_changeset(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_visible(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_timestamp(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_uid(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> get_user(v8::Local<v8::String> property, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> tags(const v8::Arguments& args);
 
     public:
 
-        static Handle<Value> tags(const Arguments& args);
-        static Handle<Value> get_id(Local<String> property,const AccessorInfo& info);
-        static Handle<Value> get_version(Local<String> property,const AccessorInfo& info);
-        static Handle<Value> get_changeset(Local<String> property,const AccessorInfo& info);
-        static Handle<Value> get_visible(Local<String> property,const AccessorInfo& info);
-        static Handle<Value> get_timestamp(Local<String> property,const AccessorInfo& info);
-        static Handle<Value> get_uid(Local<String> property,const AccessorInfo& info);
-        static Handle<Value> get_user(Local<String> property,const AccessorInfo& info);
-
-        static osmium::Object& wrapped(Local<Object> object) {
-            return *(node::ObjectWrap::Unwrap<OSMObjectWrap>(object)->get());
-        }
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
 
-        OSMObjectWrap(const input_iterator& it) :
-            m_it(it) {
+        static const osmium::OSMObject& wrapped(const v8::Local<v8::Object>& object) {
+            return static_cast<const osmium::OSMObject&>(unwrap<OSMEntityWrap>(object));
         }
 
-        input_iterator& get() {
-            return m_it;
+        OSMObjectWrap(const osmium::OSMEntity& entity) :
+            OSMEntityWrap(entity) {
         }
 
     }; // class OSMObjectWrap
diff --git a/src/osm_relation_wrap.cpp b/src/osm_relation_wrap.cpp
index 21e7d60..fc8b225 100644
--- a/src/osm_relation_wrap.cpp
+++ b/src/osm_relation_wrap.cpp
@@ -1,87 +1,90 @@
 
+#include <node.h>
+
 #include <osm_relation_wrap.hpp>
 
 namespace node_osmium {
 
-    Persistent<FunctionTemplate> OSMRelationWrap::constructor;
+    v8::Persistent<v8::FunctionTemplate> OSMRelationWrap::constructor;
+    v8::Persistent<v8::String> OSMRelationWrap::symbol_type;
+    v8::Persistent<v8::String> OSMRelationWrap::symbol_ref;
+    v8::Persistent<v8::String> OSMRelationWrap::symbol_role;
 
-    void OSMRelationWrap::Initialize(Handle<Object> target) {
-        HandleScope scope;
-        constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(OSMRelationWrap::New));
+    void OSMRelationWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMRelationWrap::New));
+        constructor->Inherit(OSMObjectWrap::constructor);
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(String::NewSymbol("Relation"));
-        NODE_SET_PROTOTYPE_METHOD(constructor, "tags", tags);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "members", members);
-        enum PropertyAttribute attributes =
-            static_cast<PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
-        SET_ACCESSOR(constructor, "id", get_id, attributes);
-        SET_ACCESSOR(constructor, "version", get_version, attributes);
-        SET_ACCESSOR(constructor, "changeset", get_changeset, attributes);
-        SET_ACCESSOR(constructor, "visible", get_visible, attributes);
-        SET_ACCESSOR(constructor, "timestamp", get_timestamp, attributes);
-        SET_ACCESSOR(constructor, "uid", get_uid, attributes);
-        SET_ACCESSOR(constructor, "user", get_user, attributes);
-        target->Set(String::NewSymbol("Relation"), constructor->GetFunction());
-    }
+        constructor->SetClassName(v8::String::NewSymbol("Relation"));
+        auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "members_count", get_members_count, attributes);
+        node::SetPrototypeMethod(constructor, "members", members);
+        target->Set(v8::String::NewSymbol("Relation"), constructor->GetFunction());
 
-    OSMRelationWrap::OSMRelationWrap(const input_iterator& it) :
-        OSMObjectWrap(it) {
+        symbol_type = NODE_PSYMBOL("type");
+        symbol_ref  = NODE_PSYMBOL("ref");
+        symbol_role = NODE_PSYMBOL("role");
     }
 
-    OSMRelationWrap::~OSMRelationWrap() {
-    }
-
-    Handle<Value> OSMRelationWrap::New(const Arguments& args) {
-        HandleScope scope;
-        if (args[0]->IsExternal()) {
-            Local<External> ext = Local<External>::Cast(args[0]);
-            void* ptr = ext->Value();
-            OSMRelationWrap* relation = static_cast<OSMRelationWrap*>(ptr);
-            relation->Wrap(args.This());
+    v8::Handle<v8::Value> OSMRelationWrap::New(const v8::Arguments& args) {
+        if (args.Length() == 1 && args[0]->IsExternal()) {
+            v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]);
+            static_cast<OSMRelationWrap*>(ext->Value())->Wrap(args.This());
             return args.This();
         } else {
-            return ThrowException(Exception::TypeError(String::New("osmium.Relation cannot be created in Javascript")));
+            return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.Relation cannot be created in Javascript")));
         }
-        return Undefined();
     }
 
-    Handle<Value> OSMRelationWrap::members(const Arguments& args) {
-        HandleScope scope;
-        osmium::Relation& relation = wrapped(args.This());
+    v8::Handle<v8::Value> OSMRelationWrap::get_members_count(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).members().size()));
+    }
+
+    v8::Handle<v8::Value> OSMRelationWrap::members(const v8::Arguments& args) {
+        v8::HandleScope scope;
+
+        const osmium::Relation& relation = wrapped(args.This());
 
-        if (args.Length() == 0) {
-            Local<Array> members = Array::New();
-            int i = 0;
-            char typec[2] = " ";
-            for (auto& member : relation.members()) {
-                Local<Object> jsmember = Object::New();
-                typec[0] = osmium::item_type_to_char(member.type());
-                jsmember->Set(String::New("type"), String::New(typec));
-                jsmember->Set(String::New("ref"), Number::New(member.ref()));
-                jsmember->Set(String::New("role"), String::New(member.role()));
-                members->Set(i, jsmember);
-                ++i;
+        switch (args.Length()) {
+            case 0: {
+                v8::Local<v8::Array> members = v8::Array::New();
+                int i = 0;
+                char typec[2] = " ";
+                for (const auto& member : relation.members()) {
+                    v8::Local<v8::Object> jsmember = v8::Object::New();
+                    typec[0] = osmium::item_type_to_char(member.type());
+                    jsmember->Set(symbol_type, v8::String::New(typec));
+                    jsmember->Set(symbol_ref, v8::Number::New(member.ref()));
+                    jsmember->Set(symbol_role, v8::String::New(member.role()));
+                    members->Set(i, jsmember);
+                    ++i;
+                }
+                return scope.Close(members);
             }
-            return scope.Close(members);
-        } else if (args.Length() == 1) {
-            if (args[0]->IsNumber()) {
+            case 1: {
+                if (!args[0]->IsNumber()) {
+                    return ThrowException(v8::Exception::TypeError(v8::String::New("call members() without parameters or the index of the member you want")));
+                }
                 int n = static_cast<int>(args[0]->ToNumber()->Value());
-                if (n > 0 && n < static_cast<int>(relation.members().size())) {
+                if (n >= 0 && n < static_cast<int>(relation.members().size())) {
                     auto it = relation.members().begin();
                     std::advance(it, n);
-                    osmium::RelationMember& member = *it;
-                    Local<Object> jsmember = Object::New();
+                    const osmium::RelationMember& member = *it;
+                    v8::Local<v8::Object> jsmember = v8::Object::New();
                     char typec[2] = " ";
                     typec[0] = osmium::item_type_to_char(member.type());
-                    jsmember->Set(String::New("type"), String::New(typec));
-                    jsmember->Set(String::New("ref"), Number::New(member.ref()));
-                    jsmember->Set(String::New("role"), String::New(member.role()));
+                    jsmember->Set(symbol_type, v8::String::New(typec));
+                    jsmember->Set(symbol_ref, v8::Number::New(member.ref()));
+                    jsmember->Set(symbol_role, v8::String::New(member.role()));
                     return scope.Close(jsmember);
+                } else {
+                    return ThrowException(v8::Exception::RangeError(v8::String::New("argument to members() out of range")));
                 }
             }
         }
 
-        return Undefined();
+        return ThrowException(v8::Exception::TypeError(v8::String::New("call members() without parameters or the index of the member you want")));
     }
 
 } // namespace node_osmium
diff --git a/src/osm_relation_wrap.hpp b/src/osm_relation_wrap.hpp
index 60386b3..534fe48 100644
--- a/src/osm_relation_wrap.hpp
+++ b/src/osm_relation_wrap.hpp
@@ -1,53 +1,56 @@
-#ifndef RELATION_WRAP_HPP
-#define RELATION_WRAP_HPP
-
-// c++
-#include <iterator>
+#ifndef OSM_RELATION_WRAP_HPP
+#define OSM_RELATION_WRAP_HPP
 
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
-
-// node
-#include <node.h>
-#include <node_version.h>
-#include <node_object_wrap.h>
+#pragma GCC diagnostic pop
 
 // osmium
 #include <osmium/osm/relation.hpp>
-#include <osmium/io/input_iterator.hpp>
-#include <osmium/io/reader.hpp>
+namespace osmium {
+    class OSMEntity;
+}
 
+// node-osmium
+#include "osm_entity_wrap.hpp"
 #include "osm_object_wrap.hpp"
-
-using namespace v8;
+#include "utils.hpp"
 
 namespace node_osmium {
 
     class OSMRelationWrap : public OSMObjectWrap {
 
+        static v8::Persistent<v8::String> symbol_type;
+        static v8::Persistent<v8::String> symbol_ref;
+        static v8::Persistent<v8::String> symbol_role;
+
+        static v8::Handle<v8::Value> get_members_count(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> members(const v8::Arguments& args);
+
     public:
 
-        static Persistent<FunctionTemplate> constructor;
-        static void Initialize(Handle<Object> target);
-        static Handle<Value> New(const Arguments& args);
-        static Handle<Value> members(const Arguments& args);
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
 
-        static osmium::Relation& wrapped(Local<Object> object) {
-            return static_cast<osmium::Relation&>(OSMObjectWrap::wrapped(object));
+        static const osmium::Relation& wrapped(const v8::Local<v8::Object>& object) {
+            return static_cast<const osmium::Relation&>(unwrap<OSMEntityWrap>(object));
         }
 
-        OSMRelationWrap(const input_iterator&);
-
-        osmium::Relation& object() {
-            return static_cast<osmium::Relation&>(*get());
+        OSMRelationWrap(const osmium::OSMEntity& entity) :
+            OSMObjectWrap(entity) {
         }
 
     private:
 
-        ~OSMRelationWrap();
+        ~OSMRelationWrap() {
+        }
 
-    };
+    }; // class OSMRelationWrap
 
 } // namespace node_osmium
 
-#endif // RELATION_WRAP_HPP
+#endif // OSM_RELATION_WRAP_HPP
diff --git a/src/osm_way_wrap.cpp b/src/osm_way_wrap.cpp
index d3be9e7..86991e3 100644
--- a/src/osm_way_wrap.cpp
+++ b/src/osm_way_wrap.cpp
@@ -1,60 +1,50 @@
 
+// node
+#include <node_buffer.h>
+#include <node_version.h>
+
+// osmium
 #include <osmium/geom/wkb.hpp>
 #include <osmium/geom/wkt.hpp>
 
+// node-osmium
 #include "osm_way_wrap.hpp"
 
 namespace node_osmium {
 
-    extern osmium::geom::WKBFactory wkb_factory;
-    extern osmium::geom::WKTFactory wkt_factory;
+    extern osmium::geom::WKBFactory<> wkb_factory;
+    extern osmium::geom::WKTFactory<> wkt_factory;
+    extern v8::Persistent<v8::Object> module;
 
-    Persistent<FunctionTemplate> OSMWayWrap::constructor;
+    v8::Persistent<v8::FunctionTemplate> OSMWayWrap::constructor;
 
-    void OSMWayWrap::Initialize(Handle<Object> target) {
-        HandleScope scope;
-        constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(OSMWayWrap::New));
+    void OSMWayWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMWayWrap::New));
+        constructor->Inherit(OSMObjectWrap::constructor);
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(String::NewSymbol("Way"));
-        NODE_SET_PROTOTYPE_METHOD(constructor, "tags", tags);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "wkb", wkb);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "wkt", wkt);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "nodes", nodes);
-        enum PropertyAttribute attributes =
-            static_cast<PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
-        SET_ACCESSOR(constructor, "id", get_id, attributes);
-        SET_ACCESSOR(constructor, "version", get_version, attributes);
-        SET_ACCESSOR(constructor, "changeset", get_changeset, attributes);
-        SET_ACCESSOR(constructor, "visible", get_visible, attributes);
-        SET_ACCESSOR(constructor, "timestamp", get_timestamp, attributes);
-        SET_ACCESSOR(constructor, "uid", get_uid, attributes);
-        SET_ACCESSOR(constructor, "user", get_user, attributes);
-        target->Set(String::NewSymbol("Way"), constructor->GetFunction());
-    }
-
-    OSMWayWrap::OSMWayWrap(const input_iterator& it) :
-        OSMObjectWrap(it) {
+        constructor->SetClassName(v8::String::NewSymbol("Way"));
+        auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "nodes_count", get_nodes_count, attributes);
+        node::SetPrototypeMethod(constructor, "node_refs", node_refs);
+        node::SetPrototypeMethod(constructor, "node_coordinates", node_coordinates);
+        node::SetPrototypeMethod(constructor, "wkb", wkb);
+        node::SetPrototypeMethod(constructor, "wkt", wkt);
+        target->Set(v8::String::NewSymbol("Way"), constructor->GetFunction());
     }
 
-    OSMWayWrap::~OSMWayWrap() {
-    }
-
-    Handle<Value> OSMWayWrap::New(const Arguments& args) {
-        HandleScope scope;
-        if (args[0]->IsExternal()) {
-            Local<External> ext = Local<External>::Cast(args[0]);
-            void* ptr = ext->Value();
-            OSMWayWrap* way = static_cast<OSMWayWrap*>(ptr);
-            way->Wrap(args.This());
+    v8::Handle<v8::Value> OSMWayWrap::New(const v8::Arguments& args) {
+        if (args.Length() == 1 && args[0]->IsExternal()) {
+            v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]);
+            static_cast<OSMWayWrap*>(ext->Value())->Wrap(args.This());
             return args.This();
         } else {
-            return ThrowException(Exception::TypeError(String::New("osmium.Way cannot be created in Javascript")));
+            return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.Way cannot be created in Javascript")));
         }
-        return Undefined();
     }
 
-    Handle<Value> OSMWayWrap::wkb(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> OSMWayWrap::wkb(const v8::Arguments& args) {
+        v8::HandleScope scope;
 
         try {
             std::string wkb { wkb_factory.create_linestring(wrapped(args.This())) };
@@ -63,43 +53,102 @@ namespace node_osmium {
 #else
             return scope.Close(node::Buffer::New(const_cast<char*>(wkb.data()), wkb.size())->handle_);
 #endif
-        } catch (osmium::geom::geometry_error&) {
-            return scope.Close(Undefined());
+        } catch (std::runtime_error& e) {
+            return ThrowException(v8::Exception::Error(v8::String::New(e.what())));
         }
     }
 
-    Handle<Value> OSMWayWrap::wkt(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> OSMWayWrap::wkt(const v8::Arguments& args) {
+        v8::HandleScope scope;
 
         try {
             std::string wkt { wkt_factory.create_linestring(wrapped(args.This())) };
-            return scope.Close(String::New(wkt.c_str()));
-        } catch (osmium::geom::geometry_error&) {
-            return scope.Close(Undefined());
+            return scope.Close(v8::String::New(wkt.c_str()));
+        } catch (std::runtime_error& e) {
+            return ThrowException(v8::Exception::Error(v8::String::New(e.what())));
         }
     }
 
-    Handle<Value> OSMWayWrap::nodes(const Arguments& args) {
-        HandleScope scope;
-        osmium::Way& way = static_cast<osmium::Way&>(*(node::ObjectWrap::Unwrap<OSMWayWrap>(args.This())->get()));
+    v8::Handle<v8::Value> OSMWayWrap::get_nodes_count(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
+        v8::HandleScope scope;
+        return scope.Close(v8::Number::New(wrapped(info.This()).nodes().size()));
+    }
 
-        if (args.Length() == 0) {
-            Local<Array> nodes = Array::New(way.nodes().size());
-            int i = 0;
-            for (auto& wn : way.nodes()) {
-                nodes->Set(i, Number::New(wn.ref()));
-                ++i;
+    v8::Handle<v8::Value> OSMWayWrap::node_refs(const v8::Arguments& args) {
+        v8::HandleScope scope;
+
+        const osmium::Way& way = wrapped(args.This());
+
+        switch (args.Length()) {
+            case 0: {
+                v8::Local<v8::Array> nodes = v8::Array::New(way.nodes().size());
+                int i = 0;
+                for (const auto& node_ref : way.nodes()) {
+                    nodes->Set(i, v8::Number::New(node_ref.ref()));
+                    ++i;
+                }
+                return scope.Close(nodes);
             }
-            return scope.Close(nodes);
-        } else if (args.Length() == 1) {
-            if (args[0]->IsNumber()) {
+            case 1: {
+                if (!args[0]->IsNumber()) {
+                    return ThrowException(v8::Exception::TypeError(v8::String::New("call node_refs() without parameters or the index of the node you want")));
+                }
                 int n = static_cast<int>(args[0]->ToNumber()->Value());
-                if (n > 0 && n < static_cast<int>(way.nodes().size())) {
-                    return scope.Close(Number::New(way.nodes()[n].ref()));
+                if (n >= 0 && n < static_cast<int>(way.nodes().size())) {
+                    return scope.Close(v8::Number::New(way.nodes()[n].ref()));
+                } else {
+                    return ThrowException(v8::Exception::RangeError(v8::String::New("argument to node_refs() out of range")));
                 }
             }
         }
-        return Undefined();
+
+        return ThrowException(v8::Exception::TypeError(v8::String::New("call node_refs() without parameters or the index of the node you want")));
+    }
+
+    v8::Handle<v8::Value> OSMWayWrap::node_coordinates(const v8::Arguments& args) {
+        v8::HandleScope scope;
+
+        auto cf = module->Get(v8::String::NewSymbol("Coordinates"));
+        assert(cf->IsFunction());
+
+        const osmium::Way& way = wrapped(args.This());
+
+        switch (args.Length()) {
+            case 0: {
+                try {
+                    v8::Local<v8::Array> nodes = v8::Array::New(way.nodes().size());
+                    int i = 0;
+                    for (const auto& node_ref : way.nodes()) {
+                        const osmium::Location location = node_ref.location();
+                        v8::Local<v8::Value> argv[2] = { v8::Number::New(location.lon()), v8::Number::New(location.lat()) };
+                        nodes->Set(i, v8::Local<v8::Function>::Cast(cf)->NewInstance(2, argv));
+                        ++i;
+                    }
+                    return scope.Close(nodes);
+                } catch (osmium::invalid_location&) {
+                    return ThrowException(v8::Exception::TypeError(v8::String::New("location of at least one of the nodes in this way not set")));
+                }
+            }
+            case 1: {
+                if (!args[0]->IsNumber()) {
+                    return ThrowException(v8::Exception::TypeError(v8::String::New("call node_coordinates() without parameters or the index of the node you want")));
+                }
+                int n = static_cast<int>(args[0]->ToNumber()->Value());
+                if (n >= 0 && n < static_cast<int>(way.nodes().size())) {
+                    const osmium::Location location = way.nodes()[n].location();
+                    if (location.valid()) {
+                        v8::Local<v8::Value> argv[2] = { v8::Number::New(location.lon()), v8::Number::New(location.lat()) };
+                        return scope.Close(v8::Local<v8::Function>::Cast(cf)->NewInstance(2, argv));
+                    } else {
+                        return scope.Close(v8::Undefined());
+                    }
+                } else {
+                    return ThrowException(v8::Exception::RangeError(v8::String::New("argument to node_coordinates() out of range")));
+                }
+            }
+        }
+
+        return ThrowException(v8::Exception::TypeError(v8::String::New("call node_coordinates() without parameters or the index of the node you want")));
     }
 
 } // namespace node_osmium
diff --git a/src/osm_way_wrap.hpp b/src/osm_way_wrap.hpp
index d381acc..229c4bb 100644
--- a/src/osm_way_wrap.hpp
+++ b/src/osm_way_wrap.hpp
@@ -1,52 +1,55 @@
-#ifndef WAY_WRAP_HPP
-#define WAY_WRAP_HPP
+#ifndef OSM_WAY_WRAP_HPP
+#define OSM_WAY_WRAP_HPP
 
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
-
-// node
-#include <node.h>
-#include <node_version.h>
-#include <node_object_wrap.h>
+#pragma GCC diagnostic pop
 
 // osmium
 #include <osmium/osm/way.hpp>
-#include <osmium/io/input_iterator.hpp>
-#include <osmium/io/reader.hpp>
+namespace osmium {
+    class OSMEntity;
+}
 
+// node-osmium
+#include "osm_entity_wrap.hpp"
 #include "osm_object_wrap.hpp"
-
-using namespace v8;
+#include "utils.hpp"
 
 namespace node_osmium {
 
     class OSMWayWrap : public OSMObjectWrap {
 
+        static v8::Handle<v8::Value> get_nodes_count(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info);
+        static v8::Handle<v8::Value> node_refs(const v8::Arguments& args);
+        static v8::Handle<v8::Value> node_coordinates(const v8::Arguments& args);
+        static v8::Handle<v8::Value> wkb(const v8::Arguments& args);
+        static v8::Handle<v8::Value> wkt(const v8::Arguments& args);
+
     public:
 
-        static Persistent<FunctionTemplate> constructor;
-        static void Initialize(Handle<Object> target);
-        static Handle<Value> New(const Arguments& args);
-        static Handle<Value> wkb(const Arguments& args);
-        static Handle<Value> wkt(const Arguments& args);
-        static Handle<Value> nodes(const Arguments& args);
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
 
-        static osmium::Way& wrapped(Local<Object> object) {
-            return static_cast<osmium::Way&>(OSMObjectWrap::wrapped(object));
+        static const osmium::Way& wrapped(const v8::Local<v8::Object>& object) {
+            return static_cast<const osmium::Way&>(unwrap<OSMEntityWrap>(object));
         }
 
-        OSMWayWrap(const input_iterator&);
-
-        osmium::Way& object() {
-            return static_cast<osmium::Way&>(*get());
+        OSMWayWrap(const osmium::OSMEntity& entity) :
+            OSMObjectWrap(entity) {
         }
 
     private:
 
-        ~OSMWayWrap();
+        ~OSMWayWrap() {
+        }
 
-    };
+    }; // class OSMWayWrap
 
 } // namespace node_osmium
 
-#endif // WAY_WRAP_HPP
+#endif // OSM_WAY_WRAP_HPP
diff --git a/src/reader_wrap.cpp b/src/reader_wrap.cpp
index d9e4884..cdf507e 100644
--- a/src/reader_wrap.cpp
+++ b/src/reader_wrap.cpp
@@ -4,233 +4,136 @@
 #include <string>
 #include <vector>
 
-// boost
-#include <boost/variant.hpp>
-
-// node.js
+// node
 #include <node.h>
-#include <node_object_wrap.h>
-
-// osmium
-#include <osmium/visitor.hpp>
 
 // node-osmium
-#include "reader_wrap.hpp"
+#include "buffer_wrap.hpp"
 #include "file_wrap.hpp"
 #include "handler.hpp"
 #include "location_handler_wrap.hpp"
 #include "osm_object_wrap.hpp"
+#include "reader_wrap.hpp"
+#include "utils.hpp"
 
 namespace node_osmium {
 
-    Persistent<FunctionTemplate> ReaderWrap::constructor;
+    extern v8::Persistent<v8::Object> module;
+
+    v8::Persistent<v8::FunctionTemplate> ReaderWrap::constructor;
 
-    void ReaderWrap::Initialize(Handle<Object> target) {
-        HandleScope scope;
-        constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(ReaderWrap::New));
+    void ReaderWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(ReaderWrap::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(String::NewSymbol("Reader"));
-        NODE_SET_PROTOTYPE_METHOD(constructor, "header", header);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "apply", apply);
-        NODE_SET_PROTOTYPE_METHOD(constructor, "close", close);
-        target->Set(String::NewSymbol("Reader"), constructor->GetFunction());
+        constructor->SetClassName(v8::String::NewSymbol("Reader"));
+        node::SetPrototypeMethod(constructor, "header", header);
+        node::SetPrototypeMethod(constructor, "close", close);
+        node::SetPrototypeMethod(constructor, "read", read);
+        target->Set(v8::String::NewSymbol("Reader"), constructor->GetFunction());
     }
 
-    Handle<Value> ReaderWrap::New(const Arguments& args) {
-        HandleScope scope;
+    v8::Handle<v8::Value> ReaderWrap::New(const v8::Arguments& args) {
+        v8::HandleScope scope;
         if (!args.IsConstructCall()) {
-            return ThrowException(Exception::Error(String::New("Cannot call constructor as function, you need to use 'new' keyword")));
+            return ThrowException(v8::Exception::Error(v8::String::New("Cannot call constructor as function, you need to use 'new' keyword")));
         }
         if (args.Length() < 1 || args.Length() > 2) {
-            return ThrowException(Exception::TypeError(String::New("please provide a File object or string for the first argument and optional options Object when creating a Reader")));
+            return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a File object or string for the first argument and optional options v8::Object when creating a Reader")));
         }
         try {
-            osmium::osm_entity::flags read_which_entities = osmium::osm_entity::flags::all;
+            osmium::osm_entity_bits::type read_which_entities = osmium::osm_entity_bits::all;
             if (args.Length() == 2) {
                 if (!args[1]->IsObject()) {
-                    return ThrowException(Exception::TypeError(String::New("Second argument to Reader constructor must be object")));
+                    return ThrowException(v8::Exception::TypeError(v8::String::New("Second argument to Reader constructor must be object")));
                 }
-                read_which_entities = osmium::osm_entity::flags::nothing;
-                Local<Object> options = args[1]->ToObject();
+                read_which_entities = osmium::osm_entity_bits::nothing;
+                v8::Local<v8::Object> options = args[1]->ToObject();
 
-                Local<Value> want_nodes = options->Get(String::New("node"));
+                v8::Local<v8::Value> want_nodes = options->Get(v8::String::NewSymbol("node"));
                 if (want_nodes->IsBoolean() && want_nodes->BooleanValue()) {
-                    read_which_entities |= osmium::osm_entity::flags::node;
+                    read_which_entities |= osmium::osm_entity_bits::node;
                 }
 
-                Local<Value> want_ways = options->Get(String::New("way"));
+                v8::Local<v8::Value> want_ways = options->Get(v8::String::NewSymbol("way"));
                 if (want_ways->IsBoolean() && want_ways->BooleanValue()) {
-                    read_which_entities |= osmium::osm_entity::flags::way;
+                    read_which_entities |= osmium::osm_entity_bits::way;
                 }
 
-                Local<Value> want_relations = options->Get(String::New("relation"));
+                v8::Local<v8::Value> want_relations = options->Get(v8::String::NewSymbol("relation"));
                 if (want_relations->IsBoolean() && want_relations->BooleanValue()) {
-                    read_which_entities |= osmium::osm_entity::flags::relation;
+                    read_which_entities |= osmium::osm_entity_bits::relation;
+                }
+
+                v8::Local<v8::Value> want_changesets = options->Get(v8::String::NewSymbol("changeset"));
+                if (want_changesets->IsBoolean() && want_changesets->BooleanValue()) {
+                    read_which_entities |= osmium::osm_entity_bits::changeset;
                 }
 
             }
             if (args[0]->IsString()) {
-                osmium::io::File file(*String::Utf8Value(args[0]));
+                osmium::io::File file(*v8::String::Utf8Value(args[0]));
                 ReaderWrap* q = new ReaderWrap(file, read_which_entities);
                 q->Wrap(args.This());
                 return args.This();
             } else if (args[0]->IsObject() && FileWrap::constructor->HasInstance(args[0]->ToObject())) {
-                Local<Object> file_obj = args[0]->ToObject();
-                FileWrap* file_wrap = node::ObjectWrap::Unwrap<FileWrap>(file_obj);
-                ReaderWrap* q = new ReaderWrap(*(file_wrap->get()), read_which_entities);
+                v8::Local<v8::Object> file_obj = args[0]->ToObject();
+                ReaderWrap* q = new ReaderWrap(unwrap<FileWrap>(file_obj), read_which_entities);
                 q->Wrap(args.This());
                 return args.This();
             } else {
-                return ThrowException(Exception::TypeError(String::New("please provide a File object or string for the first argument when creating a Reader")));
+                return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a File object or string for the first argument when creating a Reader")));
             }
         } catch (const std::exception& ex) {
-            return ThrowException(Exception::TypeError(String::New(ex.what())));
+            return ThrowException(v8::Exception::TypeError(v8::String::New(ex.what())));
         }
-        return Undefined();
-    }
-
-    Handle<Value> ReaderWrap::header(const Arguments& args) {
-        HandleScope scope;
-        Local<Object> obj = Object::New();
-        ReaderWrap* reader = node::ObjectWrap::Unwrap<ReaderWrap>(args.This());
-        const osmium::io::Header& header = reader->m_this->header();
-        obj->Set(String::New("generator"), String::New(header.get("generator").c_str()));
-        const osmium::Box& bounds = header.box();
-        Local<Array> arr = Array::New(4);
-        arr->Set(0, Number::New(bounds.bottom_left().lon()));
-        arr->Set(1, Number::New(bounds.bottom_left().lat()));
-        arr->Set(2, Number::New(bounds.top_right().lon()));
-        arr->Set(3, Number::New(bounds.top_right().lat()));
-        obj->Set(String::New("bounds"), arr);
-        return scope.Close(obj);
+        return scope.Close(v8::Undefined());
     }
 
-    struct visitor_type : public boost::static_visitor<> {
-
-        input_iterator& m_it;
+    v8::Handle<v8::Value> ReaderWrap::header(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        v8::Local<v8::Object> obj = v8::Object::New();
+        const osmium::io::Header& header = unwrap<ReaderWrap>(args.This()).header();
+        obj->Set(v8::String::NewSymbol("generator"), v8::String::New(header.get("generator").c_str()));
 
-        visitor_type(input_iterator& it) :
-            m_it(it) {
-        }
+        auto bounds_array = v8::Array::New(header.boxes().size());
 
-        void operator()(JSHandler& handler) const {
-            handler.dispatch_object(m_it);
-        }
-
-        void operator()(location_handler_type& handler) const {
-            osmium::apply_item(*m_it, handler);
-        }
-
-    }; // visitor_type
-
-    struct visitor_before_after_type : public boost::static_visitor<> {
-
-        osmium::item_type m_last;
-        osmium::item_type m_current;
-
-        visitor_before_after_type(osmium::item_type last, osmium::item_type current) :
-            m_last(last),
-            m_current(current) {
-        }
-
-        template <class TVisitor>
-        void operator()(TVisitor& visitor) const {
-            switch (m_last) {
-                case osmium::item_type::undefined:
-                    visitor.init();
-                    break;
-                case osmium::item_type::node:
-                    visitor.after_nodes();
-                    break;
-                case osmium::item_type::way:
-                    visitor.after_ways();
-                    break;
-                case osmium::item_type::relation:
-                    visitor.after_relations();
-                    break;
-                case osmium::item_type::changeset:
-                    visitor.after_changesets();
-                    break;
-                default:
-                    break;
-            }
-            switch (m_current) {
-                case osmium::item_type::undefined:
-                    visitor.done();
-                    break;
-                case osmium::item_type::node:
-                    visitor.before_nodes();
-                    break;
-                case osmium::item_type::way:
-                    visitor.before_ways();
-                    break;
-                case osmium::item_type::relation:
-                    visitor.before_relations();
-                    break;
-                case osmium::item_type::changeset:
-                    visitor.before_changesets();
-                    break;
-                default:
-                    break;
-            }
+        int i=0;
+        for (const osmium::Box& box : header.boxes()) {
+            bounds_array->Set(i++, create_js_box(box));
         }
 
-    }; // visitor_before_after
-
-    Handle<Value> ReaderWrap::apply(const Arguments& args) {
-        HandleScope scope;
-
-        typedef boost::variant<location_handler_type&, JSHandler&> some_handler_type;
-        std::vector<some_handler_type> handlers;
+        obj->Set(v8::String::NewSymbol("bounds"), bounds_array);
+        return scope.Close(obj);
+    }
 
-        for (int i=0; i != args.Length(); ++i) {
-            if (args[i]->IsObject()) {
-                Local<Object> obj = args[i]->ToObject();
-                if (JSHandler::constructor->HasInstance(obj)) {
-                    handlers.push_back(*node::ObjectWrap::Unwrap<JSHandler>(obj));
-                } else if (LocationHandlerWrap::constructor->HasInstance(obj)) {
-                    location_handler_type* lh = node::ObjectWrap::Unwrap<LocationHandlerWrap>(obj)->get().get();
-                    handlers.push_back(*lh);
-                }
-            } else {
-                return ThrowException(Exception::TypeError(String::New("please provide a handler object")));
-            }
+    v8::Handle<v8::Value> ReaderWrap::close(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        try {
+            unwrap<ReaderWrap>(args.This()).close();
+        } catch (const std::exception& e) {
+            std::string msg("osmium error: ");
+            msg += e.what();
+            return ThrowException(v8::Exception::Error(v8::String::New(msg.c_str())));
         }
+        return scope.Close(v8::Undefined());
+    }
 
-        osmium::io::Reader& reader = wrapped(args.This());
-
-        input_iterator it(reader);
-        input_iterator end;
-
-        osmium::item_type last_type = osmium::item_type::undefined;
-
-        for (; it != end; ++it) {
-            visitor_before_after_type visitor_before_after(last_type, it->type());
-            visitor_type visitor(it);
-
-            for (some_handler_type& handler : handlers) {
-                if (last_type != it->type()) {
-                    boost::apply_visitor(visitor_before_after, handler);
-                }
-                boost::apply_visitor(visitor, handler);
-            }
-
-            if (last_type != it->type()) {
-                last_type = it->type();
+    v8::Handle<v8::Value> ReaderWrap::read(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        try {
+            osmium::memory::Buffer buffer = unwrap<ReaderWrap>(args.This()).read();
+            if (buffer) {
+                v8::Handle<v8::Value> ext = v8::External::New(new BufferWrap(std::move(buffer)));
+                v8::Local<v8::Object> obj = BufferWrap::constructor->GetFunction()->NewInstance(1, &ext);
+                return scope.Close(obj);
             }
+        } catch (const std::exception& e) {
+            std::string msg("osmium error: ");
+            msg += e.what();
+            return ThrowException(v8::Exception::Error(v8::String::New(msg.c_str())));
         }
-
-        visitor_before_after_type visitor_before_after(last_type, osmium::item_type::undefined);
-        for (auto handler : handlers) {
-            boost::apply_visitor(visitor_before_after, handler);
-        }
-
-        return Undefined();
-    }
-
-    Handle<Value> ReaderWrap::close(const Arguments& args) {
-        wrapped(args.This()).close();
-        return Undefined();
+        return scope.Close(v8::Undefined());
     }
 
 } // namespace node_osmium
diff --git a/src/reader_wrap.hpp b/src/reader_wrap.hpp
index 11a8b95..084c7ef 100644
--- a/src/reader_wrap.hpp
+++ b/src/reader_wrap.hpp
@@ -5,46 +5,47 @@
 #include <memory>
 
 // v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <v8.h>
+#pragma GCC diagnostic pop
 
-// node.js
+// node
+#include <node.h>
+#include <node_version.h>
 #include <node_object_wrap.h>
 
 // osmium
 #include <osmium/io/any_input.hpp>
 #include <osmium/io/reader.hpp>
-#include <osmium/osm/entity_flags.hpp>
+#include <osmium/osm/entity_bits.hpp>
 #include <osmium/osm/location.hpp>
 #include <osmium/osm/types.hpp>
 
-using namespace v8;
-
 namespace node_osmium {
 
-    typedef std::shared_ptr<osmium::io::Reader> reader_ptr;
-
     class ReaderWrap : public node::ObjectWrap {
 
-    public:
+        static v8::Handle<v8::Value> header(const v8::Arguments& args);
+        static v8::Handle<v8::Value> close(const v8::Arguments& args);
+        static v8::Handle<v8::Value> read(const v8::Arguments& args);
 
-        static Persistent<FunctionTemplate> constructor;
-        static void Initialize(Handle<Object> target);
-        static Handle<Value> New(const Arguments& args);
-        static Handle<Value> header(const Arguments& args);
-        static Handle<Value> apply(const Arguments& args);
-        static Handle<Value> close(const Arguments& args);
+        std::shared_ptr<osmium::io::Reader> m_this;
 
-        static osmium::io::Reader& wrapped(Local<Object> object) {
-            return *(node::ObjectWrap::Unwrap<ReaderWrap>(object)->get());
-        }
+    public:
+
+        static v8::Persistent<v8::FunctionTemplate> constructor;
+        static void Initialize(v8::Handle<v8::Object> target);
+        static v8::Handle<v8::Value> New(const v8::Arguments& args);
 
-        ReaderWrap(const osmium::io::File& file, osmium::osm_entity::flags entities) :
+        ReaderWrap(const osmium::io::File& file, osmium::osm_entity_bits::type entities) :
             ObjectWrap(),
             m_this(std::make_shared<osmium::io::Reader>(file, entities)) {
         }
 
-        reader_ptr get() {
-            return m_this;
+        osmium::io::Reader& get() {
+            return *m_this;
         }
 
     private:
@@ -52,8 +53,7 @@ namespace node_osmium {
         ~ReaderWrap() {
         }
 
-        reader_ptr m_this;
-    };
+    }; // class ReaderWrap
 
 } // namespace node_osmium
 
diff --git a/src/utils.cpp b/src/utils.cpp
new file mode 100644
index 0000000..df40fdb
--- /dev/null
+++ b/src/utils.cpp
@@ -0,0 +1,33 @@
+
+// osmium
+#include <osmium/osm/box.hpp>
+
+#include "utils.hpp"
+
+namespace node_osmium {
+
+    extern v8::Persistent<v8::Object> module;
+
+    v8::Handle<v8::Value> create_js_box(const osmium::Box& box) {
+        v8::HandleScope scope;
+
+        if (!box.valid()) {
+            return scope.Close(v8::Undefined());
+        }
+
+        auto cf = module->Get(v8::String::NewSymbol("Coordinates"));
+        assert(cf->IsFunction());
+        auto bf = module->Get(v8::String::NewSymbol("Box"));
+        assert(bf->IsFunction());
+
+        v8::Local<v8::Value> argv_bl[2] = { v8::Number::New(box.bottom_left().lon()), v8::Number::New(box.bottom_left().lat()) };
+        auto bottom_left = v8::Local<v8::Function>::Cast(cf)->NewInstance(2, argv_bl);
+
+        v8::Local<v8::Value> argv_tr[2] = { v8::Number::New(box.top_right().lon()), v8::Number::New(box.top_right().lat()) };
+        auto top_right = v8::Local<v8::Function>::Cast(cf)->NewInstance(2, argv_tr);
+
+        v8::Local<v8::Value> argv_box[2] = { bottom_left, top_right };
+        return scope.Close(v8::Local<v8::Function>::Cast(bf)->NewInstance(2, argv_box));
+    }
+
+} // namespace node_osmium
diff --git a/src/utils.hpp b/src/utils.hpp
new file mode 100644
index 0000000..331be88
--- /dev/null
+++ b/src/utils.hpp
@@ -0,0 +1,30 @@
+#ifndef UTILS_HPP
+#define UTILS_HPP
+
+// v8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <v8.h>
+#pragma GCC diagnostic pop
+
+// node
+#include <node_object_wrap.h>
+
+// osmium
+namespace osmium {
+    class Box;
+}
+
+namespace node_osmium {
+
+    template<class T>
+    auto unwrap(const v8::Local<v8::Object>& object) -> decltype(node::ObjectWrap::Unwrap<T>(object)->get()) {
+        return node::ObjectWrap::Unwrap<T>(object)->get();
+    }
+
+    v8::Handle<v8::Value> create_js_box(const osmium::Box& box);
+
+} // namespace node_osmium
+
+#endif // UTILS_HPP
diff --git a/test/apply.test.js b/test/apply.test.js
new file mode 100644
index 0000000..b6790ba
--- /dev/null
+++ b/test/apply.test.js
@@ -0,0 +1,34 @@
+var osmium = require('../');
+var fs = require('fs');
+var assert = require('assert');
+
+describe('apply', function() {
+
+    it('should be able to call apply with a Buffer and a handler', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.equal(node.id, 50031066);
+                assert.equal(node.visible, true);
+                assert.equal(node.version, 2);
+                assert.equal(node.changeset, 3137735);
+                assert.equal(node.user, "woodpeck_fixbot");
+                assert.equal(node.uid, 147510);
+                assert.equal(node.timestamp_seconds_since_epoch, 1258416656);
+                assert.equal(node.timestamp().toISOString(), '2009-11-17T00:10:56.000Z');
+                assert.equal(node.coordinates.lon, -120.1891610);
+                assert.equal(node.coordinates.lat, 48.4655800);
+                assert.equal(node.lon, -120.1891610);
+                assert.equal(node.lat, 48.4655800);
+                done();
+            }
+        });
+
+        var buffer = fs.readFileSync(__dirname + "/data/winthrop.osm.ser");
+
+        osmium.apply(buffer, handler);
+    });
+
+});
+
diff --git a/test/buffer.test.js b/test/buffer.test.js
new file mode 100644
index 0000000..06c84f3
--- /dev/null
+++ b/test/buffer.test.js
@@ -0,0 +1,97 @@
+var osmium = require('../');
+var fs = require('fs');
+var assert = require('assert');
+
+describe('buffer', function() {
+
+    it('should allow reading into buffer', function(done) {
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.equal(node.id, 50031066);
+                done();
+            }
+        });
+
+        var buffer = reader.read();
+        osmium.apply(buffer, handler);
+    });
+
+    it('should allow reading into buffer in a loop', function(done) {
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.equal(node.id, 50031066);
+                done();
+            }
+        });
+
+        var buffer;
+        while (buffer = reader.read()) {
+            osmium.apply(buffer, handler);
+        }
+    });
+
+    it('should allow iterating over buffer', function() {
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+
+        var buffer;
+        while (buffer = reader.read()) {
+            var object = buffer.next();
+            assert.ok(object instanceof osmium.Node);
+            assert.equal(object.id, 50031066);
+
+            object = buffer.next();
+            assert.ok(object instanceof osmium.Node);
+            assert.equal(object.id, 50031085);
+
+            var count=0, ways=0;
+            while (object = buffer.next()) {
+                if (ways == 0 && object instanceof osmium.Way) {
+                    ++ways;
+                    assert.equal(object.id, 6091729);
+                }
+                ++count;
+            }
+            assert.equal(count, 1525 /*nodes*/ + 98 /*ways*/ + 2 /*relations*/ - 2 /*already read*/);
+        }
+    });
+
+    it('should be able to call apply with a Buffer and a handler', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.equal(node.id, 50031066);
+                assert.equal(node.visible, true);
+                assert.equal(node.version, 2);
+                assert.equal(node.changeset, 3137735);
+                assert.equal(node.user, "woodpeck_fixbot");
+                assert.equal(node.uid, 147510);
+                assert.equal(node.timestamp_seconds_since_epoch, 1258416656);
+                assert.equal(node.timestamp().toISOString(), '2009-11-17T00:10:56.000Z');
+                assert.equal(node.coordinates.lon, -120.1891610);
+                assert.equal(node.coordinates.lat, 48.4655800);
+                assert.equal(node.lon, -120.1891610);
+                assert.equal(node.lat, 48.4655800);
+                done();
+            }
+        });
+
+        var data = fs.readFileSync(__dirname + "/data/winthrop.osm.ser");
+        var buffer = new osmium.Buffer(data);
+
+        osmium.apply(buffer, handler);
+    });
+
+});
+
diff --git a/test/changesets.test.js b/test/changesets.test.js
new file mode 100644
index 0000000..da4b366
--- /dev/null
+++ b/test/changesets.test.js
@@ -0,0 +1,70 @@
+var osmium = require('../');
+var assert = require('assert');
+
+describe('changesets', function() {
+
+   it('should be able to access basic attributes from closed changeset', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('changeset', function(changeset) {
+            if (count++ == 0) {
+                assert.equal(changeset.id, 15449957);
+                assert.equal(changeset.user, "Elbert");
+                assert.equal(changeset.uid, 1237205);
+                assert.equal(changeset.num_changes, 10);
+                assert.equal(changeset.created_at_seconds_since_epoch, 1363918135);
+                assert.equal(changeset.created_at().toISOString(), '2013-03-22T02:08:55.000Z');
+                assert.equal(changeset.closed_at_seconds_since_epoch, 1363918138);
+                assert.equal(changeset.closed_at().toISOString(), '2013-03-22T02:08:58.000Z');
+                assert.equal(changeset.bounds.left(), 120.2988730);
+                assert.equal(changeset.bounds.bottom(), -10.0004425);
+                assert.equal(changeset.bounds.right(), 120.2991740);
+                assert.equal(changeset.bounds.top(), -10.0002384);
+                assert.ok(changeset.closed);
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/changesets.osm");
+        var reader = new osmium.Reader(file, {changeset: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able to access basic attributes from open changeset', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('changeset', function(changeset) {
+            if (count++ == 2) {
+                assert.equal(changeset.id, 15450185);
+                assert.equal(changeset.user, "garl");
+                assert.equal(changeset.uid, 51196);
+                assert.equal(changeset.num_changes, 0);
+                assert.equal(changeset.created_at_seconds_since_epoch, 1363926025);
+                assert.equal(changeset.created_at().toISOString(), '2013-03-22T04:20:25.000Z');
+                assert.equal(changeset.closed_at_seconds_since_epoch, 0);
+                assert.equal(changeset.closed_at(), undefined);
+                assert.equal(changeset.bounds, undefined);
+                assert.ok(changeset.open);
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/changesets.osm");
+        var reader = new osmium.Reader(file, {changeset: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able to access tags from changeset', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('changeset', function(changeset) {
+            if (count++ == 0) {
+                assert.equal(changeset.tags().created_by, 'JOSM/1.5 (5356 en)');
+                assert.equal(changeset.tags('created_by'), 'JOSM/1.5 (5356 en)');
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/changesets.osm");
+        var reader = new osmium.Reader(file, {changeset: true});
+        osmium.apply(reader, handler);
+    });
+
+});
diff --git a/test/coordinates.test.js b/test/coordinates.test.js
new file mode 100644
index 0000000..8e055a3
--- /dev/null
+++ b/test/coordinates.test.js
@@ -0,0 +1,44 @@
+var osmium = require('../');
+var assert = require('assert');
+
+describe('coordinates', function() {
+
+    it('should be able to instantiate coordinates', function() {
+        var c = new osmium.Coordinates(5.3, 2.1);
+        assert.equal(c.lon, 5.3);
+        assert.equal(c.lat, 2.1);
+        assert.ok(c.valid());
+    });
+
+    it('should use undefined defaults', function() {
+        var c = new osmium.Coordinates();
+        assert.equal(c.lon, undefined);
+        assert.equal(c.lat, undefined);
+        assert.ok(!c.valid());
+    });
+
+    it('should be invalid for out of bounds', function() {
+        var c = new osmium.Coordinates(200, 10);
+        assert.equal(c.lon, 200);
+        assert.equal(c.lat, 10);
+        assert.ok(!c.valid());
+    });
+
+    it('box can be default instantiated', function() {
+        var box = new osmium.Box();
+
+        assert.ok(box);
+        assert.equal(box.left(), undefined);
+    });
+
+    it('box can be instantiated with coordinates', function() {
+        var c1 = new osmium.Coordinates(1.2, 3.4);
+        var c2 = new osmium.Coordinates(5.6, 7.8);
+        var box = new osmium.Box(c1, c2);
+
+        assert.ok(box);
+        assert.equal(box.left(), 1.2);
+    });
+
+});
+
diff --git a/test/data/changesets.osm b/test/data/changesets.osm
new file mode 100644
index 0000000..ca10c2e
--- /dev/null
+++ b/test/data/changesets.osm
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis 0.41">
+  <changeset id="15449957" created_at="2013-03-22T02:08:55Z" num_changes="10" closed_at="2013-03-22T02:08:58Z" open="false" min_lon="120.2988730" min_lat="-10.0004425" max_lon="120.2991740" max_lat="-10.0002384" user="Elbert" uid="1237205">
+    <tag k="created_by" v="JOSM/1.5 (5356 en)"/>
+  </changeset>
+  <changeset id="15449958" created_at="2013-03-22T02:09:11Z" num_changes="0" closed_at="2013-03-22T03:09:11Z" open="false" user="sree dinesh" uid="1233268">
+    <tag k="comment" v="new residen road"/>
+    <tag k="created_by" v="JOSM/1.5 (5697 en)"/>
+  </changeset>
+  <changeset id="15450185" created_at="2013-03-22T04:20:25Z" num_changes="0" open="true" user="garl" uid="51196">
+    <tag k="comment" v="BBOX:40.53,43.93,40.77,44.11 ADD:18 UPD:33 DEL:0"/>
+    <tag k="created_by" v="Merkaartor 0.18.1 (ru)"/>
+  </changeset>
+</osm>
diff --git a/test/data/missing-node.osm b/test/data/missing-node.osm
new file mode 100644
index 0000000..4e327fd
--- /dev/null
+++ b/test/data/missing-node.osm
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+    <node id="1" version="1" timestamp="2014-01-01T00:00:00Z" uid="1" user="test" changeset="1" lon="1.02" lat="2.03"/>
+    <way id="10" visible="true" version="1" changeset="1" timestamp="2014-01-01T00:00:01Z" user="test" uid="1">
+        <nd ref="1"/>
+        <nd ref="2"/>
+    </way>
+</osm>
diff --git a/test/data/winthrop.osm.ser b/test/data/winthrop.osm.ser
new file mode 100644
index 0000000..9592f70
Binary files /dev/null and b/test/data/winthrop.osm.ser differ
diff --git a/test/file.test.js b/test/file.test.js
new file mode 100644
index 0000000..7b6feea
--- /dev/null
+++ b/test/file.test.js
@@ -0,0 +1,38 @@
+var osmium = require('../');
+var assert = require('assert');
+
+describe('file', function() {
+
+    it('should throw when File called as function', function() {
+        assert.throws(function() {
+            var file = osmium.File();
+        }, Error);
+    });
+
+    it('should create File object without parameters', function() {
+        assert.throws(function() {
+            var file = new osmium.File();
+        }, Error);
+    });
+
+    it('should create File object with one parameters', function() {
+        var file = new osmium.File("file.osm");
+    });
+
+    it('should create File object with two parameters', function() {
+        var file = new osmium.File("file.osm", "osm");
+    });
+
+    it('should create File object for stdin/stdout', function() {
+        var file = new osmium.File("", "osm");
+        file = new osmium.File("-", "osm");
+    });
+
+    it('should not create File object with three parameters', function() {
+        assert.throws(function() {
+            var file = new osmium.File("file.osm", "osm", "foo");
+        }, TypeError);
+    });
+
+});
+
diff --git a/test/geojson.test.js b/test/geojson.test.js
new file mode 100644
index 0000000..c6b68de
--- /dev/null
+++ b/test/geojson.test.js
@@ -0,0 +1,43 @@
+var osmium = require('../');
+var assert = require('assert');
+
+describe('geojson', function() {
+
+   it('should be able to create geojson from a node', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.deepEqual(node.geojson(), {
+                    type: 'Point',
+                    coordinates: [-120.1891610, 48.4655800]
+                });
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {node: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able to create geojson from a way', function(done) {
+        var handler = new osmium.Handler();
+        handler.on('way', function(way) {
+            if (way.id == 6089456) {
+                assert.deepEqual(way.geojson(), {
+                    type: 'LineString',
+                    coordinates: [
+                        [-120.1796227, 48.4798110],
+                        [-120.1787663, 48.4802976]
+                    ]
+                });
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+        osmium.apply(reader, new osmium.LocationHandler("stlmap"), handler);
+    });
+
+});
+
diff --git a/test/handler.test.js b/test/handler.test.js
new file mode 100644
index 0000000..f2c493d
--- /dev/null
+++ b/test/handler.test.js
@@ -0,0 +1,256 @@
+var osmium = require('../');
+var assert = require('assert');
+
+var get_size = function(obj) {
+    var size = 0, key;
+    for (key in obj) {
+        if (obj.hasOwnProperty(key)) size++;
+    }
+    return size;
+};
+
+describe('handler', function() {
+
+    it('should throw when Handler called as function', function() {
+        assert.throws(function() {
+            var handler = osmium.Handler();
+        }, Error);
+    });
+
+    it('should be able to initialize handler', function() {
+        var handler = new osmium.Handler();
+        handler.on('node', function(node) {
+        });
+    });
+
+    it('should throw when handler initializion is wrong', function() {
+        var handler = new osmium.Handler();
+        assert.throws(function() {
+            handler.on();
+        }, TypeError);
+        assert.throws(function() {
+            handler.on("node");
+        }, TypeError);
+        assert.throws(function() {
+            handler.on("node", "foo");
+        }, TypeError);
+        assert.throws(function() {
+            handler.on("foo", function(node) {
+            });
+        }, RangeError);
+    });
+
+    it('should call all object callbacks of handler', function() {
+        var handler = new osmium.Handler();
+
+        var count_nodes = 0;
+        var count_ways = 0;
+        var count_relations = 0;
+        var count_changesets = 0;
+
+        handler.on('node',      function(obj) { count_nodes++;      });
+        handler.on('way',       function(obj) { count_ways++;       });
+        handler.on('relation',  function(obj) { count_relations++;  });
+        handler.on('changeset', function(obj) { count_changesets++; });
+
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+        osmium.apply(reader, handler);
+
+        assert.equal(count_nodes, 1525);
+        assert.equal(count_ways, 98);
+        assert.equal(count_relations, 2);
+        assert.equal(count_changesets, 0);
+
+        file = new osmium.File(__dirname + "/data/changesets.osm");
+        reader = new osmium.Reader(file);
+        osmium.apply(reader, handler);
+
+        assert.equal(count_nodes, 1525);
+        assert.equal(count_ways, 98);
+        assert.equal(count_relations, 2);
+        assert.equal(count_changesets, 3);
+    });
+
+    it('should call all before and after callbacks of handler', function() {
+        var handler = new osmium.Handler();
+
+        var count = 0;
+
+        var callbacks = ['init', 'before_nodes', 'after_nodes', 'before_ways', 'after_ways', 'before_relations', 'after_relations', 'done'];
+
+        callbacks.forEach(function (cb) {
+            handler.on(cb, function() { count++; });
+        });
+
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+        osmium.apply(reader, handler);
+
+        assert.equal(count, 8);
+    });
+
+    it('should call before_changesets and after_changesets callbacks of handler', function() {
+        var handler = new osmium.Handler();
+
+        var count = 0;
+
+        var callbacks = ['init', 'before_changesets', 'after_changesets', 'done'];
+
+        callbacks.forEach(function (cb) {
+            handler.on(cb, function() { count++; });
+        });
+
+        var file = new osmium.File(__dirname + "/data/changesets.osm");
+        var reader = new osmium.Reader(file);
+        osmium.apply(reader, handler);
+
+        assert.equal(count, 4);
+    });
+
+    it('should call node callback only for tagged nodes if tagged_nodes_only is set', function() {
+        var handler = new osmium.Handler();
+
+        var count = 0;
+        handler.on('node', function(node) {
+            if (get_size(node.tags()) != 0) {
+                count++;
+            }
+        });
+
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+        osmium.apply(reader, handler);
+
+        assert.equal(count, 15);
+
+        handler.options({ 'tagged_nodes_only': true });
+        handler.on('node', function(node) { count--; });
+
+        file = new osmium.File(__dirname + "/data/winthrop.osm");
+        reader = new osmium.Reader(file);
+        osmium.apply(reader, handler);
+
+        assert.equal(count, 0);
+    });
+
+    it('should if options() is called with wrong arguments', function() {
+        var handler = new osmium.Handler();
+
+        assert.throws(function() {
+            handler.options();
+        }, TypeError);
+
+        assert.throws(function() {
+            handler.options("foo");
+        }, TypeError);
+
+        assert.throws(function() {
+            handler.options({}, "b");
+        }, TypeError);
+    });
+
+    it('should allow a callback to be redefined', function() {
+        var handler = new osmium.Handler();
+
+        var count = 0;
+        handler.on('node', function(node) {
+            count++;
+        });
+
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+        osmium.apply(reader, handler);
+
+        assert.equal(count, 1525);
+
+        count = 0;
+        handler.on('node', function(node) { });
+
+        file = new osmium.File(__dirname + "/data/winthrop.osm");
+        reader = new osmium.Reader(file);
+        osmium.apply(reader, handler);
+
+        assert.equal(count, 0);
+    });
+
+    it('should be able to get node data from handler parameter', function() {
+        var handler = new osmium.Handler();
+        var nodes = 0, ways = 0, relations = 0;
+        handler.on('node', function(node) {
+            if (nodes == 0) {
+                assert.equal(node.id, 50031066);
+                assert.equal(node.lon, -120.1891610);
+            }
+            if (nodes == 1) {
+                assert.equal(node.id, 50031085);
+                assert.equal(node.lon, -120.1929190);
+            }
+            if (node.id == 1564464078) {
+                assert.equal(node.changeset, 10220832);
+                assert.equal(node.tags().amenity, 'pub');
+                assert.equal(node.tags('name'), 'Old Schoolhouse Brewery');
+            }
+            ++nodes;
+        });
+        handler.on('way', function(way) {
+            if (ways == 0) {
+                assert.equal(way.id, 6091729);
+                assert.equal(way.node_refs(1), 50253602);
+                assert.deepEqual(way.node_refs(), [50253600, 50253602, 50137292, 50137371, 50253605, 50253608]);
+            }
+            ++ways;
+        });
+        handler.on('relation', function(relation) {
+            if (relations == 0) {
+                assert.equal(relation.id, 237891);
+                assert.deepEqual(relation.members()[0], ['w', 40512249, 'outer']);
+                assert.deepEqual(relation.members(3), ['w', 40512257, 'inner']);
+            }
+            ++relations;
+        });
+        handler.on('done', function() {
+            assert.equal(nodes, 1525);
+        });
+
+        var reader = new osmium.Reader(__dirname + "/data/winthrop.osm", { 'node': true, 'way': true });
+        osmium.apply(reader, handler);
+    });
+
+    it('should be able to call two handlers one after the other', function() {
+        var handler1 = new osmium.Handler();
+        var handler2 = new osmium.Handler();
+
+        var count=0;
+        handler1.on('init', function() {
+            assert.equal(count, 0);
+            count++;
+        });
+        handler2.on('init', function() {
+            assert.equal(count, 1);
+            count++;
+        });
+
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+        osmium.apply(reader, handler1, handler2);
+
+        assert.equal(count, 2);
+    });
+
+    it('should catch errors in handler callbacks and rethrow from apply function', function() {
+        var handler = new osmium.Handler();
+        handler.on('node', function(node) {
+            throw new Error("test error");
+        });
+
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+
+        assert.throws(function() {
+            osmium.apply(reader, handler);
+        }, Error);
+    });
+
+});
+
diff --git a/test/location_handler.test.js b/test/location_handler.test.js
new file mode 100644
index 0000000..59e781b
--- /dev/null
+++ b/test/location_handler.test.js
@@ -0,0 +1,83 @@
+var osmium = require('../');
+var assert = require('assert');
+
+function get_handler() {
+    var handler = new osmium.Handler();
+
+    var ways = 0;
+    handler.on('way', function(way) {
+        if (ways == 0) {
+            assert.equal(way.wkt(), "LINESTRING(-120.1872774 48.4715898,-120.188291 48.472511,-120.188374 48.472591,-120.188496 48.472707,-120.188625 48.47283,-120.18914 48.473561)");
+        }
+        ++ways;
+    });
+
+    return handler;
+}
+
+describe('location handler', function() {
+
+    it('should be able to use the sparsetable location handler', function() {
+        var reader = new osmium.Reader(__dirname + "/data/winthrop.osm", { 'node': true, 'way': true });
+        osmium.apply(reader, new osmium.LocationHandler("sparsetable"), get_handler());
+    });
+
+    it('should be able to use the stlmap location handler', function() {
+        var reader = new osmium.Reader(__dirname + "/data/winthrop.osm", { 'node': true, 'way': true });
+        osmium.apply(reader, new osmium.LocationHandler("stlmap"), get_handler());
+    });
+
+    it('should throw on missing location if ignoreErrors is not set', function() {
+        var reader = new osmium.Reader(__dirname + "/data/missing-node.osm", { 'node': true, 'way': true });
+        var location_handler = new osmium.LocationHandler();
+        var handler = new osmium.Handler();
+
+        assert.throws(function() {
+            osmium.apply(reader, location_handler, handler);
+        }, Error);
+    });
+
+    it('should throw in wkb/wkt/node_coordinates function if ignoreErrors is set', function(done) {
+        var reader = new osmium.Reader(__dirname + "/data/missing-node.osm", { 'node': true, 'way': true });
+        var location_handler = new osmium.LocationHandler();
+        location_handler.ignoreErrors();
+
+        var handler = new osmium.Handler();
+
+        handler.on('way', function(way) {
+            assert.equal(way.id, 10);
+            assert.throws(function() {
+                way.wkb();
+            }, Error);
+            assert.throws(function() {
+                way.wkt();
+            }, Error);
+            assert.throws(function() {
+                way.node_coordinates();
+            }, Error);
+            done();
+        });
+
+        osmium.apply(reader, location_handler, handler);
+    });
+
+    it('should return undefined for node_coordinates(n) function if ignoreErrors is set', function(done) {
+        var reader = new osmium.Reader(__dirname + "/data/missing-node.osm", { 'node': true, 'way': true });
+        var location_handler = new osmium.LocationHandler();
+        location_handler.ignoreErrors();
+
+        var handler = new osmium.Handler();
+
+        handler.on('way', function(way) {
+            assert.equal(way.id, 10);
+            assert.deepEqual(way.node_refs(), [1, 2]);
+            assert.deepEqual(way.node_coordinates(0).lon, 1.02);
+            assert.deepEqual(way.node_coordinates(0).lat, 2.03);
+            assert.deepEqual(way.node_coordinates(1), undefined);
+            done();
+        });
+
+        osmium.apply(reader, location_handler, handler);
+    });
+
+});
diff --git a/test/osm-objects.test.js b/test/osm-objects.test.js
new file mode 100644
index 0000000..7b10669
--- /dev/null
+++ b/test/osm-objects.test.js
@@ -0,0 +1,177 @@
+var osmium = require('../');
+var assert = require('assert');
+
+describe('basic', function() {
+
+   it('should be able to access basic attributes from node', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.equal(node.id, 50031066);
+                assert.equal(node.visible, true);
+                assert.equal(node.version, 2);
+                assert.equal(node.changeset, 3137735);
+                assert.equal(node.user, "woodpeck_fixbot");
+                assert.equal(node.uid, 147510);
+                assert.equal(node.timestamp_seconds_since_epoch, 1258416656);
+                assert.equal(node.timestamp().toISOString(), '2009-11-17T00:10:56.000Z');
+                assert.equal(node.coordinates.lon, -120.1891610);
+                assert.equal(node.coordinates.lat, 48.4655800);
+                assert.equal(node.lon, -120.1891610);
+                assert.equal(node.lat, 48.4655800);
+                assert.equal(node.coordinates.lon, -120.1891610);
+                assert.equal(node.coordinates.lat, 48.4655800);
+                assert.equal(node.wkt(), "POINT(-120.189161 48.46558)");
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {node: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able to access basic attributes from way', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('way', function(way) {
+            if (count++ == 0) {
+                assert.equal(way.id, 6091729);
+                assert.equal(way.visible, true);
+                assert.equal(way.version, 1);
+                assert.equal(way.changeset, 417421);
+                assert.equal(way.user, "DaveHansenTiger");
+                assert.equal(way.uid, 7168);
+                assert.equal(way.timestamp_seconds_since_epoch, 1189655636);
+                assert.equal(way.timestamp().toISOString(), '2007-09-13T03:53:56.000Z');
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {way: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able to access basic attributes from relation', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('relation', function(relation) {
+            if (count++ == 0) {
+                assert.equal(relation.id, 237891);
+                assert.equal(relation.visible, true);
+                assert.equal(relation.version, 2);
+                assert.equal(relation.changeset, 15155909);
+                assert.equal(relation.user, "Jano John Akim Franke");
+                assert.equal(relation.uid, 42191);
+                assert.equal(relation.timestamp_seconds_since_epoch, 1361751094);
+                assert.equal(relation.timestamp().toISOString(), '2013-02-25T00:11:34.000Z');
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {relation: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able to handle object without tags', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.deepEqual(node.tags(), {});
+                assert.equal(node.tags("foobar"), undefined);
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {node: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able access tags on object', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('way', function(way) {
+            if (count++ == 0) {
+                assert.equal(way.tags().name, "National Fish Hatchery Entranc");
+                assert.equal(way.tags().foobar, undefined);
+                assert.equal(way.tags("highway"), "residential");
+                assert.equal(way.tags("foobar"), undefined);
+                assert.throws(function() {
+                    way.tags({});
+                }, TypeError);
+                assert.throws(function() {
+                    way.tags("foo", "bar");
+                }, TypeError);
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {way: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able access nodes on ways', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('way', function(way) {
+            if (count++ == 0) {
+                assert.equal(way.nodes_count, 6);
+                assert.equal(way.node_refs().length, 6);
+                assert.equal(way.node_refs()[0], 50253600);
+                assert.equal(way.node_refs()[5], 50253608);
+                assert.equal(way.node_refs(0), 50253600);
+                assert.equal(way.node_refs(5), 50253608);
+                assert.throws(function() {
+                    way.node_refs(6);
+                }, RangeError);
+                assert.throws(function() {
+                    way.node_refs("foo");
+                }, TypeError);
+                assert.throws(function() {
+                    way.node_refs(1, "bar");
+                }, TypeError);
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {way: true});
+        osmium.apply(reader, handler);
+    });
+
+   it('should be able access members on relations', function(done) {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('relation', function(relation) {
+            if (count++ == 0) {
+                assert.equal(relation.members_count, 5);
+                assert.equal(relation.members().length, 5);
+                assert.deepEqual(relation.members()[0], {
+                    type: 'w',
+                    ref: 40512249,
+                    role: 'outer'
+                });
+                assert.deepEqual(relation.members()[4], {
+                    type: 'w',
+                    ref: 40512263,
+                    role: 'inner'
+                });
+                assert.deepEqual(relation.members(1), relation.members()[1]);
+                assert.throws(function() {
+                    relation.members(5);
+                }, RangeError);
+                assert.throws(function() {
+                    relation.members("foo");
+                }, TypeError);
+                assert.throws(function() {
+                    relation.members(1, "bar");
+                }, TypeError);
+                done();
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {relation: true});
+        osmium.apply(reader, handler);
+    });
+
+});
diff --git a/test/osm_object_creation.test.js b/test/osm_object_creation.test.js
new file mode 100644
index 0000000..bed021b
--- /dev/null
+++ b/test/osm_object_creation.test.js
@@ -0,0 +1,59 @@
+var osmium = require('../');
+var assert = require('assert');
+
+describe('osm object creation', function() {
+
+    it('should not be able to create an osmium.OSMObject from Javascript', function() {
+        var got_exception = false;
+        try {
+            var object = new osmium.OSMObject;
+        } catch (e) {
+            assert.ok(e instanceof TypeError);
+            assert.equal(e.message, 'osmium.OSMObject cannot be created in Javascript');
+            got_exception = true;
+        } finally {
+            assert.ok(got_exception);
+        }
+    });
+
+    it('should not be able to create an osmium.Node from Javascript', function() {
+        var got_exception = false;
+        try {
+            var node = new osmium.Node;
+        } catch (e) {
+            assert.ok(e instanceof TypeError);
+            assert.equal(e.message, 'osmium.Node cannot be created in Javascript');
+            got_exception = true;
+        } finally {
+            assert.ok(got_exception);
+        }
+    });
+
+    it('should not be able to create an osmium.Way from Javascript', function() {
+        var got_exception = false;
+        try {
+            var way = new osmium.Way;
+        } catch (e) {
+            assert.ok(e instanceof TypeError);
+            assert.equal(e.message, 'osmium.Way cannot be created in Javascript');
+            got_exception = true;
+        } finally {
+            assert.ok(got_exception);
+        }
+    });
+
+    it('should not be able to create an osmium.Relation from Javascript', function() {
+        var got_exception = false;
+        try {
+            var relation = new osmium.Relation;
+        } catch (e) {
+            assert.ok(e instanceof TypeError);
+            assert.equal(e.message, 'osmium.Relation cannot be created in Javascript');
+            got_exception = true;
+        } finally {
+            assert.ok(got_exception);
+        }
+    });
+
+});
+
diff --git a/test/osmium.test.js b/test/osmium.test.js
deleted file mode 100644
index c5ea5c6..0000000
--- a/test/osmium.test.js
+++ /dev/null
@@ -1,150 +0,0 @@
-var osmium = require('../');
-var assert = require('assert');
-
-describe('osmium', function() {
-
-    it('should be able to create an osmium.Reader', function(done) {
-        var file = new osmium.File(__dirname+"/data/winthrop.osm");
-        var reader = new osmium.Reader(file, {});
-        var header = reader.header();
-        assert.equal(header.generator, 'CGImap 0.2.0');
-        var bounds = header.bounds;
-        var expected = [ -120.2024, 48.4636, -120.1569, 48.4869 ];
-        assert.ok(Math.abs(bounds[0] - expected[0]) < .000000001);
-        assert.ok(Math.abs(bounds[1] - expected[1]) < .000000001);
-        assert.ok(Math.abs(bounds[2] - expected[2]) < .000000001);
-        assert.ok(Math.abs(bounds[3] - expected[3]) < .000000001);
-        reader.close();
-        done();
-    });
-
-   it('should be able to read ISO time from node', function(done) {
-        var handler = new osmium.Handler();
-        var count = 0;
-        handler.on('node',function(node) {
-            if (count == 0) {
-                assert.equal(node.date().toISOString(),'2009-11-17T00:10:56.000Z');
-                done();
-            }
-            count++;
-        });
-        var file = new osmium.File(__dirname+"/data/winthrop.osm");
-        var reader = new osmium.Reader(file, {node:true});
-        reader.apply(handler);
-    });
-
-    it('should be able to get node data from handler parameter', function(done) {
-        var handler = new osmium.Handler();
-        var nodes = 0, ways = 0, relations = 0;
-        handler.on('node', function(node) {
-            if (nodes == 0) {
-                assert.equal(node.id, 50031066);
-                assert.equal(node.lon, -120.1891610);
-            }
-            if (nodes == 1) {
-                assert.equal(node.id, 50031085);
-                assert.equal(node.lon, -120.1929190);
-            }
-            if (node.id == 1564464078) {
-                assert.equal(node.changeset, 10220832);
-                assert.equal(node.tags().amenity, 'pub');
-                assert.equal(node.tags('name'), 'Old Schoolhouse Brewery');
-            }
-            ++nodes;
-        });
-        handler.on('way', function(way) {
-            if (ways == 0) {
-                assert.equal(way.id, 6091729);
-                assert.equal(way.nodes(1), 50253602);
-                assert.deepEqual(way.nodes(), [50253600, 50253602, 50137292, 50137371, 50253605, 50253608]);
-            }
-            ++ways;
-        });
-        handler.on('relation', function(relation) {
-            if (relations == 0) {
-                assert.equal(relation.id, 237891);
-                assert.deepEqual(relation.members()[0], ['w', 40512249, 'outer']);
-                assert.deepEqual(relation.members(3), ['w', 40512257, 'inner']);
-            }
-            ++relations;
-        });
-        handler.on('done', function() {
-            assert.equal(nodes, 1525);
-            done();
-        });
-        var reader = new osmium.Reader(__dirname+"/data/winthrop.osm", { 'node': true, 'way': true });
-        reader.apply(handler);
-    });
-
-    it('should be able to use location handler', function(done) {
-        var location_handler = new osmium.LocationHandler();
-        var handler = new osmium.Handler();
-        var ways = 0;
-        handler.on('way', function(way) {
-            if (ways == 0) {
-                assert.equal(way.wkt(), "LINESTRING(-120.1872774 48.4715898,-120.188291 48.472511,-120.188374 48.472591,-120.188496 48.472707,-120.188625 48.47283,-120.18914 48.473561)");
-            }
-            ++ways;
-        });
-        handler.on('done', function() {
-            done();
-        });
-        var reader = new osmium.Reader(__dirname+"/data/winthrop.osm", { 'node': true, 'way': true });
-        reader.apply(location_handler, handler);
-    });
-
-    it('should be able to call before and after callbacks', function(done) {
-        var handler = new osmium.Handler();
-        var nodes = 0;
-        var before_nodes = 0, after_nodes = 0;
-
-        handler.on('init', function() {
-            assert.equal(nodes, 0);
-        });
-        handler.on('before_nodes', function() {
-            assert.equal(nodes, 0);
-            before_nodes++;
-        });
-        handler.on('node',function(node) {
-            ++nodes;
-        });
-        handler.on('after_nodes', function() {
-            assert.equal(nodes >= 1500, true);
-            after_nodes++;
-        });
-        handler.on('done',function() {
-            assert.equal(nodes >= 1500, true);
-            done();
-        });
-
-        var file = new osmium.File(__dirname+"/data/winthrop.osm");
-        var reader = new osmium.Reader(file);
-        reader.apply(handler);
-
-        assert.equal(before_nodes, 1);
-        assert.equal(after_nodes, 1);
-    });
-
-    it('should be able to call two handlers one after the other', function(done) {
-        var handler1 = new osmium.Handler();
-        var handler2 = new osmium.Handler();
-
-        var count=0;
-        handler1.on('init', function() {
-            assert.equal(count, 0);
-            count++;
-        });
-        handler2.on('init', function() {
-            assert.equal(count, 1);
-            count++;
-        });
-
-        var file = new osmium.File(__dirname+"/data/winthrop.osm");
-        var reader = new osmium.Reader(file);
-        reader.apply(handler1, handler2);
-
-        assert.equal(count, 2);
-        done();
-    });
-
-});
diff --git a/test/reader.test.js b/test/reader.test.js
new file mode 100644
index 0000000..b71a8a3
--- /dev/null
+++ b/test/reader.test.js
@@ -0,0 +1,69 @@
+var osmium = require('../');
+var fs = require('fs');
+var assert = require('assert');
+
+describe('reader', function() {
+
+    it('should throw when Reader called as function', function() {
+        assert.throws(function() {
+            var reader = osmium.Reader("foo");
+        }, Error);
+    });
+
+    it('should not hang when apply() is called twice on reader', function() {
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+        var handler = new osmium.Handler(); 
+        osmium.apply(reader, handler);
+
+        assert.throws(function() {
+            osmium.apply(reader, handler);
+        }, Error);
+    });
+
+    it('should be able to create an osmium.Reader and access header', function() {
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file, {});
+        var header = reader.header();
+        assert.equal(header.generator, 'CGImap 0.2.0');
+
+        var bounds = header.bounds[0];
+        assert.ok(Math.abs(bounds.left()   - (-120.2024)) < .000000001);
+        assert.ok(Math.abs(bounds.bottom() - (  48.4636)) < .000000001);
+        assert.ok(Math.abs(bounds.right()  - (-120.1569)) < .000000001);
+        assert.ok(Math.abs(bounds.top()    - (  48.4869)) < .000000001);
+        reader.close();
+    });
+
+    it('should be able to call apply() with an osmium.Reader and a handler', function() {
+        var reader = new osmium.Reader(__dirname + "/data/winthrop.osm");
+        var handler = new osmium.Handler();
+
+        osmium.apply(reader, handler);
+    });
+
+    it('should be able to create osmium.File with node.Buffer and read from it', function(done) {
+        var buffer = fs.readFileSync(__dirname + "/data/winthrop.osm");
+        assert.equal(buffer.length, 359898);
+
+        var file = new osmium.File(buffer, "osm");
+        assert.ok(file);
+
+        var reader = new osmium.Reader(file);
+        assert.ok(reader);
+
+        var handler = new osmium.Handler();
+
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.equal(node.id, 50031066);
+                done();
+            }
+        });
+
+        osmium.apply(reader, handler);
+    });
+
+});
+

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



More information about the Pkg-grass-devel mailing list