[node-osmium] 09/10: Imported Upstream version 0.3.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Fri Mar 6 17:09:14 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 38391dbafd15a97c071294c3269db47740af9df6
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Mar 6 15:16:44 2015 +0100

    Imported Upstream version 0.3.0
---
 .npmignore                                         |  10 +
 Makefile                                           |   1 +
 binding.gyp                                        |   3 +
 demo/converter/README.md                           |  14 +
 demo/converter/index.js                            | 117 +++++++++
 demo/converter/package.json                        |   7 +
 demo/converter/roads-from-buffer.js                |  94 +++++++
 demo/converter/roads.js                            |  92 +++++++
 demo/count/README.md                               |   8 +
 demo/count/index.js                                |  35 +++
 demo/count/package.json                            |   6 +
 demo/histstat/README.md                            |  13 +
 demo/histstat/index.js                             |  46 ++++
 demo/histstat/package.json                         |   6 +
 demo/multipolygon/README.md                        |  12 +
 demo/multipolygon/index.js                         |  41 +++
 demo/multipolygon/package.json                     |   7 +
 doc/tutorial.md                                    | 111 ++++++++
 lib/osmium.js                                      | 291 +++++++++++++++++++++
 package.json                                       |   2 +-
 src/apply.cpp                                      |  13 +-
 src/apply.hpp                                      |   8 +-
 src/buffer_wrap.cpp                                |  71 +++--
 src/buffer_wrap.hpp                                |  29 +-
 src/file_wrap.cpp                                  |   9 +-
 src/file_wrap.hpp                                  |  19 +-
 src/handler.cpp                                    |  27 +-
 src/handler.hpp                                    |  12 +-
 src/{apply.hpp => include_v8.hpp}                  |  13 +-
 src/location_handler_wrap.cpp                      |   6 +-
 src/location_handler_wrap.hpp                      |  18 +-
 src/multipolygon_collector_wrap.cpp                |  97 +++++++
 src/multipolygon_collector_wrap.hpp                |  46 ++++
 src/multipolygon_handler_wrap.cpp                  |  33 +++
 src/multipolygon_handler_wrap.hpp                  |  42 +++
 src/node_osmium.cpp                                |  75 +++++-
 src/node_osmium.hpp                                |  45 ++++
 src/osm_area_wrap.cpp                              |  71 +++++
 src/osm_area_wrap.hpp                              |  52 ++++
 src/osm_changeset_wrap.cpp                         |  15 +-
 src/osm_changeset_wrap.hpp                         |  17 +-
 src/osm_entity_wrap.cpp                            |   6 +-
 src/osm_entity_wrap.hpp                            |  14 +-
 src/osm_node_wrap.cpp                              |  29 +-
 src/osm_node_wrap.hpp                              |  16 +-
 src/osm_object_wrap.cpp                            |  14 +-
 src/osm_object_wrap.hpp                            |  12 +-
 src/osm_relation_wrap.cpp                          |  24 +-
 src/osm_relation_wrap.hpp                          |  18 +-
 src/osm_way_wrap.cpp                               |  21 +-
 src/osm_way_wrap.hpp                               |  16 +-
 src/reader_wrap.cpp                                |  50 ++--
 src/reader_wrap.hpp                                |  25 +-
 src/utils.cpp                                      |   5 +-
 src/utils.hpp                                      |  20 +-
 test/changesets.test.js                            |   1 +
 test/data/coordinates-problems.osm                 |   6 +
 ...reation.test.js => osm-object-creation.test.js} |   0
 test/osm-objects.test.js                           |  27 ++
 test/reader.test.js                                |  18 ++
 60 files changed, 1713 insertions(+), 243 deletions(-)

diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..f6a4e2e
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,10 @@
+.gitignore
+.gitmodules
+.npmignore
+.travis.yml
+Makefile
+build
+demo
+node_modules
+scripts
+test
diff --git a/Makefile b/Makefile
index f5e5d06..5ee8736 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@ INCLUDES_REPORT_FILES := $(subst src,check_reports,$(INCLUDE_FILES:.hpp=.compile
 DEMOS := $(shell find demo -mindepth 1 -maxdepth 1 -type d)
 
 all: build
+.PHONY: all build
 
 ./node_modules:
 	npm install --build-from-source
diff --git a/binding.gyp b/binding.gyp
index fd4eb2b..6dae20d 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -13,7 +13,10 @@
         "src/file_wrap.cpp",
         "src/handler.cpp",
         "src/location_handler_wrap.cpp",
+        "src/multipolygon_collector_wrap.cpp",
+        "src/multipolygon_handler_wrap.cpp",
         "src/node_osmium.cpp",
+        "src/osm_area_wrap.cpp",
         "src/osm_changeset_wrap.cpp",
         "src/osm_entity_wrap.cpp",
         "src/osm_node_wrap.cpp",
diff --git a/demo/converter/README.md b/demo/converter/README.md
new file mode 100644
index 0000000..9f7af87
--- /dev/null
+++ b/demo/converter/README.md
@@ -0,0 +1,14 @@
+# converter
+
+Convert OSM file into spatialite database.
+
+## Install
+
+    npm install
+
+## Usage
+
+    ./index.js ../../test/data/winthrop.osm winthrop.db
+
+You will get a spatialite database with several tables.
+
diff --git a/demo/converter/index.js b/demo/converter/index.js
new file mode 100755
index 0000000..3121e75
--- /dev/null
+++ b/demo/converter/index.js
@@ -0,0 +1,117 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+
+if (process.argv.length != 4) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE DBFILE");
+    process.exit(1);
+}
+
+var input_file = process.argv[2];
+var output_file = process.argv[3];
+
+var converter = new osmium.Converter({ output: output_file, show_layers: true });
+
+converter.create_layer('natural_pois', 'point').
+    with_attribute('osm_id', 'string').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string');
+
+converter.create_layer('roads', 'linestring').
+    with_attribute('osm_id', 'integer').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string').
+    with_attribute('ref', 'string').
+    with_attribute('oneway', 'integer').
+    with_attribute('maxspeed', 'integer');
+
+converter.create_layer('cycleways', 'linestring').
+    with_attribute('osm_id', 'integer').
+    with_attribute('name', 'string');
+
+converter.create_layer('railways', 'linestring').
+    with_attribute('osm_id', 'integer').
+    with_attribute('name', 'string');
+
+converter.create_layer('waterways', 'linestring').
+    with_attribute('osm_id', 'integer').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string');
+
+converter.create_layer('boundaries', 'multipolygon').
+    with_attribute('osm_id', 'integer').
+    with_attribute('level', 'integer').
+    with_attribute('name', 'string');
+
+converter.create_layer('landuse', 'multipolygon').
+    with_attribute('osm_id', 'integer').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string');
+
+converter.create_layer('water', 'multipolygon').
+    with_attribute('osm_id', 'integer').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string');
+
+// ---- rules ----
+
+converter.add_nodes().
+    matching('natural', 'tree|peak|spring').
+    to_layer('natural_pois').
+        with_attribute('type', 'natural').
+        with_attribute('name');
+
+converter.add_ways().
+    matching('waterway', 'stream|river|ditch|canal|drain').
+    to_layer('waterways').
+        with_attribute('type', 'waterway').
+        with_attribute('name');
+
+converter.add_ways().
+    matching('highway', /^(motorway|trunk|primary|secondary)(_link)?$/).
+    to_layer('roads').
+        with_attribute('type', 'highway').
+        with_attribute('ref').
+        with_attribute('name').
+        with_attribute('oneway', function(tags) {
+            var o = tags['oneway'];
+            if (o == 'yes' || o == 'true' || o == '1') {
+                return 1;
+            } else if (o == "-1") {
+                return -1;
+            } else {
+                return 0;
+            }
+        }).
+        with_attribute('maxspeed')
+
+converter.add_ways().
+    matching('highway', 'cycleway').
+    to_layer('cycleways').
+        with_attribute('name');
+
+converter.add_ways().
+    matching('railway', 'rail').
+    to_layer('railways').
+        with_attribute('name');
+
+converter.add_areas().
+    matching('boundary', 'administrative').
+    to_layer('boundaries').
+        with_attribute('level', 'admin_level').
+        with_attribute('name');
+
+converter.add_areas().
+    matching('landuse', 'forest|grass|residential|farm|meadow|farmland|industrial|farmyard|cemetery|commercial|quarry|orchard|vineyard|allotments|retail|construction|recreation_ground|village_green').
+    to_layer('landuse').
+        with_attribute('type', 'landuse').
+        with_attribute('name');
+
+converter.add_areas().
+    matching('natural', 'water').
+    to_layer('water').
+        with_attribute('type', 'natural').
+        with_attribute('name');
+
+converter.convert(input_file);
+
diff --git a/demo/converter/package.json b/demo/converter/package.json
new file mode 100644
index 0000000..1339b30
--- /dev/null
+++ b/demo/converter/package.json
@@ -0,0 +1,7 @@
+{
+  "name": "test",
+  "version": "0.0.0",
+  "dependencies": {
+    "spatialite": "*"
+  }
+}
diff --git a/demo/converter/roads-from-buffer.js b/demo/converter/roads-from-buffer.js
new file mode 100755
index 0000000..66adad4
--- /dev/null
+++ b/demo/converter/roads-from-buffer.js
@@ -0,0 +1,94 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+
+if (process.argv.length != 4) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE DBFILE");
+    process.exit(1);
+}
+
+var input_file = process.argv[2];
+var output_file = process.argv[3];
+
+var converter = new osmium.Converter({ output: output_file, show_layers: true });
+
+// ---- layers ----
+
+converter.create_layer('turning_circles').
+    with_attribute('osm_id', 'string');
+
+converter.create_layer('traffic_signals').
+    with_attribute('osm_id', 'string');
+
+converter.create_layer('major_roads', 'linestring').
+    with_attribute('osm_id', 'integer').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string').
+    with_attribute('ref', 'string').
+    with_attribute('oneway', 'integer').
+    with_attribute('maxspeed', 'integer');
+
+converter.create_layer('minor_roads', 'linestring').
+    with_attribute('osm_id', 'integer').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string').
+    with_attribute('ref', 'string').
+    with_attribute('oneway', 'integer').
+    with_attribute('maxspeed', 'integer');
+
+converter.create_layer('plazas', 'multipolygon').
+    with_attribute('osm_id', 'integer').
+    with_attribute('name', 'string');
+
+
+// ---- rules ----
+
+converter.add_nodes().
+    matching('highway', 'traffic_signals').
+    to_layer('traffic_signals');
+
+converter.add_nodes().
+    matching('highway', 'turning_circle').
+    to_layer('turning_circles');
+
+function convert_oneway(tags) {
+    var o = tags['oneway'];
+    if (o == 'yes' || o == 'true' || o == '1') {
+        return 1;
+    } else if (o == "-1") {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+converter.add_ways().
+    matching('highway', /^(motorway|trunk|primary|secondary)(_link)?$/).
+    to_layer('major_roads').
+        with_attribute('type', 'highway').
+        with_attribute('ref').
+        with_attribute('name').
+        with_attribute('oneway', convert_oneway).
+        with_attribute('maxspeed')
+
+var minor_roads = 'tertiary|unclassified|residential|living_street|pedestrian|service|track|path|footway|cycleway|road|steps';
+
+converter.add_ways().
+    matching('highway', minor_roads).
+    to_layer('minor_roads').
+        with_attribute('type', 'highway').
+        with_attribute('ref').
+        with_attribute('name').
+        with_attribute('oneway', convert_oneway).
+        with_attribute('maxspeed');
+
+converter.add_areas().
+    matching('highway', minor_roads).
+    matching('area', 'yes').
+    to_layer('plazas').
+        with_attribute('name');
+
+var reader = new osmium.Reader(input_file);
+var buffer = reader.read_all();
+converter.convert(buffer);
+
diff --git a/demo/converter/roads.js b/demo/converter/roads.js
new file mode 100755
index 0000000..1ff6de0
--- /dev/null
+++ b/demo/converter/roads.js
@@ -0,0 +1,92 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+
+if (process.argv.length != 4) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE DBFILE");
+    process.exit(1);
+}
+
+var input_file = process.argv[2];
+var output_file = process.argv[3];
+
+var converter = new osmium.Converter({ output: output_file, show_layers: true });
+
+// ---- layers ----
+
+converter.create_layer('turning_circles').
+    with_attribute('osm_id', 'string');
+
+converter.create_layer('traffic_signals').
+    with_attribute('osm_id', 'string');
+
+converter.create_layer('major_roads', 'linestring').
+    with_attribute('osm_id', 'integer').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string').
+    with_attribute('ref', 'string').
+    with_attribute('oneway', 'integer').
+    with_attribute('maxspeed', 'integer');
+
+converter.create_layer('minor_roads', 'linestring').
+    with_attribute('osm_id', 'integer').
+    with_attribute('type', 'string').
+    with_attribute('name', 'string').
+    with_attribute('ref', 'string').
+    with_attribute('oneway', 'integer').
+    with_attribute('maxspeed', 'integer');
+
+converter.create_layer('plazas', 'multipolygon').
+    with_attribute('osm_id', 'integer').
+    with_attribute('name', 'string');
+
+
+// ---- rules ----
+
+converter.add_nodes().
+    matching('highway', 'traffic_signals').
+    to_layer('traffic_signals');
+
+converter.add_nodes().
+    matching('highway', 'turning_circle').
+    to_layer('turning_circles');
+
+function convert_oneway(tags) {
+    var o = tags['oneway'];
+    if (o == 'yes' || o == 'true' || o == '1') {
+        return 1;
+    } else if (o == "-1") {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+converter.add_ways().
+    matching('highway', /^(motorway|trunk|primary|secondary)(_link)?$/).
+    to_layer('major_roads').
+        with_attribute('type', 'highway').
+        with_attribute('ref').
+        with_attribute('name').
+        with_attribute('oneway', convert_oneway).
+        with_attribute('maxspeed')
+
+var minor_roads = 'tertiary|unclassified|residential|living_street|pedestrian|service|track|path|footway|cycleway|road|steps';
+
+converter.add_ways().
+    matching('highway', minor_roads).
+    to_layer('minor_roads').
+        with_attribute('type', 'highway').
+        with_attribute('ref').
+        with_attribute('name').
+        with_attribute('oneway', convert_oneway).
+        with_attribute('maxspeed');
+
+converter.add_areas().
+    matching('highway', minor_roads).
+    matching('area', 'yes').
+    to_layer('plazas').
+        with_attribute('name');
+
+converter.convert(input_file);
+
diff --git a/demo/count/README.md b/demo/count/README.md
new file mode 100644
index 0000000..0e8cdbf
--- /dev/null
+++ b/demo/count/README.md
@@ -0,0 +1,8 @@
+# count
+
+Count the number of nodes, ways, and relations in an OSM file.
+
+## Usage
+
+    ./index.js ../../test/data/winthrop.osm
+
diff --git a/demo/count/index.js b/demo/count/index.js
new file mode 100755
index 0000000..87bcadb
--- /dev/null
+++ b/demo/count/index.js
@@ -0,0 +1,35 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+
+if (process.argv.length != 3) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE");
+    process.exit(1);
+}
+
+var input_filename = process.argv[2];
+
+// =====================================
+
+var handler = new osmium.Handler();
+
+var nodes=0, ways=0, relations=0;
+
+handler.on('node', function(node) {
+    nodes++;
+});
+
+handler.on('way', function(way) {
+    ways++;
+});
+
+handler.on('relation', function(relation) {
+    relations++;
+});
+
+var reader = new osmium.Reader(input_filename);
+osmium.apply(reader, handler);
+console.log("Nodes: " + nodes);
+console.log("Ways: " + ways);
+console.log("Relations: " + relations);
+
diff --git a/demo/count/package.json b/demo/count/package.json
new file mode 100644
index 0000000..71993f6
--- /dev/null
+++ b/demo/count/package.json
@@ -0,0 +1,6 @@
+{
+  "name": "test",
+  "version": "0.0.0",
+  "dependencies": {
+  }
+}
diff --git a/demo/histstat/README.md b/demo/histstat/README.md
new file mode 100644
index 0000000..2e556a4
--- /dev/null
+++ b/demo/histstat/README.md
@@ -0,0 +1,13 @@
+# histstat
+
+Reads OSM history file and outputs the number of highways at the beginning of
+each year.
+
+## Install
+
+    npm install
+
+## Usage
+
+    ./index.js OSMFILE
+
diff --git a/demo/histstat/index.js b/demo/histstat/index.js
new file mode 100755
index 0000000..fb2c666
--- /dev/null
+++ b/demo/histstat/index.js
@@ -0,0 +1,46 @@
+#!/usr/bin/env node
+
+var osmium = require('../../');
+
+if (process.argv.length != 3) {
+    console.log("Usage: " + process.argv[0] + ' ' + process.argv[1] + " OSMFILE");
+    process.exit(1);
+}
+
+var input_filename = process.argv[2];
+
+// =====================================
+
+var handler = new osmium.Handler();
+
+var count = 0;
+handler.on('way', function(way) {
+    if (way.tags('highway')) {
+        count++;
+    }
+});
+
+console.log("Reading input file into memory...\n");
+var reader = new osmium.Reader(input_filename);
+var buffer = reader.read_all();
+reader.close();
+
+
+var year = 2008;
+var endyear = (new Date()).getYear() + 1900;
+
+console.log("Calculating stats for years " + year + " to " + endyear + "\n");
+
+while (year <= endyear) {
+    var date = new Date(year, 1, 1);
+
+    var tbuffer = buffer.filter_point_in_time(date);
+
+    osmium.apply(tbuffer, handler);
+    tbuffer.clear();
+    console.log(year + ": " + count);
+
+    count = 0;
+    year++;
+}
+
diff --git a/demo/histstat/package.json b/demo/histstat/package.json
new file mode 100644
index 0000000..71993f6
--- /dev/null
+++ b/demo/histstat/package.json
@@ -0,0 +1,6 @@
+{
+  "name": "test",
+  "version": "0.0.0",
+  "dependencies": {
+  }
+}
diff --git a/demo/multipolygon/README.md b/demo/multipolygon/README.md
new file mode 100644
index 0000000..d209f2c
--- /dev/null
+++ b/demo/multipolygon/README.md
@@ -0,0 +1,12 @@
+# multipolygon
+
+Reads OSM file and outputs some data for landuse areas.
+
+## Install
+
+    npm install
+
+## Usage
+
+    ./index.js ../../test/data/winthrop.osm winthrop.csv
+
diff --git a/demo/multipolygon/index.js b/demo/multipolygon/index.js
new file mode 100755
index 0000000..f536a75
--- /dev/null
+++ b/demo/multipolygon/index.js
@@ -0,0 +1,41 @@
+#!/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.on('area', function(area) {
+    var landuse = area.tags('landuse');
+    if (landuse) {
+        stream.write('a' + area.id + ' ' + landuse + ' ' + (area.tags('name') || '') + ' ' +  area.wkt() + "\n"); 
+    }
+});
+
+handler.on('done', function() {
+    stream.close();
+});
+
+var mp = new osmium.MultipolygonCollector();
+
+var reader = new osmium.Reader(input_filename);
+mp.read_relations(reader);
+reader.close();
+
+reader = new osmium.Reader(input_filename);
+var location_handler = new osmium.LocationHandler();
+osmium.apply(reader, location_handler, handler, mp.handler(handler));
+reader.close();
+
diff --git a/demo/multipolygon/package.json b/demo/multipolygon/package.json
new file mode 100644
index 0000000..2635237
--- /dev/null
+++ b/demo/multipolygon/package.json
@@ -0,0 +1,7 @@
+{
+  "name": "test",
+  "version": "0.0.0",
+  "dependencies": {
+    "buffered-writer": "*"
+  }
+}
diff --git a/doc/tutorial.md b/doc/tutorial.md
index 32931c9..5150a3c 100644
--- a/doc/tutorial.md
+++ b/doc/tutorial.md
@@ -357,6 +357,117 @@ 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.
 
+## Working with Multipolygons
+
+OSM doesn't have a data type for areas or polygons. Instead areas are stored
+as closed ways (ie first node == last node) or relations tagged as
+`type=multipolygon`. Osmium can hide this complexity and create pseudo-objects
+called "Areass" that are either based on closed ways or on those multipoygon
+relations. Usually OSM files have to be read twice to allow this, though. In
+the first pass, relations are read and prepared, in the second pass nodes and
+ways are read and everything is assembled. In addition to the `node`, `way`,
+and `relation` callbacks, you can define an `area` callback.
+
+Here is an example:
+
+    var handler = new osmium.Handler();
+
+    handler.on('area', function(area) {
+        var landuse = area.tags('landuse');
+        if (landuse) {
+            console.log(area.wkt() + ' ' + landuse);
+        }
+    });
+
+    var mp = new osmium.MultipolygonCollector();
+
+    var reader = new osmium.Reader(input_filename);
+    mp.read_relations(reader);
+    reader.close();
+
+    reader = new osmium.Reader(input_filename);
+    var location_handler = new osmium.LocationHandler();
+    osmium.apply(reader, location_handler, handler, mp.handler(handler));
+    reader.close();
+
+The code looks a bit complicated, because it is modelled after the C++ it is
+based on. It is likely we'll make this easier at some point. You can not re-use
+the reader from the first pass, you have to create a new one for the second
+pass.
+
+
+## Working With Buffers
+
+Instead of calling `apply()` you can call `read()` on the Reader and you'll get
+an `osmium.Buffer` with zero or more OSM entities in it. If there is no more
+data in the file, `read()` returns `undefined`:
+
+    var reader = new osmium.Reader("foo.osm");
+    var buffer;
+    while (buffer = reader.read()) {
+        // do something here
+    }
+
+You can call `apply()` with a `Buffer` and handlers, just like you would do
+with the `Reader`:
+
+    osmium.apply(buffer, handler1, handler2);
+
+There are no guarantees how many entities are in one of those buffers. They can
+even be empty, but usually they will contain a few thousand entities, so they
+are "small" in comparison to a big OSM data file. So if you call `apply()` on a
+`Buffer` it will return much sooner than when calling `apply()` on the
+`Reader`. This allows you some amount of control over how fast the input is
+read.
+
+    var reader = new osmium.Reader("foo.osm");
+    var buffer;
+    while (buffer = reader.read()) {
+        osmium.apply(buffer, handler1, handler2);
+        // wait here if needed to slow down reading the file
+    }
+
+Note that calling `apply()` repeatedly in this way on the buffers instead of
+once on the `Reader`, confuses the `done`, `init`, and `before_*` and `after_*`
+handlers. You will get those handlers called for each `apply()` separately.
+
+### Iterating Over the Contents of a Buffer
+
+Instead of calling `apply()` you can iterate over the contents of the buffer by
+calling `next()`:
+
+    var object;
+    while (object = buffer.next()) {
+        console.log(object.id());
+    }
+
+`buffer.next()` returns `undefined` when there is no more data.
+
+If you use the `buffer.next()` call, you'll get the next object, whatever that
+is, you have to check with `object instanceof osmium.Node` or so if you got the
+right type. Note that you can not use the `LocationHandler` this way.
+
+Together you can read the content of a file like this:
+
+    var reader = new osmium.Reader("foo.osm");
+    var buffer;
+    while (buffer = reader.read()) {
+        var object;
+        while (object = buffer.next()) {
+            // do something here with object
+        }
+    }
+
+### Creating an Osmium Buffer From a Node Buffer
+
+You can also create an `osmium.Buffer` from a `node.Buffer`:
+
+    var node_buffer = new node.Buffer;
+    // fill it with OSM data somehow
+    var osmium_buffer = new osmium.Buffer(node_buffer);
+
+This buffer can now be used like the buffers we got from the `Reader`.
+
 ## A Complete Example
 
 Finally here is a complete example to get you started: This parses an OSM file
diff --git a/lib/osmium.js b/lib/osmium.js
index f31365c..2f76c5a 100644
--- a/lib/osmium.js
+++ b/lib/osmium.js
@@ -56,3 +56,294 @@ exports.Way.prototype.geojson = function() {
     };
 }
 
+exports.Converter = function(args) {
+    if (args === undefined) {
+        args = {};
+    }
+
+    this.show_layers = args.show_layers ? true : false;
+    this.output_name = args.output || "out.db";
+
+    function Layer(name, geom_type) {
+        this.name = name;
+        this.geom_type = geom_type.toUpperCase();
+
+        if (this.geom_type != 'POINT' && this.geom_type != 'LINESTRING' && this.geom_type != 'MULTIPOLYGON') {
+            throw new Error('Unknown geometry type: "' + geom_type + '" (allowed are: "POINT", "LINESTRING", and "MULTIPOLYGON")');
+        }
+
+        this.attributes = [];
+        this.attribute_names = {};
+
+        this.with_attribute = function(name, type) {
+            var attribute_type = type.toUpperCase();
+            if (attribute_type != 'INTEGER' && attribute_type != 'STRING' && attribute_type != 'BOOL' && attribute_type != 'REAL') {
+                throw new Error('Unknown attribute type: ' + type);
+            }
+            var attribute = { name: name, type: attribute_type };
+            this.attributes.push(attribute);
+            this.attribute_names[name] = attribute;
+            return this;
+        }
+    }
+
+    var layers = {};
+
+    function Filter(key, value) {
+        this.key = key;
+
+        if (value == '*') {
+            this.value = null;
+        } else {
+            this.value = value;
+        }
+
+        if (typeof(value) == 'string' && value.match(/\|/)) {
+            value = value.split('|');
+        }
+
+        if (value == null) {
+            this.match = function(obj) {
+                return !!obj.tags(key);
+            };
+        } else if (typeof(value) == 'string') {
+            this.match = function(obj) {
+                return obj.tags(key) == value;
+            };
+        } else if (value instanceof Array) {
+            this.match = function(obj) {
+                var val = obj.tags(key);
+                if (! val) {
+                    return false;
+                }
+                return value.indexOf(val) != -1;
+            };
+        } else if (value instanceof RegExp) {
+            this.match = function(obj) {
+                return obj.tags(key) && value.test(obj.tags(key));
+            }
+        } else {
+            throw new Error('Can not understand rule');
+        }
+    }
+
+    function Rule(type) {
+        this.type = type;
+
+        this.layer = null;
+        this.filters = [];
+        this.attrs = {};
+
+        this.matching = function(key, value) {
+            this.filters.push(new Filter(key, value));
+            return this;
+        };
+
+        this.to_layer = function(name) {
+            if (! layers[name]) {
+                throw new Error('Unknown layer: ' + name);
+            }
+            this.layer = name;
+            return this;
+        };
+
+        this.with_attribute = function(attr, key) {
+            if (this.layer == null) {
+                throw new Error('Layer not set for rule ' + key + '=' + value);
+            }
+
+            if (! layers[this.layer].attribute_names[attr]) {
+                throw new Error("There is no attribute named '" + attr + "' in layer '" + this.layer + "'");
+            }
+
+            if (key instanceof Function) {
+                this.attrs[attr] = key;
+            } else if (key == null) {
+                this.attrs[attr] = function(tags) {
+                    return tags[attr];
+                };
+            } else {
+                this.attrs[attr] = function(tags) {
+                    return tags[key];
+                };
+            }
+
+            return this;
+        };
+
+        this.match = function(osm_object) {
+            var result = true;
+            this.filters.forEach(function(filter) {
+                if (!filter.match(osm_object)) {
+                    result = false;
+                }
+            });
+            return result;
+        };
+    }
+
+    this.create_layer = function(name, geom_type) {
+        if (geom_type == undefined) {
+            geom_type = 'POINT';
+        }
+        var layer = new Layer(name, geom_type);
+        layers[name] = layer;
+        return layer;
+    }
+
+    var rules = {
+        node: [],
+        way:  [],
+        area: []
+    };
+
+    this.rule = function(object_type) {
+        var rule = new Rule(object_type);
+        rules[object_type].push(rule);
+        return rule;
+    }
+
+    this.add_nodes = function() {
+        return this.rule('node');
+    }
+
+    this.add_ways = function() {
+        return this.rule('way');
+    }
+
+    this.add_areas = function() {
+        return this.rule('area');
+    }
+
+    function tags2attributes(id, tags, attrs) {
+        var obj = { osm_id: id };
+        for (var a in attrs) {
+            obj[a] = attrs[a](tags);
+        }
+        return obj;
+    }
+
+    function convert(osm_object) {
+        rules[osm_object.type].forEach(function(rule) {
+            if (rule.match(osm_object)) {
+                try {
+                    var attributes = tags2attributes(osm_object.id, osm_object.tags(), rule.attrs);
+                    var p = [osm_object.wkb()];
+                    var layer = layers[rule.layer];
+                    layer.attributes.forEach(function(attribute) {
+                        p.push(attributes[attribute.name]);
+                    });
+                    layer.insert.run.apply(layer.insert, p);
+                } catch (e) {
+                    // ignore broken geometries
+                }
+            }
+        });
+    }
+
+    var handler = new osmium.Handler;
+    handler.on('node', convert);
+    handler.on('way', convert);
+    handler.on('area', convert);
+
+    this.convert = function(input) {
+
+        var fs = require('fs');
+        if (fs.existsSync(this.output_name)) {
+            console.log("Output database file '" + this.output_name + "' exists. Refusing to overwrite it.");
+            process.exit(1);
+        }
+
+        // setup database
+        var sqlite = require('spatialite');
+        var db = new sqlite.Database(this.output_name);
+        db.serialize();
+
+        db.spatialite();
+        db.run("PRAGMA synchronous = OFF;"); // otherwise it is very slow
+        db.run("SELECT InitSpatialMetaData('WGS84');");
+
+        var show_layers = this.show_layers;
+
+        // setup layers in database
+        for (var t in layers) {
+            var layer = layers[t];
+
+            if (show_layers) {
+                console.log('Layer: ' + layer.name + ' (' + layer.geom_type + ')');
+            }
+
+            db.run("CREATE TABLE " + layer.name + " (id INTEGER PRIMARY KEY);");
+            db.run("SELECT AddGeometryColumn('" + layer.name + "', 'geom', 4326, '" + layer.geom_type + "', 2);");
+
+            var insert = "INSERT INTO " + layer.name + " (geom";
+            var qm = '';
+
+            layer.attributes.forEach(function(attribute) {
+                if (show_layers) {
+                    console.log('  ' + (attribute.name + '          ').substr(0, 11) + attribute.type);
+                }
+                db.run("ALTER TABLE " + layer.name + " ADD COLUMN " + attribute.name + " " + attribute.type + ";");
+                insert += ", " + attribute.name;
+                qm += ", ?";
+            });
+
+            insert += ") VALUES (GeomFromWKB(?, 4326)" + qm + ");";
+
+            layer.insert = db.prepare(insert);
+            if (show_layers) {
+                console.log('');
+            }
+        }
+
+        // convert data
+        console.log("Converting data...");
+        if (rules.area.length > 0) {
+            console.log("  First pass. Reading relations...");
+            var mp = new osmium.MultipolygonCollector();
+
+            if (input instanceof osmium.Buffer) {
+                mp.read_relations(input);
+            } else {
+                var reader = new osmium.Reader(input, { 'relation': true });
+                mp.read_relations(reader);
+                reader.close();
+            }
+
+            console.log("  Second pass. Reading nodes and ways...");
+            var location_handler = new osmium.LocationHandler();
+            location_handler.ignoreErrors();
+            if (input instanceof osmium.Buffer) {
+                osmium.apply(input, location_handler, handler, mp.handler(handler));
+            } else {
+                reader = new osmium.Reader(input, { 'node': true, 'way': true });
+                osmium.apply(reader, location_handler, handler, mp.handler(handler));
+                reader.close();
+            }
+        } else {
+            console.log("  Reading everything in one pass...");
+
+            var location_handler = new osmium.LocationHandler();
+            location_handler.ignoreErrors();
+            if (input instanceof osmium.Buffer) {
+                osmium.apply(input, location_handler, handler);
+            } else {
+                var reader = new osmium.Reader(input);
+                osmium.apply(reader, location_handler, handler);
+                reader.close();
+            }
+        }
+
+        // cleanup
+        console.log("Cleanup...");
+        for (var layer in layers) {
+            layers[layer].insert.finalize();
+        }
+
+        db.close(function() {
+            console.log("Done.");
+        });
+    };
+
+};
+
diff --git a/package.json b/package.json
index 28707e4..46e21cd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "osmium",
-    "version": "0.2.0",
+    "version": "0.3.0",
     "description": "Node.js bindings to Osmium",
     "url": "https://github.com/osmcode/node-osmium",
     "homepage": "http://osmcode.org/node-osmium",
diff --git a/src/apply.cpp b/src/apply.cpp
index 9c5f8ff..18fa436 100644
--- a/src/apply.cpp
+++ b/src/apply.cpp
@@ -16,6 +16,8 @@
 #include "buffer_wrap.hpp"
 #include "handler.hpp"
 #include "location_handler_wrap.hpp"
+#include "multipolygon_collector_wrap.hpp"
+#include "multipolygon_handler_wrap.hpp"
 #include "reader_wrap.hpp"
 #include "utils.hpp"
 
@@ -37,6 +39,10 @@ namespace node_osmium {
             osmium::apply_item(m_entity, handler);
         }
 
+        void operator()(osmium::area::MultipolygonCollector<osmium::area::Assembler>::HandlerPass2& handler) const {
+            osmium::apply_item(m_entity, handler);
+        }
+
     }; // struct visitor_type
 
     struct visitor_before_after_type : public boost::static_visitor<> {
@@ -57,6 +63,9 @@ namespace node_osmium {
         void operator()(location_handler_type& visitor) const {
         }
 
+        void operator()(osmium::area::MultipolygonCollector<osmium::area::Assembler>::HandlerPass2& handler) const {
+        }
+
         void operator()(JSHandler& visitor) const {
             switch (m_last) {
                 case osmium::item_type::undefined:
@@ -100,7 +109,7 @@ namespace node_osmium {
 
     }; // struct visitor_before_after
 
-    typedef boost::variant<location_handler_type&, JSHandler&> some_handler_type;
+    typedef boost::variant<location_handler_type&, JSHandler&, osmium::area::MultipolygonCollector<osmium::area::Assembler>::HandlerPass2&> some_handler_type;
 
     template <class TIter>
     v8::Handle<v8::Value> apply_iterator(TIter it, TIter end, std::vector<some_handler_type>& handlers) {
@@ -165,6 +174,8 @@ namespace node_osmium {
                     handlers.push_back(unwrap<JSHandler>(obj));
                 } else if (LocationHandlerWrap::constructor->HasInstance(obj)) {
                     handlers.push_back(unwrap<LocationHandlerWrap>(obj));
+                } else if (MultipolygonHandlerWrap::constructor->HasInstance(obj)) {
+                    handlers.push_back(unwrap<MultipolygonHandlerWrap>(obj));
                 }
             }
 
diff --git a/src/apply.hpp b/src/apply.hpp
index 903327f..e0a5115 100644
--- a/src/apply.hpp
+++ b/src/apply.hpp
@@ -1,12 +1,8 @@
 #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
+// v8/node
+#include "include_v8.hpp"
 
 namespace node_osmium {
 
diff --git a/src/buffer_wrap.cpp b/src/buffer_wrap.cpp
index 6f38cb2..0e03ec5 100644
--- a/src/buffer_wrap.cpp
+++ b/src/buffer_wrap.cpp
@@ -2,11 +2,16 @@
 // node
 #include <node_buffer.h>
 
+// osmium
+#include <osmium/diff_iterator.hpp>
+
 // node-osmium
+#include "node_osmium.hpp"
 #include "buffer_wrap.hpp"
 #include "osm_node_wrap.hpp"
 #include "osm_way_wrap.hpp"
 #include "osm_relation_wrap.hpp"
+#include "osm_area_wrap.hpp"
 #include "osm_changeset_wrap.hpp"
 #include "utils.hpp"
 
@@ -18,9 +23,11 @@ namespace node_osmium {
         v8::HandleScope scope;
         constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(BufferWrap::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(v8::String::NewSymbol("Buffer"));
+        constructor->SetClassName(symbol_Buffer);
+        node::SetPrototypeMethod(constructor, "clear", clear);
         node::SetPrototypeMethod(constructor, "next", next);
-        target->Set(v8::String::NewSymbol("Buffer"), constructor->GetFunction());
+        node::SetPrototypeMethod(constructor, "filter_point_in_time", filter_point_in_time);
+        target->Set(symbol_Buffer, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> BufferWrap::New(const v8::Arguments& args) {
@@ -32,8 +39,8 @@ namespace node_osmium {
             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());
+                BufferWrap* buffer_wrap = new BufferWrap(std::move(buffer));
+                buffer_wrap->Wrap(args.This());
                 return args.This();
             }
         }
@@ -43,31 +50,26 @@ namespace node_osmium {
     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()) {
+        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);
+                return scope.Close(new_external<OSMNodeWrap>(entity));
             }
             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);
+                return scope.Close(new_external<OSMWayWrap>(entity));
             }
             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);
+                return scope.Close(new_external<OSMRelationWrap>(entity));
+            }
+            case osmium::item_type::area: {
+                return scope.Close(new_external<OSMAreaWrap>(entity));
             }
             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);
+                return scope.Close(new_external<OSMChangesetWrap>(entity));
             }
             default:
                 break;
@@ -75,5 +77,40 @@ namespace node_osmium {
         return scope.Close(v8::Undefined());
     }
 
+    v8::Handle<v8::Value> BufferWrap::filter_point_in_time(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        if (args.Length() != 1) {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a point in time as first and only argument")));
+        }
+
+        osmium::Timestamp point_in_time;
+        if (args[0]->IsInt32()) {
+            point_in_time = args[0]->Int32Value();
+        } else if (args[0]->IsString()) {
+            point_in_time = osmium::Timestamp(*v8::String::Utf8Value(args[0]->ToString()));
+        } else if (args[0]->IsDate()) {
+            point_in_time = osmium::Timestamp(static_cast<int32_t>(v8::Date::Cast(*args[0])->NumberValue() / 1000));
+        }
+
+        typedef osmium::DiffIterator<osmium::memory::Buffer::t_iterator<osmium::OSMObject>> diff_iterator;
+        osmium::memory::Buffer& buffer = unwrap<BufferWrap>(args.This());
+        osmium::memory::Buffer fbuffer(buffer.committed(), osmium::memory::Buffer::auto_grow::yes);
+        {
+            auto dbegin = diff_iterator(buffer.begin<osmium::OSMObject>(), buffer.end<osmium::OSMObject>());
+            auto dend   = diff_iterator(buffer.end<osmium::OSMObject>(), buffer.end<osmium::OSMObject>());
+
+            std::for_each(dbegin, dend, [point_in_time, &fbuffer](const osmium::DiffObject& d) {
+                if (((d.end_time() == 0 || d.end_time() > point_in_time) &&
+                        d.start_time() <= point_in_time) &&
+                    d.curr().visible()) {
+                    fbuffer.add_item(d.curr());
+                    fbuffer.commit();
+                }
+            });
+        }
+
+        return scope.Close(new_external<BufferWrap>(std::move(fbuffer)));
+    }
+
 } // namespace node_osmium
 
diff --git a/src/buffer_wrap.hpp b/src/buffer_wrap.hpp
index c2db592..c218fe4 100644
--- a/src/buffer_wrap.hpp
+++ b/src/buffer_wrap.hpp
@@ -4,16 +4,8 @@
 // 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>
+// v8/node
+#include "include_v8.hpp"
 #include <node_object_wrap.h>
 
 // osmium
@@ -23,10 +15,16 @@ namespace node_osmium {
 
     class BufferWrap : public node::ObjectWrap {
 
-        std::shared_ptr<osmium::memory::Buffer> m_this;
+        osmium::memory::Buffer m_this;
         osmium::memory::Buffer::iterator m_iterator;
 
+        static v8::Handle<v8::Value> clear(const v8::Arguments& args) {
+            BufferWrap* buffer_wrap = node::ObjectWrap::Unwrap<BufferWrap>(args.This());
+            buffer_wrap->m_this = std::move(osmium::memory::Buffer());
+        }
+
         static v8::Handle<v8::Value> next(const v8::Arguments& args);
+        static v8::Handle<v8::Value> filter_point_in_time(const v8::Arguments& args);
 
     public:
 
@@ -35,18 +33,17 @@ namespace node_osmium {
         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()) {
+            m_this(std::move(buffer)),
+            m_iterator(m_this.begin()) {
         }
 
         osmium::memory::Buffer& get() {
-            return *m_this;
+            return m_this;
         }
 
     private:
 
-        ~BufferWrap() {
-        }
+        ~BufferWrap() = default;
 
     }; // class BufferWrap
 
diff --git a/src/file_wrap.cpp b/src/file_wrap.cpp
index 0bacd01..78e6dea 100644
--- a/src/file_wrap.cpp
+++ b/src/file_wrap.cpp
@@ -2,12 +2,11 @@
 // c++
 #include <exception>
 
-// v8
-#include <v8.h>
-
 // node
 #include <node_buffer.h>
 
+// node-osmium
+#include "node_osmium.hpp"
 #include "file_wrap.hpp"
 
 namespace node_osmium {
@@ -18,8 +17,8 @@ namespace node_osmium {
         v8::HandleScope scope;
         constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(FileWrap::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(v8::String::NewSymbol("File"));
-        target->Set(v8::String::NewSymbol("File"), constructor->GetFunction());
+        constructor->SetClassName(symbol_File);
+        target->Set(symbol_File, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> FileWrap::New(const v8::Arguments& args) {
diff --git a/src/file_wrap.hpp b/src/file_wrap.hpp
index 2020d5e..195c3f5 100644
--- a/src/file_wrap.hpp
+++ b/src/file_wrap.hpp
@@ -5,14 +5,8 @@
 #include <memory>
 #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
+// v8/node
+#include "include_v8.hpp"
 #include <node_object_wrap.h>
 
 // osmium
@@ -22,7 +16,7 @@ namespace node_osmium {
 
     class FileWrap : public node::ObjectWrap {
 
-        std::shared_ptr<osmium::io::File> m_this;
+        osmium::io::File m_this;
 
     public:
 
@@ -32,17 +26,16 @@ namespace node_osmium {
 
         FileWrap(osmium::io::File&& file) :
             node::ObjectWrap(),
-            m_this(std::make_shared<osmium::io::File>(file)) {
+            m_this(std::move(file)) {
         }
 
         osmium::io::File& get() {
-            return *m_this;
+            return m_this;
         }
 
     private:
 
-        ~FileWrap() {
-        }
+        ~FileWrap() = default;
 
     }; // class FileWrap
 
diff --git a/src/handler.cpp b/src/handler.cpp
index 5482fd1..bbce412 100644
--- a/src/handler.cpp
+++ b/src/handler.cpp
@@ -2,9 +2,6 @@
 // c++
 #include <string>
 
-// v8
-#include <v8.h>
-
 // node
 #include <node.h>
 #include <node_object_wrap.h>
@@ -13,10 +10,12 @@
 #include <osmium/osm/object.hpp>
 
 // node-osmium
+#include "node_osmium.hpp"
 #include "handler.hpp"
 #include "osm_node_wrap.hpp"
 #include "osm_way_wrap.hpp"
 #include "osm_relation_wrap.hpp"
+#include "osm_area_wrap.hpp"
 #include "osm_changeset_wrap.hpp"
 #include "utils.hpp"
 
@@ -29,10 +28,10 @@ namespace node_osmium {
         v8::HandleScope scope;
         constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(JSHandler::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(v8::String::NewSymbol("Handler"));
+        constructor->SetClassName(symbol_Handler);
         node::SetPrototypeMethod(constructor, "on", on);
         node::SetPrototypeMethod(constructor, "options", options);
-        target->Set(v8::String::NewSymbol("Handler"), constructor->GetFunction());
+        target->Set(symbol_Handler, constructor->GetFunction());
 
         symbol_tagged_nodes_only = NODE_PSYMBOL("tagged_nodes_only");
     }
@@ -57,6 +56,8 @@ namespace node_osmium {
         relation_cb.Dispose();
         after_relations_cb.Dispose();
 
+        area_cb.Dispose();
+
         before_changesets_cb.Dispose();
         changeset_cb.Dispose();
         after_changesets_cb.Dispose();
@@ -76,8 +77,8 @@ namespace node_osmium {
             b->Wrap(args.This());
             return args.This();
         } else {
-            JSHandler* h = new JSHandler();
-            h->Wrap(args.This());
+            JSHandler* jshandler = new JSHandler();
+            jshandler->Wrap(args.This());
             return args.This();
         }
         return scope.Close(v8::Undefined());
@@ -118,6 +119,9 @@ namespace node_osmium {
         } else if (callback_name == "relation") {
             handler.relation_cb.Dispose();
             handler.relation_cb = v8::Persistent<v8::Function>::New(callback);
+        } else if (callback_name == "area") {
+            handler.area_cb.Dispose();
+            handler.area_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);
@@ -165,9 +169,7 @@ namespace node_osmium {
         }
 
         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 };
+        v8::Local<v8::Value> argv[1] = { new_external<TWrapped>(entity) };
         function->Call(v8::Context::GetCurrent()->Global(), 1, argv);
     }
 
@@ -198,6 +200,11 @@ namespace node_osmium {
                     call_callback_with_entity<OSMRelationWrap>(relation_cb, entity);
                 }
                 break;
+            case osmium::item_type::area:
+                if (!area_cb.IsEmpty()) {
+                    call_callback_with_entity<OSMAreaWrap>(area_cb, entity);
+                }
+                break;
             case osmium::item_type::changeset:
                 if (!changeset_cb.IsEmpty()) {
                     call_callback_with_entity<OSMChangesetWrap>(changeset_cb, entity);
diff --git a/src/handler.hpp b/src/handler.hpp
index 843874e..922ef5c 100644
--- a/src/handler.hpp
+++ b/src/handler.hpp
@@ -1,14 +1,8 @@
 #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
+// v8/node
+#include "include_v8.hpp"
 #include <node_object_wrap.h>
 
 // osmium
@@ -38,6 +32,8 @@ namespace node_osmium {
         v8::Persistent<v8::Function> relation_cb;
         v8::Persistent<v8::Function> after_relations_cb;
 
+        v8::Persistent<v8::Function> area_cb;
+
         v8::Persistent<v8::Function> before_changesets_cb;
         v8::Persistent<v8::Function> changeset_cb;
         v8::Persistent<v8::Function> after_changesets_cb;
diff --git a/src/apply.hpp b/src/include_v8.hpp
similarity index 50%
copy from src/apply.hpp
copy to src/include_v8.hpp
index 903327f..6b72ffd 100644
--- a/src/apply.hpp
+++ b/src/include_v8.hpp
@@ -1,17 +1,10 @@
-#ifndef APPLY_HPP
-#define APPLY_HPP
+#ifndef INCLUDE_V8_HPP
+#define INCLUDE_V8_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
+#endif // INCLUDE_V8_HPP
diff --git a/src/location_handler_wrap.cpp b/src/location_handler_wrap.cpp
index 63a2e10..b7b9e8b 100644
--- a/src/location_handler_wrap.cpp
+++ b/src/location_handler_wrap.cpp
@@ -1,4 +1,6 @@
 
+// node-osmium
+#include "node_osmium.hpp"
 #include "location_handler_wrap.hpp"
 #include "utils.hpp"
 
@@ -10,10 +12,10 @@ namespace node_osmium {
         v8::HandleScope scope;
         constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(LocationHandlerWrap::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(v8::String::NewSymbol("LocationHandler"));
+        constructor->SetClassName(symbol_LocationHandler);
         node::SetPrototypeMethod(constructor, "clear", clear);
         node::SetPrototypeMethod(constructor, "ignoreErrors", ignoreErrors);
-        target->Set(v8::String::NewSymbol("LocationHandler"), constructor->GetFunction());
+        target->Set(symbol_LocationHandler, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> LocationHandlerWrap::New(const v8::Arguments& args) {
diff --git a/src/location_handler_wrap.hpp b/src/location_handler_wrap.hpp
index 1d47a6e..70e85b2 100644
--- a/src/location_handler_wrap.hpp
+++ b/src/location_handler_wrap.hpp
@@ -3,15 +3,10 @@
 
 // c++
 #include <memory>
+#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
+// v8/node
+#include "include_v8.hpp"
 #include <node_object_wrap.h>
 
 // osmium
@@ -67,7 +62,7 @@ namespace node_osmium {
         std::unique_ptr<index_pos_type> m_index_pos;
         std::unique_ptr<index_neg_type> m_index_neg;
 
-        std::shared_ptr<location_handler_type> m_this;
+        std::unique_ptr<location_handler_type> m_this;
 
         static v8::Handle<v8::Value> clear(const v8::Arguments& args);
         static v8::Handle<v8::Value> ignoreErrors(const v8::Arguments& args);
@@ -82,7 +77,7 @@ namespace node_osmium {
             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)) {
+            m_this(new location_handler_type(*m_index_pos, *m_index_neg)) {
             if (!m_index_pos) {
                 throw std::runtime_error("unknown node cache type");
             }
@@ -94,8 +89,7 @@ namespace node_osmium {
 
     private:
 
-        ~LocationHandlerWrap() {
-        }
+        ~LocationHandlerWrap() = default;
 
     }; // class LocationHandlerWrap
 
diff --git a/src/multipolygon_collector_wrap.cpp b/src/multipolygon_collector_wrap.cpp
new file mode 100644
index 0000000..cedad87
--- /dev/null
+++ b/src/multipolygon_collector_wrap.cpp
@@ -0,0 +1,97 @@
+
+// node
+#include <node_buffer.h>
+
+// node-osmium
+#include "node_osmium.hpp"
+#include "multipolygon_collector_wrap.hpp"
+#include "multipolygon_handler_wrap.hpp"
+#include "handler.hpp"
+#include "buffer_wrap.hpp"
+#include "reader_wrap.hpp"
+#include "utils.hpp"
+#include "apply.hpp"
+
+namespace node_osmium {
+
+    v8::Persistent<v8::FunctionTemplate> MultipolygonCollectorWrap::constructor;
+
+    void MultipolygonCollectorWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(MultipolygonCollectorWrap::New));
+        constructor->InstanceTemplate()->SetInternalFieldCount(1);
+        constructor->SetClassName(symbol_MultipolygonCollector);
+        node::SetPrototypeMethod(constructor, "read_relations", read_relations);
+        node::SetPrototypeMethod(constructor, "handler", handler);
+        target->Set(symbol_MultipolygonCollector, constructor->GetFunction());
+    }
+
+    v8::Handle<v8::Value> MultipolygonCollectorWrap::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.Length() != 0) {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("MultipolygonCollector is constructed without arguments")));
+        }
+
+        try {
+            MultipolygonCollectorWrap* multipolygon_collector_wrap = new MultipolygonCollectorWrap();
+            multipolygon_collector_wrap->Wrap(args.This());
+            return args.This();
+        } catch (const std::exception& e) {
+            return ThrowException(v8::Exception::TypeError(v8::String::New(e.what())));
+        }
+
+        return scope.Close(v8::Undefined());
+    }
+
+    v8::Handle<v8::Value> MultipolygonCollectorWrap::read_relations(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        if (args.Length() != 1 || !args[0]->IsObject()) {
+            return ThrowException(v8::Exception::Error(v8::String::New("call MultipolygonCollector.read_relation() with Reader or Buffer object")));
+        }
+        try {
+            if (ReaderWrap::constructor->HasInstance(args[0]->ToObject())) {
+                osmium::io::Reader& reader = unwrap<ReaderWrap>(args[0]->ToObject());
+                unwrap<MultipolygonCollectorWrap>(args.This()).read_relations(reader);
+            } else if (BufferWrap::constructor->HasInstance(args[0]->ToObject())) {
+                osmium::memory::Buffer& buffer = unwrap<BufferWrap>(args[0]->ToObject());
+                unwrap<MultipolygonCollectorWrap>(args.This()).read_relations(buffer.begin(), buffer.end());
+            } else {
+                return ThrowException(v8::Exception::Error(v8::String::New("call MultipolygonCollector.read_relation() with Reader or Buffer object")));
+            }
+        } 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> MultipolygonCollectorWrap::handler(const v8::Arguments& args) {
+        v8::HandleScope scope;
+        if (args.Length() != 1 || !args[0]->IsObject() || !JSHandler::constructor->HasInstance(args[0]->ToObject())) {
+            return ThrowException(v8::Exception::Error(v8::String::New("call MultipolygonCollector.handler() with Handler object")));
+        }
+        try {
+            JSHandler& handler = unwrap<JSHandler>(args[0]->ToObject());
+            auto& mc_handler = unwrap<MultipolygonCollectorWrap>(args.This()).handler([&handler](const osmium::memory::Buffer& area_buffer) {
+                for (const osmium::OSMEntity& entity : area_buffer) {
+                    handler.dispatch_entity(entity);
+                }
+            });
+            return scope.Close(new_external<MultipolygonHandlerWrap>(mc_handler));
+
+        } 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());
+    }
+
+} // namespace node_osmium
+
diff --git a/src/multipolygon_collector_wrap.hpp b/src/multipolygon_collector_wrap.hpp
new file mode 100644
index 0000000..b59c5fb
--- /dev/null
+++ b/src/multipolygon_collector_wrap.hpp
@@ -0,0 +1,46 @@
+#ifndef MULTIPOLYGON_COLLECTOR_WRAP_HPP
+#define MULTIPOLYGON_COLLECTOR_WRAP_HPP
+
+// v8/node
+#include "include_v8.hpp"
+#include <node_object_wrap.h>
+
+// osmium
+#include <osmium/area/multipolygon_collector.hpp>
+#include <osmium/area/assembler.hpp>
+
+namespace node_osmium {
+
+    class MultipolygonCollectorWrap : public node::ObjectWrap {
+
+        static v8::Handle<v8::Value> read_relations(const v8::Arguments& args);
+        static v8::Handle<v8::Value> handler(const v8::Arguments& args);
+
+        osmium::area::Assembler::config_type m_assembler_config;
+        osmium::area::MultipolygonCollector<osmium::area::Assembler> m_collector;
+
+    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);
+
+        MultipolygonCollectorWrap() :
+            node::ObjectWrap(),
+            m_assembler_config(),
+            m_collector(m_assembler_config) {
+        }
+
+        osmium::area::MultipolygonCollector<osmium::area::Assembler>& get() {
+            return m_collector;
+        }
+
+    private:
+
+        ~MultipolygonCollectorWrap() = default;
+
+    }; // class MultipolygonCollectorWrap
+
+} // namespace node_osmium
+
+#endif // MULTIPOLYGON_COLLECTOR_WRAP_HPP
diff --git a/src/multipolygon_handler_wrap.cpp b/src/multipolygon_handler_wrap.cpp
new file mode 100644
index 0000000..e715bfe
--- /dev/null
+++ b/src/multipolygon_handler_wrap.cpp
@@ -0,0 +1,33 @@
+
+// node
+#include <node_buffer.h>
+
+// node-osmium
+#include "node_osmium.hpp"
+#include "multipolygon_handler_wrap.hpp"
+#include "utils.hpp"
+
+namespace node_osmium {
+
+    v8::Persistent<v8::FunctionTemplate> MultipolygonHandlerWrap::constructor;
+
+    void MultipolygonHandlerWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(MultipolygonHandlerWrap::New));
+        constructor->InstanceTemplate()->SetInternalFieldCount(1);
+        constructor->SetClassName(symbol_MultipolygonHandler);
+        target->Set(symbol_MultipolygonHandler, constructor->GetFunction());
+    }
+
+    v8::Handle<v8::Value> MultipolygonHandlerWrap::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<MultipolygonHandlerWrap*>(ext->Value())->Wrap(args.This());
+            return args.This();
+        } else {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.MultipolygonHandler cannot be created in Javascript")));
+        }
+    }
+
+} // namespace node_osmium
+
diff --git a/src/multipolygon_handler_wrap.hpp b/src/multipolygon_handler_wrap.hpp
new file mode 100644
index 0000000..155cae1
--- /dev/null
+++ b/src/multipolygon_handler_wrap.hpp
@@ -0,0 +1,42 @@
+#ifndef MULTIPOLYGON_HANDLER_WRAP_HPP
+#define MULTIPOLYGON_HANDLER_WRAP_HPP
+
+// v8/node
+#include "include_v8.hpp"
+#include <node_object_wrap.h>
+
+// osmium
+#include <osmium/area/multipolygon_collector.hpp>
+#include <osmium/area/assembler.hpp>
+
+namespace node_osmium {
+
+    class MultipolygonHandlerWrap : public node::ObjectWrap {
+
+        typedef osmium::area::MultipolygonCollector<osmium::area::Assembler>::HandlerPass2 handler_type;
+        handler_type m_handler;
+
+    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);
+
+        MultipolygonHandlerWrap(handler_type& handler) :
+            node::ObjectWrap(),
+            m_handler(handler) {
+        }
+
+        handler_type& get() {
+            return m_handler;
+        }
+
+    private:
+
+        ~MultipolygonHandlerWrap() = default;
+
+    }; // class MultipolygonHandlerWrap
+
+} // namespace node_osmium
+
+#endif // MULTIPOLYGON_HANDLER_WRAP_HPP
diff --git a/src/node_osmium.cpp b/src/node_osmium.cpp
index 16b6e47..fc439d8 100644
--- a/src/node_osmium.cpp
+++ b/src/node_osmium.cpp
@@ -1,9 +1,3 @@
-// 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>
@@ -21,6 +15,9 @@
 #include "file_wrap.hpp"
 #include "handler.hpp"
 #include "location_handler_wrap.hpp"
+#include "multipolygon_collector_wrap.hpp"
+#include "multipolygon_handler_wrap.hpp"
+#include "osm_area_wrap.hpp"
 #include "osm_changeset_wrap.hpp"
 #include "osm_node_wrap.hpp"
 #include "osm_relation_wrap.hpp"
@@ -33,6 +30,41 @@ namespace node_osmium {
     osmium::geom::WKBFactory<> wkb_factory;
     osmium::geom::WKTFactory<> wkt_factory;
 
+    v8::Persistent<v8::String> symbol_OSMEntity;
+    v8::Persistent<v8::String> symbol_OSMObject;
+
+    v8::Persistent<v8::String> symbol_Node;
+    v8::Persistent<v8::String> symbol_node;
+
+    v8::Persistent<v8::String> symbol_Way;
+    v8::Persistent<v8::String> symbol_way;
+
+    v8::Persistent<v8::String> symbol_Relation;
+    v8::Persistent<v8::String> symbol_relation;
+    v8::Persistent<v8::String> symbol_type;
+    v8::Persistent<v8::String> symbol_ref;
+    v8::Persistent<v8::String> symbol_role;
+
+    v8::Persistent<v8::String> symbol_Area;
+    v8::Persistent<v8::String> symbol_area;
+
+    v8::Persistent<v8::String> symbol_Changeset;
+    v8::Persistent<v8::String> symbol_changeset;
+
+    v8::Persistent<v8::String> symbol_Coordinates;
+    v8::Persistent<v8::String> symbol_Box;
+
+    v8::Persistent<v8::String> symbol_generator;
+    v8::Persistent<v8::String> symbol_bounds;
+
+    v8::Persistent<v8::String> symbol_Buffer;
+    v8::Persistent<v8::String> symbol_File;
+    v8::Persistent<v8::String> symbol_Handler;
+    v8::Persistent<v8::String> symbol_LocationHandler;
+    v8::Persistent<v8::String> symbol_MultipolygonCollector;
+    v8::Persistent<v8::String> symbol_MultipolygonHandler;
+    v8::Persistent<v8::String> symbol_Reader;
+
     extern "C" {
         static void start(v8::Handle<v8::Object> target) {
             v8::HandleScope scope;
@@ -40,12 +72,43 @@ namespace node_osmium {
 
             node::SetMethod(target, "apply", node_osmium::apply);
 
+            symbol_OSMEntity       = NODE_PSYMBOL("OSMEntity");
+            symbol_OSMObject       = NODE_PSYMBOL("OSMObject");
+            symbol_Node            = NODE_PSYMBOL("Node");
+            symbol_node            = NODE_PSYMBOL("node");
+            symbol_Way             = NODE_PSYMBOL("Way");
+            symbol_way             = NODE_PSYMBOL("way");
+            symbol_Relation        = NODE_PSYMBOL("Relation");
+            symbol_relation        = NODE_PSYMBOL("relation");
+            symbol_type            = NODE_PSYMBOL("type");
+            symbol_ref             = NODE_PSYMBOL("ref");
+            symbol_role            = NODE_PSYMBOL("role");
+            symbol_Area            = NODE_PSYMBOL("Area");
+            symbol_area            = NODE_PSYMBOL("area");
+            symbol_Changeset       = NODE_PSYMBOL("Changeset");
+            symbol_changeset       = NODE_PSYMBOL("changeset");
+            symbol_Coordinates     = NODE_PSYMBOL("Coordinates");
+            symbol_Box             = NODE_PSYMBOL("Box");
+            symbol_generator       = NODE_PSYMBOL("generator");
+            symbol_bounds          = NODE_PSYMBOL("bounds");
+
+            symbol_Buffer                = NODE_PSYMBOL("Buffer");
+            symbol_File                  = NODE_PSYMBOL("File");
+            symbol_Handler               = NODE_PSYMBOL("Handler");
+            symbol_LocationHandler       = NODE_PSYMBOL("LocationHandler");
+            symbol_MultipolygonCollector = NODE_PSYMBOL("MultipolygonCollector");
+            symbol_MultipolygonHandler   = NODE_PSYMBOL("MultipolygonHandler");
+            symbol_Reader                = NODE_PSYMBOL("Reader");
+
             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::OSMAreaWrap::Initialize(target);
             node_osmium::OSMChangesetWrap::Initialize(target);
+            node_osmium::MultipolygonCollectorWrap::Initialize(target);
+            node_osmium::MultipolygonHandlerWrap::Initialize(target);
             node_osmium::LocationHandlerWrap::Initialize(target);
             node_osmium::JSHandler::Initialize(target);
             node_osmium::FileWrap::Initialize(target);
diff --git a/src/node_osmium.hpp b/src/node_osmium.hpp
new file mode 100644
index 0000000..8ab7915
--- /dev/null
+++ b/src/node_osmium.hpp
@@ -0,0 +1,45 @@
+#ifndef NODE_OSMIUM
+#define NODE_OSMIUM
+
+#include "include_v8.hpp"
+
+namespace node_osmium {
+
+    extern v8::Persistent<v8::String> symbol_OSMEntity;
+    extern v8::Persistent<v8::String> symbol_OSMObject;
+
+    extern v8::Persistent<v8::String> symbol_Node;
+    extern v8::Persistent<v8::String> symbol_node;
+
+    extern v8::Persistent<v8::String> symbol_Way;
+    extern v8::Persistent<v8::String> symbol_way;
+
+    extern v8::Persistent<v8::String> symbol_Relation;
+    extern v8::Persistent<v8::String> symbol_relation;
+    extern v8::Persistent<v8::String> symbol_type;
+    extern v8::Persistent<v8::String> symbol_ref;
+    extern v8::Persistent<v8::String> symbol_role;
+
+    extern v8::Persistent<v8::String> symbol_Area;
+    extern v8::Persistent<v8::String> symbol_area;
+
+    extern v8::Persistent<v8::String> symbol_Changeset;
+    extern v8::Persistent<v8::String> symbol_changeset;
+
+    extern v8::Persistent<v8::String> symbol_Coordinates;
+    extern v8::Persistent<v8::String> symbol_Box;
+
+    extern v8::Persistent<v8::String> symbol_generator;
+    extern v8::Persistent<v8::String> symbol_bounds;
+
+    extern v8::Persistent<v8::String> symbol_Buffer;
+    extern v8::Persistent<v8::String> symbol_File;
+    extern v8::Persistent<v8::String> symbol_Handler;
+    extern v8::Persistent<v8::String> symbol_LocationHandler;
+    extern v8::Persistent<v8::String> symbol_MultipolygonCollector;
+    extern v8::Persistent<v8::String> symbol_MultipolygonHandler;
+    extern v8::Persistent<v8::String> symbol_Reader;
+
+} // namespace node_osmium
+
+#endif // NODE_OSMIUM
diff --git a/src/osm_area_wrap.cpp b/src/osm_area_wrap.cpp
new file mode 100644
index 0000000..5bf59db
--- /dev/null
+++ b/src/osm_area_wrap.cpp
@@ -0,0 +1,71 @@
+
+// node
+#include <node_buffer.h>
+#include <node_version.h>
+
+// osmium
+#include <osmium/geom/wkb.hpp>
+#include <osmium/geom/wkt.hpp>
+
+// node-osmium
+#include "osm_area_wrap.hpp"
+
+namespace node_osmium {
+
+    extern osmium::geom::WKBFactory<> wkb_factory;
+    extern osmium::geom::WKTFactory<> wkt_factory;
+    extern v8::Persistent<v8::Object> module;
+
+    v8::Persistent<v8::FunctionTemplate> OSMAreaWrap::constructor;
+
+    void OSMAreaWrap::Initialize(v8::Handle<v8::Object> target) {
+        v8::HandleScope scope;
+        constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMAreaWrap::New));
+        constructor->Inherit(OSMObjectWrap::constructor);
+        constructor->InstanceTemplate()->SetInternalFieldCount(1);
+        constructor->SetClassName(symbol_Area);
+        auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "type", get_type, attributes);
+        node::SetPrototypeMethod(constructor, "wkb", wkb);
+        node::SetPrototypeMethod(constructor, "wkt", wkt);
+        target->Set(symbol_Area, constructor->GetFunction());
+    }
+
+    v8::Handle<v8::Value> OSMAreaWrap::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<OSMAreaWrap*>(ext->Value())->Wrap(args.This());
+            return args.This();
+        } else {
+            return ThrowException(v8::Exception::TypeError(v8::String::New("osmium.Area cannot be created in Javascript")));
+        }
+    }
+
+    v8::Handle<v8::Value> OSMAreaWrap::wkb(const v8::Arguments& args) {
+        v8::HandleScope scope;
+
+        try {
+            std::string wkb { wkb_factory.create_multipolygon(wrapped(args.This())) };
+#if NODE_VERSION_AT_LEAST(0, 10, 0)
+            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_);
+#endif
+        } catch (std::runtime_error& e) {
+            return ThrowException(v8::Exception::Error(v8::String::New(e.what())));
+        }
+    }
+
+    v8::Handle<v8::Value> OSMAreaWrap::wkt(const v8::Arguments& args) {
+        v8::HandleScope scope;
+
+        try {
+            std::string wkt { wkt_factory.create_multipolygon(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_area_wrap.hpp b/src/osm_area_wrap.hpp
new file mode 100644
index 0000000..f62aaf7
--- /dev/null
+++ b/src/osm_area_wrap.hpp
@@ -0,0 +1,52 @@
+#ifndef OSM_AREA_WRAP_HPP
+#define OSM_AREA_WRAP_HPP
+
+// v8/node
+#include "include_v8.hpp"
+
+// osmium
+#include <osmium/osm/area.hpp>
+namespace osmium {
+    class OSMEntity;
+}
+
+// node-osmium
+#include "node_osmium.hpp"
+#include "osm_entity_wrap.hpp"
+#include "osm_object_wrap.hpp"
+#include "utils.hpp"
+
+namespace node_osmium {
+
+    class OSMAreaWrap : public OSMObjectWrap {
+
+        static v8::Handle<v8::Value> get_type(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
+            return symbol_area;
+        }
+
+        static v8::Handle<v8::Value> wkb(const v8::Arguments& args);
+        static v8::Handle<v8::Value> wkt(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::Area& wrapped(const v8::Local<v8::Object>& object) {
+            return static_cast<const osmium::Area&>(unwrap<OSMEntityWrap>(object));
+        }
+
+        OSMAreaWrap(const osmium::OSMEntity& entity) :
+            OSMObjectWrap(entity) {
+        }
+
+    private:
+
+        ~OSMAreaWrap() = default;
+
+    }; // class OSMAreaWrap
+
+} // namespace node_osmium
+
+#endif // OSM_AREA_WRAP_HPP
diff --git a/src/osm_changeset_wrap.cpp b/src/osm_changeset_wrap.cpp
index f8a02ca..e2c78f5 100644
--- a/src/osm_changeset_wrap.cpp
+++ b/src/osm_changeset_wrap.cpp
@@ -1,4 +1,5 @@
 
+// node-osmium
 #include "osm_changeset_wrap.hpp"
 #include "utils.hpp"
 
@@ -10,11 +11,13 @@ namespace node_osmium {
 
     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"));
+        constructor->SetClassName(symbol_Changeset);
         auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "type", get_type, attributes);
         set_accessor(constructor, "id", get_id, attributes);
         set_accessor(constructor, "uid", get_uid, attributes);
         set_accessor(constructor, "user", get_user, attributes);
@@ -25,7 +28,7 @@ namespace node_osmium {
         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());
+        target->Set(symbol_Changeset, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> OSMChangesetWrap::New(const v8::Arguments& args) {
@@ -49,7 +52,7 @@ namespace node_osmium {
 
     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()));
+        return scope.Close(v8::Uint32::New(wrapped(info.This()).uid()));
     }
 
     v8::Handle<v8::Value> OSMChangesetWrap::get_user(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
@@ -59,17 +62,17 @@ namespace node_osmium {
 
     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()));
+        return scope.Close(v8::Uint32::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()));
+        return scope.Close(v8::Uint32::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()));
+        return scope.Close(v8::Uint32::New(wrapped(info.This()).closed_at()));
     }
 
     v8::Handle<v8::Value> OSMChangesetWrap::get_open(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
diff --git a/src/osm_changeset_wrap.hpp b/src/osm_changeset_wrap.hpp
index ca605f9..b490045 100644
--- a/src/osm_changeset_wrap.hpp
+++ b/src/osm_changeset_wrap.hpp
@@ -1,12 +1,8 @@
 #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
+// v8/node
+#include "include_v8.hpp"
 
 // osmium
 #include <osmium/osm/changeset.hpp>
@@ -15,6 +11,7 @@ namespace osmium {
 }
 
 // node-osmium
+#include "node_osmium.hpp"
 #include "osm_entity_wrap.hpp"
 #include "utils.hpp"
 
@@ -22,6 +19,10 @@ namespace node_osmium {
 
     class OSMChangesetWrap : public OSMEntityWrap {
 
+        static v8::Handle<v8::Value> get_type(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
+            return symbol_changeset;
+        }
+
         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);
@@ -47,6 +48,10 @@ namespace node_osmium {
             OSMEntityWrap(entity) {
         }
 
+    private:
+
+        ~OSMChangesetWrap() = default;
+
     }; // class OSMChangesetWrap
 
 } // namespace node_osmium
diff --git a/src/osm_entity_wrap.cpp b/src/osm_entity_wrap.cpp
index 91191f0..51122bd 100644
--- a/src/osm_entity_wrap.cpp
+++ b/src/osm_entity_wrap.cpp
@@ -1,4 +1,6 @@
 
+// node-osmium
+#include "node_osmium.hpp"
 #include "osm_entity_wrap.hpp"
 
 namespace node_osmium {
@@ -9,8 +11,8 @@ namespace node_osmium {
         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());
+        constructor->SetClassName(symbol_OSMEntity);
+        target->Set(symbol_OSMEntity, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> OSMEntityWrap::New(const v8::Arguments& args) {
diff --git a/src/osm_entity_wrap.hpp b/src/osm_entity_wrap.hpp
index bf25136..0e14587 100644
--- a/src/osm_entity_wrap.hpp
+++ b/src/osm_entity_wrap.hpp
@@ -1,14 +1,8 @@
 #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
+// v8/node
+#include "include_v8.hpp"
 #include <node_object_wrap.h>
 
 // osmium
@@ -71,6 +65,10 @@ namespace node_osmium {
             return *m_entity;
         }
 
+    protected:
+
+        virtual ~OSMEntityWrap() = default;
+
     }; // class OSMEntityWrap
 
 } // namespace node_osmium
diff --git a/src/osm_node_wrap.cpp b/src/osm_node_wrap.cpp
index 57a0f2c..3d21eb5 100644
--- a/src/osm_node_wrap.cpp
+++ b/src/osm_node_wrap.cpp
@@ -23,15 +23,16 @@ namespace node_osmium {
         constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMNodeWrap::New));
         constructor->Inherit(OSMObjectWrap::constructor);
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(v8::String::NewSymbol("Node"));
+        constructor->SetClassName(symbol_Node);
         node::SetPrototypeMethod(constructor, "wkb", wkb);
         node::SetPrototypeMethod(constructor, "wkt", wkt);
         auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "type", get_type, attributes);
         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());
+        target->Set(symbol_Node, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> OSMNodeWrap::New(const v8::Arguments& args) {
@@ -47,24 +48,36 @@ namespace node_osmium {
     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"));
+        auto cf = module->Get(symbol_Coordinates);
         assert(cf->IsFunction());
 
+        const osmium::Location& location = wrapped(info.This()).location();
+        if (!location) {
+            return scope.Close(v8::Local<v8::Function>::Cast(cf)->NewInstance());
+        }
+
+        v8::Local<v8::Value> lon = v8::Number::New(location.lon_without_check());
+        v8::Local<v8::Value> lat = v8::Number::New(location.lat_without_check());
         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()));
+        try {
+            return scope.Close(v8::Number::New(wrapped(info.This()).location().lon()));
+        } catch (osmium::invalid_location&) {
+            return scope.Close(v8::Undefined());
+        }
     }
 
     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()));
+        try {
+            return scope.Close(v8::Number::New(wrapped(info.This()).location().lat()));
+        } catch (osmium::invalid_location&) {
+            return scope.Close(v8::Undefined());
+        }
     }
 
     v8::Handle<v8::Value> OSMNodeWrap::wkb(const v8::Arguments& args) {
diff --git a/src/osm_node_wrap.hpp b/src/osm_node_wrap.hpp
index 157df91..5ae12da 100644
--- a/src/osm_node_wrap.hpp
+++ b/src/osm_node_wrap.hpp
@@ -1,12 +1,8 @@
 #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>
-#pragma GCC diagnostic pop
+// v8/node
+#include "include_v8.hpp"
 
 // osmium
 #include <osmium/osm/node.hpp>
@@ -15,6 +11,7 @@ namespace osmium {
 }
 
 // node-osmium
+#include "node_osmium.hpp"
 #include "osm_entity_wrap.hpp"
 #include "osm_object_wrap.hpp"
 #include "utils.hpp"
@@ -23,6 +20,10 @@ namespace node_osmium {
 
     class OSMNodeWrap : public OSMObjectWrap {
 
+        static v8::Handle<v8::Value> get_type(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
+            return symbol_node;
+        }
+
         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);
@@ -45,8 +46,7 @@ namespace node_osmium {
 
     private:
 
-        ~OSMNodeWrap() {
-        }
+        ~OSMNodeWrap() = default;
 
     }; // class OSMNodeWrap
 
diff --git a/src/osm_object_wrap.cpp b/src/osm_object_wrap.cpp
index 710018a..b771947 100644
--- a/src/osm_object_wrap.cpp
+++ b/src/osm_object_wrap.cpp
@@ -1,4 +1,6 @@
 
+// node-osmium
+#include "node_osmium.hpp"
 #include "osm_object_wrap.hpp"
 
 namespace node_osmium {
@@ -9,7 +11,7 @@ namespace node_osmium {
         v8::HandleScope scope;
         constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMObjectWrap::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(v8::String::NewSymbol("OSMObject"));
+        constructor->SetClassName(symbol_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);
@@ -19,7 +21,7 @@ namespace node_osmium {
         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());
+        target->Set(symbol_OSMObject, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> OSMObjectWrap::New(const v8::Arguments& args) {
@@ -43,12 +45,12 @@ namespace node_osmium {
 
     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()));
+        return scope.Close(v8::Uint32::New(wrapped(info.This()).version()));
     }
 
     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()));
+        return scope.Close(v8::Uint32::New(wrapped(info.This()).changeset()));
     }
 
     v8::Handle<v8::Value> OSMObjectWrap::get_visible(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
@@ -58,12 +60,12 @@ namespace node_osmium {
 
     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()));
+        return scope.Close(v8::Uint32::New(wrapped(info.This()).timestamp()));
     }
 
     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()));
+        return scope.Close(v8::Uint32::New(wrapped(info.This()).uid()));
     }
 
     v8::Handle<v8::Value> OSMObjectWrap::get_user(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info) {
diff --git a/src/osm_object_wrap.hpp b/src/osm_object_wrap.hpp
index a735d93..2bf27f3 100644
--- a/src/osm_object_wrap.hpp
+++ b/src/osm_object_wrap.hpp
@@ -1,12 +1,8 @@
 #ifndef OSM_OBJECT_WRAP_HPP
 #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>
-#pragma GCC diagnostic pop
+// v8/node
+#include "include_v8.hpp"
 
 // osmium
 #include <osmium/osm/object.hpp>
@@ -45,6 +41,10 @@ namespace node_osmium {
             OSMEntityWrap(entity) {
         }
 
+    protected:
+
+        virtual ~OSMObjectWrap() = default;
+
     }; // class OSMObjectWrap
 
 } // namespace node_osmium
diff --git a/src/osm_relation_wrap.cpp b/src/osm_relation_wrap.cpp
index fc8b225..db30e22 100644
--- a/src/osm_relation_wrap.cpp
+++ b/src/osm_relation_wrap.cpp
@@ -1,29 +1,25 @@
 
+// node
 #include <node.h>
 
-#include <osm_relation_wrap.hpp>
+// node-osmium
+#include "osm_relation_wrap.hpp"
 
 namespace node_osmium {
 
     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(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(v8::String::NewSymbol("Relation"));
+        constructor->SetClassName(symbol_Relation);
         auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "type", get_type, attributes);
         set_accessor(constructor, "members_count", get_members_count, attributes);
         node::SetPrototypeMethod(constructor, "members", members);
-        target->Set(v8::String::NewSymbol("Relation"), constructor->GetFunction());
-
-        symbol_type = NODE_PSYMBOL("type");
-        symbol_ref  = NODE_PSYMBOL("ref");
-        symbol_role = NODE_PSYMBOL("role");
+        target->Set(symbol_Relation, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> OSMRelationWrap::New(const v8::Arguments& args) {
@@ -38,7 +34,7 @@ namespace node_osmium {
 
     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()));
+        return scope.Close(v8::Uint32::New(wrapped(info.This()).members().size()));
     }
 
     v8::Handle<v8::Value> OSMRelationWrap::members(const v8::Arguments& args) {
@@ -63,11 +59,11 @@ namespace node_osmium {
                 return scope.Close(members);
             }
             case 1: {
-                if (!args[0]->IsNumber()) {
+                if (!args[0]->IsUint32()) {
                     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())) {
+                uint32_t n = args[0]->ToUint32()->Value();
+                if (n < relation.members().size()) {
                     auto it = relation.members().begin();
                     std::advance(it, n);
                     const osmium::RelationMember& member = *it;
diff --git a/src/osm_relation_wrap.hpp b/src/osm_relation_wrap.hpp
index 534fe48..cc88ce9 100644
--- a/src/osm_relation_wrap.hpp
+++ b/src/osm_relation_wrap.hpp
@@ -1,12 +1,8 @@
 #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>
-#pragma GCC diagnostic pop
+// v8/node
+#include "include_v8.hpp"
 
 // osmium
 #include <osmium/osm/relation.hpp>
@@ -15,6 +11,7 @@ namespace osmium {
 }
 
 // node-osmium
+#include "node_osmium.hpp"
 #include "osm_entity_wrap.hpp"
 #include "osm_object_wrap.hpp"
 #include "utils.hpp"
@@ -23,9 +20,9 @@ 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_type(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
+            return symbol_relation;
+        }
 
         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);
@@ -46,8 +43,7 @@ namespace node_osmium {
 
     private:
 
-        ~OSMRelationWrap() {
-        }
+        ~OSMRelationWrap() = default;
 
     }; // class OSMRelationWrap
 
diff --git a/src/osm_way_wrap.cpp b/src/osm_way_wrap.cpp
index 86991e3..b26c92e 100644
--- a/src/osm_way_wrap.cpp
+++ b/src/osm_way_wrap.cpp
@@ -23,14 +23,15 @@ namespace node_osmium {
         constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(OSMWayWrap::New));
         constructor->Inherit(OSMObjectWrap::constructor);
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(v8::String::NewSymbol("Way"));
+        constructor->SetClassName(symbol_Way);
         auto attributes = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
+        set_accessor(constructor, "type", get_type, attributes);
         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());
+        target->Set(symbol_Way, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> OSMWayWrap::New(const v8::Arguments& args) {
@@ -71,7 +72,7 @@ namespace node_osmium {
 
     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()));
+        return scope.Close(v8::Uint32::New(wrapped(info.This()).nodes().size()));
     }
 
     v8::Handle<v8::Value> OSMWayWrap::node_refs(const v8::Arguments& args) {
@@ -90,11 +91,11 @@ namespace node_osmium {
                 return scope.Close(nodes);
             }
             case 1: {
-                if (!args[0]->IsNumber()) {
+                if (!args[0]->IsUint32()) {
                     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())) {
+                uint32_t n = args[0]->ToUint32()->Value();
+                if (n < 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")));
@@ -108,7 +109,7 @@ namespace node_osmium {
     v8::Handle<v8::Value> OSMWayWrap::node_coordinates(const v8::Arguments& args) {
         v8::HandleScope scope;
 
-        auto cf = module->Get(v8::String::NewSymbol("Coordinates"));
+        auto cf = module->Get(symbol_Coordinates);
         assert(cf->IsFunction());
 
         const osmium::Way& way = wrapped(args.This());
@@ -130,11 +131,11 @@ namespace node_osmium {
                 }
             }
             case 1: {
-                if (!args[0]->IsNumber()) {
+                if (!args[0]->IsUint32()) {
                     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())) {
+                uint32_t n = args[0]->ToUint32()->Value();
+                if (n < 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()) };
diff --git a/src/osm_way_wrap.hpp b/src/osm_way_wrap.hpp
index 229c4bb..1cf2724 100644
--- a/src/osm_way_wrap.hpp
+++ b/src/osm_way_wrap.hpp
@@ -1,12 +1,8 @@
 #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>
-#pragma GCC diagnostic pop
+// v8/node
+#include "include_v8.hpp"
 
 // osmium
 #include <osmium/osm/way.hpp>
@@ -15,6 +11,7 @@ namespace osmium {
 }
 
 // node-osmium
+#include "node_osmium.hpp"
 #include "osm_entity_wrap.hpp"
 #include "osm_object_wrap.hpp"
 #include "utils.hpp"
@@ -23,6 +20,10 @@ namespace node_osmium {
 
     class OSMWayWrap : public OSMObjectWrap {
 
+        static v8::Handle<v8::Value> get_type(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
+            return symbol_way;
+        }
+
         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);
@@ -45,8 +46,7 @@ namespace node_osmium {
 
     private:
 
-        ~OSMWayWrap() {
-        }
+        ~OSMWayWrap() = default;
 
     }; // class OSMWayWrap
 
diff --git a/src/reader_wrap.cpp b/src/reader_wrap.cpp
index cdf507e..a1e2986 100644
--- a/src/reader_wrap.cpp
+++ b/src/reader_wrap.cpp
@@ -8,6 +8,7 @@
 #include <node.h>
 
 // node-osmium
+#include "node_osmium.hpp"
 #include "buffer_wrap.hpp"
 #include "file_wrap.hpp"
 #include "handler.hpp"
@@ -26,11 +27,12 @@ namespace node_osmium {
         v8::HandleScope scope;
         constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(ReaderWrap::New));
         constructor->InstanceTemplate()->SetInternalFieldCount(1);
-        constructor->SetClassName(v8::String::NewSymbol("Reader"));
+        constructor->SetClassName(symbol_Reader);
         node::SetPrototypeMethod(constructor, "header", header);
         node::SetPrototypeMethod(constructor, "close", close);
         node::SetPrototypeMethod(constructor, "read", read);
-        target->Set(v8::String::NewSymbol("Reader"), constructor->GetFunction());
+        node::SetPrototypeMethod(constructor, "read_all", read_all);
+        target->Set(symbol_Reader, constructor->GetFunction());
     }
 
     v8::Handle<v8::Value> ReaderWrap::New(const v8::Arguments& args) {
@@ -50,22 +52,22 @@ namespace node_osmium {
                 read_which_entities = osmium::osm_entity_bits::nothing;
                 v8::Local<v8::Object> options = args[1]->ToObject();
 
-                v8::Local<v8::Value> want_nodes = options->Get(v8::String::NewSymbol("node"));
+                v8::Local<v8::Value> want_nodes = options->Get(symbol_node);
                 if (want_nodes->IsBoolean() && want_nodes->BooleanValue()) {
                     read_which_entities |= osmium::osm_entity_bits::node;
                 }
 
-                v8::Local<v8::Value> want_ways = options->Get(v8::String::NewSymbol("way"));
+                v8::Local<v8::Value> want_ways = options->Get(symbol_way);
                 if (want_ways->IsBoolean() && want_ways->BooleanValue()) {
                     read_which_entities |= osmium::osm_entity_bits::way;
                 }
 
-                v8::Local<v8::Value> want_relations = options->Get(v8::String::NewSymbol("relation"));
+                v8::Local<v8::Value> want_relations = options->Get(symbol_relation);
                 if (want_relations->IsBoolean() && want_relations->BooleanValue()) {
                     read_which_entities |= osmium::osm_entity_bits::relation;
                 }
 
-                v8::Local<v8::Value> want_changesets = options->Get(v8::String::NewSymbol("changeset"));
+                v8::Local<v8::Value> want_changesets = options->Get(symbol_changeset);
                 if (want_changesets->IsBoolean() && want_changesets->BooleanValue()) {
                     read_which_entities |= osmium::osm_entity_bits::changeset;
                 }
@@ -73,13 +75,13 @@ namespace node_osmium {
             }
             if (args[0]->IsString()) {
                 osmium::io::File file(*v8::String::Utf8Value(args[0]));
-                ReaderWrap* q = new ReaderWrap(file, read_which_entities);
-                q->Wrap(args.This());
+                ReaderWrap* reader_wrap = new ReaderWrap(file, read_which_entities);
+                reader_wrap->Wrap(args.This());
                 return args.This();
             } else if (args[0]->IsObject() && FileWrap::constructor->HasInstance(args[0]->ToObject())) {
                 v8::Local<v8::Object> file_obj = args[0]->ToObject();
-                ReaderWrap* q = new ReaderWrap(unwrap<FileWrap>(file_obj), read_which_entities);
-                q->Wrap(args.This());
+                ReaderWrap* reader_wrap = new ReaderWrap(unwrap<FileWrap>(file_obj), read_which_entities);
+                reader_wrap->Wrap(args.This());
                 return args.This();
             } else {
                 return ThrowException(v8::Exception::TypeError(v8::String::New("please provide a File object or string for the first argument when creating a Reader")));
@@ -94,7 +96,7 @@ namespace node_osmium {
         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()));
+        obj->Set(symbol_generator, v8::String::New(header.get("generator").c_str()));
 
         auto bounds_array = v8::Array::New(header.boxes().size());
 
@@ -103,7 +105,7 @@ namespace node_osmium {
             bounds_array->Set(i++, create_js_box(box));
         }
 
-        obj->Set(v8::String::NewSymbol("bounds"), bounds_array);
+        obj->Set(symbol_bounds, bounds_array);
         return scope.Close(obj);
     }
 
@@ -124,9 +126,27 @@ namespace node_osmium {
         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);
+                return scope.Close(new_external<BufferWrap>(std::move(buffer)));
+            }
+        } 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> ReaderWrap::read_all(const v8::Arguments& args) {
+        osmium::memory::Buffer buffer(1024*1024, osmium::memory::Buffer::auto_grow::yes);
+        v8::HandleScope scope;
+        try {
+            osmium::io::Reader& reader = unwrap<ReaderWrap>(args.This());
+            while (osmium::memory::Buffer read_buffer = reader.read()) {
+                buffer.add_buffer(read_buffer);
+                buffer.commit();
+            }
+            if (buffer) {
+                return scope.Close(new_external<BufferWrap>(std::move(buffer)));
             }
         } catch (const std::exception& e) {
             std::string msg("osmium error: ");
diff --git a/src/reader_wrap.hpp b/src/reader_wrap.hpp
index 084c7ef..1d313cd 100644
--- a/src/reader_wrap.hpp
+++ b/src/reader_wrap.hpp
@@ -1,19 +1,8 @@
 #ifndef READER_WRAP_HPP
 #define READER_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>
+// v8/node
+#include "include_v8.hpp"
 #include <node_object_wrap.h>
 
 // osmium
@@ -30,8 +19,9 @@ namespace node_osmium {
         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 v8::Handle<v8::Value> read_all(const v8::Arguments& args);
 
-        std::shared_ptr<osmium::io::Reader> m_this;
+        osmium::io::Reader m_this;
 
     public:
 
@@ -41,17 +31,16 @@ namespace node_osmium {
 
         ReaderWrap(const osmium::io::File& file, osmium::osm_entity_bits::type entities) :
             ObjectWrap(),
-            m_this(std::make_shared<osmium::io::Reader>(file, entities)) {
+            m_this(file, entities) {
         }
 
         osmium::io::Reader& get() {
-            return *m_this;
+            return m_this;
         }
 
     private:
 
-        ~ReaderWrap() {
-        }
+        ~ReaderWrap() = default;
 
     }; // class ReaderWrap
 
diff --git a/src/utils.cpp b/src/utils.cpp
index df40fdb..b4cde2e 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -2,6 +2,7 @@
 // osmium
 #include <osmium/osm/box.hpp>
 
+#include "node_osmium.hpp"
 #include "utils.hpp"
 
 namespace node_osmium {
@@ -15,9 +16,9 @@ namespace node_osmium {
             return scope.Close(v8::Undefined());
         }
 
-        auto cf = module->Get(v8::String::NewSymbol("Coordinates"));
+        auto cf = module->Get(symbol_Coordinates);
         assert(cf->IsFunction());
-        auto bf = module->Get(v8::String::NewSymbol("Box"));
+        auto bf = module->Get(symbol_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()) };
diff --git a/src/utils.hpp b/src/utils.hpp
index 331be88..a2bc4b1 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -1,14 +1,11 @@
 #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
+// c++
+#include <utility>
+
+// v8/node
+#include "include_v8.hpp"
 #include <node_object_wrap.h>
 
 // osmium
@@ -23,6 +20,13 @@ namespace node_osmium {
         return node::ObjectWrap::Unwrap<T>(object)->get();
     }
 
+    template<class T, class... Args>
+    v8::Local<v8::Object> new_external(Args&&... args) {
+        v8::HandleScope scope;
+        v8::Handle<v8::Value> ext = v8::External::New(new T(std::forward<Args>(args)...));
+        return scope.Close(T::constructor->GetFunction()->NewInstance(1, &ext));
+    }
+
     v8::Handle<v8::Value> create_js_box(const osmium::Box& box);
 
 } // namespace node_osmium
diff --git a/test/changesets.test.js b/test/changesets.test.js
index da4b366..31fd00b 100644
--- a/test/changesets.test.js
+++ b/test/changesets.test.js
@@ -8,6 +8,7 @@ describe('changesets', function() {
         var count = 0;
         handler.on('changeset', function(changeset) {
             if (count++ == 0) {
+                assert.equal(changeset.type, "changeset");
                 assert.equal(changeset.id, 15449957);
                 assert.equal(changeset.user, "Elbert");
                 assert.equal(changeset.uid, 1237205);
diff --git a/test/data/coordinates-problems.osm b/test/data/coordinates-problems.osm
new file mode 100644
index 0000000..cd61132
--- /dev/null
+++ b/test/data/coordinates-problems.osm
@@ -0,0 +1,6 @@
+<?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"/> <!-- lon/lat okay -->
+    <node id="2" version="1" timestamp="2014-01-01T00:00:00Z" uid="1" user="test" changeset="1"/> <!-- lon/lat missing -->
+    <node id="3" version="1" timestamp="2014-01-01T00:00:00Z" uid="1" user="test" changeset="1" lon="190" lat="190"/> <!-- outside valid range -->
+</osm>
diff --git a/test/osm_object_creation.test.js b/test/osm-object-creation.test.js
similarity index 100%
rename from test/osm_object_creation.test.js
rename to test/osm-object-creation.test.js
diff --git a/test/osm-objects.test.js b/test/osm-objects.test.js
index 7b10669..491be4d 100644
--- a/test/osm-objects.test.js
+++ b/test/osm-objects.test.js
@@ -8,6 +8,7 @@ describe('basic', function() {
         var count = 0;
         handler.on('node', function(node) {
             if (count++ == 0) {
+                assert.equal(node.type, "node");
                 assert.equal(node.id, 50031066);
                 assert.equal(node.visible, true);
                 assert.equal(node.version, 2);
@@ -36,6 +37,7 @@ describe('basic', function() {
         var count = 0;
         handler.on('way', function(way) {
             if (count++ == 0) {
+                assert.equal(way.type, "way");
                 assert.equal(way.id, 6091729);
                 assert.equal(way.visible, true);
                 assert.equal(way.version, 1);
@@ -57,6 +59,7 @@ describe('basic', function() {
         var count = 0;
         handler.on('relation', function(relation) {
             if (count++ == 0) {
+                assert.equal(relation.type, "relation");
                 assert.equal(relation.id, 237891);
                 assert.equal(relation.visible, true);
                 assert.equal(relation.version, 2);
@@ -174,4 +177,28 @@ describe('basic', function() {
         osmium.apply(reader, handler);
     });
 
+   it('should be able to handle missing and invalid coordinates', function() {
+        var handler = new osmium.Handler();
+        var count = 0;
+        handler.on('node', function(node) {
+            count++;
+            if (count == 1) {
+                assert.equal(node.coordinates.lon, 1.02);
+                assert.equal(node.coordinates.lat, 2.03);
+                assert.equal(node.coordinates.valid(), true);
+            } else if (count == 2) {
+                assert.equal(node.coordinates.lon, undefined);
+                assert.equal(node.coordinates.lat, undefined);
+                assert.equal(node.coordinates.valid(), false);
+            } else if (count == 3) {
+                assert.equal(node.coordinates.lon, 190);
+                assert.equal(node.coordinates.lat, 190);
+                assert.equal(node.coordinates.valid(), false);
+            }
+        });
+        var file = new osmium.File(__dirname + "/data/coordinates-problems.osm");
+        var reader = new osmium.Reader(file, {node: true});
+        osmium.apply(reader, handler);
+    });
+
 });
diff --git a/test/reader.test.js b/test/reader.test.js
index b71a8a3..fb4cb60 100644
--- a/test/reader.test.js
+++ b/test/reader.test.js
@@ -65,5 +65,23 @@ describe('reader', function() {
         osmium.apply(reader, handler);
     });
 
+    it('should read a whole file with Reader.read_all()', function(done) {
+        var file = new osmium.File(__dirname + "/data/winthrop.osm");
+        var reader = new osmium.Reader(file);
+        var buffer = reader.read_all();
+
+        var handler = new osmium.Handler();
+
+        var count = 0;
+        handler.on('node', function(node) {
+            if (count++ == 0) {
+                assert.equal(node.id, 50031066);
+                done();
+            }
+        });
+
+        osmium.apply(buffer, 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