[mapnik-vector-tile] 01/15: Imported Upstream version 0.8.1+dfsg

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Sat Sep 12 11:06:50 UTC 2015


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

sebastic pushed a commit to branch master
in repository mapnik-vector-tile.

commit 49f807b4efdb6975697456d92e0a30be2ee7e1bf
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Sep 11 22:54:58 2015 +0200

    Imported Upstream version 0.8.1+dfsg
---
 CHANGELOG.md                                     |  10 +
 Makefile                                         |  14 +-
 README.md                                        |   2 +-
 bootstrap.sh                                     |   2 +-
 examples/c++/tileinfo.cpp                        |   3 +-
 gyp/build.gyp                                    |  36 +-
 package.json                                     |   2 +-
 src/vector_tile_backend_pbf.ipp                  |   2 +
 src/vector_tile_datasource.ipp                   |   3 +-
 src/vector_tile_datasource_pbf.cpp               |   2 +
 src/vector_tile_datasource_pbf.hpp               |  59 +++
 src/vector_tile_datasource_pbf.ipp               | 413 +++++++++++++++++
 src/vector_tile_geometry_decoder.hpp             | 105 ++++-
 src/vector_tile_geometry_encoder.hpp             |  12 +-
 src/vector_tile_processor.ipp                    |  63 +--
 src/vector_tile_strategy.hpp                     |  88 ++++
 test/clipper_test.cpp                            | 143 ++++++
 test/data/natural_earth.tif                      | Bin 3299605 -> 0 bytes
 test/data/tile_with_extra_feature_field.pbf      |   2 +
 test/data/tile_with_extra_field.pbf              |   2 +
 test/data/tile_with_extra_layer_fields.pbf       |   2 +
 test/data/tile_with_invalid_layer_value_type.pbf |   2 +
 test/data/tile_with_unexpected_geomtype.pbf      |   3 +
 test/encoding_util.hpp                           |   3 +-
 test/geometry_encoding.cpp                       |  22 +-
 test/test_main.cpp                               |   9 +-
 test/test_utils.cpp                              |  16 +-
 test/vector_tile.cpp                             |  84 ++--
 test/vector_tile_pbf.cpp                         | 557 +++++++++++++++++++++++
 29 files changed, 1543 insertions(+), 118 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 72c1c12..da1f497 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
 # Changelog
 
+## 0.8.1
+
+ - Added `tile_datasource_pbf` - It should be used in places where you need to plug
+   in a `mapnik::datasource` to read from a binary encoded .pbf buffer. (@danpat #114)
+ - Updated bundled clipper to https://github.com/mapnik/clipper/commit/bfad32ec4b41783497d076c2ec44c7cbf4ebe56b
+ - Clipper is now patched to avoid abort on out of range coordinates (#111)
+ - Fixed handling of geometry collections (#106)
+ - Added mapnik vector tile strategy for transform
+ - Updated test cases
+
 ## 0.8.0
 
  - Now using `boost::geometry` to clip lines and `ClipperLib` to clip polygons
diff --git a/Makefile b/Makefile
index 71b405d..a518b5a 100755
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,22 @@
 MAPNIK_PLUGINDIR := $(shell mapnik-config --input-plugins)
 BUILDTYPE ?= Release
 
+CLIPPER_REVISION=bfad32e
+PBF_REVISION=1df6453
+GYP_REVISION=3464008
+
 all: libvtile
 
 ./deps/gyp:
-	git clone https://chromium.googlesource.com/external/gyp.git ./deps/gyp && cd ./deps/gyp && git checkout 3464008
+	git clone https://chromium.googlesource.com/external/gyp.git ./deps/gyp && cd ./deps/gyp && git checkout $(GYP_REVISION)
+
+./deps/pbf:
+	git clone https://github.com/mapbox/pbf.hpp.git ./deps/pbf && cd ./deps/pbf && git checkout $(PBF_REVISION)
+
+./deps/clipper:
+	git clone https://github.com/mapnik/clipper.git -b r493-mapnik ./deps/clipper && cd ./deps/clipper && git checkout $(CLIPPER_REVISION) && ./cpp/fix_members.sh
 
-build/Makefile: ./deps/gyp gyp/build.gyp test/*cpp
+build/Makefile: ./deps/gyp ./deps/clipper ./deps/pbf gyp/build.gyp test/*cpp
 	deps/gyp/gyp gyp/build.gyp --depth=. -DMAPNIK_PLUGINDIR=\"$(MAPNIK_PLUGINDIR)\" -Goutput_dir=. --generator-output=./build -f make
 
 libvtile: build/Makefile Makefile
diff --git a/README.md b/README.md
index f47e297..5a1c2b2 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Provides C++ headers that support rendering geodata into vector tiles and render
 
 ## Depends
 
- - mapnik-vector-tile 0.7.x depends on Mapnik v3.0.x (until 3.0.0 is released this means latest mapnik HEAD)
+ - mapnik-vector-tile >=0.7.x depends on Mapnik v3.0.x (until 3.0.0 is released this means latest mapnik HEAD)
  - mapnik-vector-tile 0.6.x and previous work with Mapnik v2.2.x or v2.3.x
  - You will need `libmapnik` and `mapnik-config` available
  - Protobuf: `libprotobuf` and `protoc`
diff --git a/bootstrap.sh b/bootstrap.sh
index d98f431..6e47b2f 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -22,7 +22,7 @@ function install() {
 }
 
 function install_mason_deps() {
-    install mapnik latest
+    install mapnik 3.0.0-rc3
     install protobuf 2.6.1
     install freetype 2.5.5
     install harfbuzz 0.9.40
diff --git a/examples/c++/tileinfo.cpp b/examples/c++/tileinfo.cpp
index 397ffbb..7a9ddca 100644
--- a/examples/c++/tileinfo.cpp
+++ b/examples/c++/tileinfo.cpp
@@ -104,7 +104,6 @@ int main(int argc, char** argv)
                 {
                     vector_tile::Tile_Feature const & f = layer.features(j);
                     total_repeated += f.geometry_size();
-                    eGeomType g_type = static_cast<eGeomType>(f.type());
                     int cmd = -1;
                     const int cmd_bits = 3;
                     unsigned length = 0;
@@ -117,7 +116,6 @@ int main(int argc, char** argv)
                             length = cmd_length >> cmd_bits;
                             if (length <= 0) num_empty++;
                             num_commands++;
-                            g_length = 0;
                         }
                         if (length > 0) {
                             length--;
@@ -138,6 +136,7 @@ int main(int argc, char** argv)
                             else if (cmd == (SEG_CLOSE & ((1 << cmd_bits) - 1)))
                             {
                                 if (g_length <= 2) degenerate++;
+                                g_length = 0;
                                 num_close++;
                             }
                             else
diff --git a/gyp/build.gyp b/gyp/build.gyp
index 3c145d3..42a4b71 100644
--- a/gyp/build.gyp
+++ b/gyp/build.gyp
@@ -3,7 +3,14 @@
       "common.gypi"
   ],
   'variables': {
-    'MAPNIK_PLUGINDIR%': ''
+    'MAPNIK_PLUGINDIR%': '',
+    'common_defines' : [
+        'MAPNIK_VECTOR_TILE_LIBRARY=1',
+        'CLIPPER_INTPOINT_IMPL=mapnik::geometry::point<cInt>',
+        'CLIPPER_PATH_IMPL=mapnik::geometry::line_string<cInt>',
+        'CLIPPER_PATHS_IMPL=mapnik::geometry::multi_line_string<cInt>',
+        'CLIPPER_IMPL_INCLUDE=<mapnik/geometry.hpp>'
+    ]
   },
   "targets": [
     {
@@ -34,7 +41,8 @@
         "<(SHARED_INTERMEDIATE_DIR)/vector_tile.pb.cc"
       ],
       'include_dirs': [
-        '<(SHARED_INTERMEDIATE_DIR)/'
+        '<(SHARED_INTERMEDIATE_DIR)/',
+        '../deps/pbf'
       ],
       'cflags_cc' : [
           '-D_THREAD_SAFE',
@@ -48,7 +56,8 @@
       },
       'direct_dependent_settings': {
         'include_dirs': [
-          '<(SHARED_INTERMEDIATE_DIR)/'
+          '<(SHARED_INTERMEDIATE_DIR)/',
+          '../deps/pbf'
         ],
         'libraries':[
           '-lprotobuf-lite'
@@ -69,14 +78,19 @@
       'hard_dependency': 1,
       "type": "static_library",
       "sources": [
-        "<!@(find ../src/ -name '*.cpp')"
+        "<!@(find ../src/ -name '*.cpp')",
+        "../deps/clipper/cpp/clipper.cpp"
       ],
       'defines' : [
-        'MAPNIK_VECTOR_TILE_LIBRARY=1'
+        "<@(common_defines)"
       ],
       'cflags_cc' : [
           '<!@(mapnik-config --cflags)'
       ],
+      'include_dirs': [
+        '../deps/pbf',
+        '../deps/clipper/cpp'
+      ],
       'xcode_settings': {
         'OTHER_CPLUSPLUSFLAGS':[
            '<!@(mapnik-config --cflags)'
@@ -84,10 +98,12 @@
       },
       'direct_dependent_settings': {
         'include_dirs': [
-          '<(SHARED_INTERMEDIATE_DIR)/'
+          '<(SHARED_INTERMEDIATE_DIR)/',
+          '../deps/pbf',
+          '../deps/clipper/cpp'
         ],
         'defines' : [
-          'MAPNIK_VECTOR_TILE_LIBRARY=1'
+          "<@(common_defines)"
         ],
         'cflags_cc' : [
             '<!@(mapnik-config --cflags)'
@@ -113,13 +129,15 @@
       'dependencies': [ 'mapnik_vector_tile_impl' ],
       "type": "executable",
       "defines": [
+        "<@(common_defines)",
         "MAPNIK_PLUGINDIR=<(MAPNIK_PLUGINDIR)"
       ], 
       "sources": [
         "<!@(find ../test/ -name '*.cpp')"
       ],
       "include_dirs": [
-        "../src"
+        "../src",
+        '../deps/pbf'
       ]
     },
     {
@@ -150,4 +168,4 @@
     }    
 
   ]
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index d7c65ca..515480b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "mapnik-vector-tile",
-    "version": "0.8.0",
+    "version": "0.8.1",
     "description": "Mapnik vector tile API",
     "main": "./package.json",
     "repository"   :  {
diff --git a/src/vector_tile_backend_pbf.ipp b/src/vector_tile_backend_pbf.ipp
index dbc07a0..8b1792d 100644
--- a/src/vector_tile_backend_pbf.ipp
+++ b/src/vector_tile_backend_pbf.ipp
@@ -59,6 +59,8 @@ backend_pbf::backend_pbf(vector_tile::Tile & _tile,
     : tile_(_tile),
       path_multiplier_(path_multiplier),
       current_layer_(NULL),
+      x_(0),
+      y_(0),
       current_feature_(NULL)
 {
 }
diff --git a/src/vector_tile_datasource.ipp b/src/vector_tile_datasource.ipp
index 1176b71..3ee0dc4 100644
--- a/src/vector_tile_datasource.ipp
+++ b/src/vector_tile_datasource.ipp
@@ -190,7 +190,8 @@ namespace mapnik { namespace vector_tile_impl {
                 {
                     continue;
                 }
-                mapnik::geometry::geometry<double> geom = decode_geometry(f,tile_x_,tile_y_,scale_,-1*scale_);
+                mapnik::vector_tile_impl::Geometry geoms(f,tile_x_, tile_y_, scale_, -1*scale_);
+                mapnik::geometry::geometry<double> geom = decode_geometry(geoms, f.type());
                 if (geom.is<mapnik::geometry::geometry_empty>())
                 {
                     continue;
diff --git a/src/vector_tile_datasource_pbf.cpp b/src/vector_tile_datasource_pbf.cpp
new file mode 100644
index 0000000..9a13992
--- /dev/null
+++ b/src/vector_tile_datasource_pbf.cpp
@@ -0,0 +1,2 @@
+#include "vector_tile_datasource_pbf.hpp"
+#include "vector_tile_datasource_pbf.ipp"
diff --git a/src/vector_tile_datasource_pbf.hpp b/src/vector_tile_datasource_pbf.hpp
new file mode 100644
index 0000000..5e778df
--- /dev/null
+++ b/src/vector_tile_datasource_pbf.hpp
@@ -0,0 +1,59 @@
+#ifndef __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__
+#define __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__
+
+#include <mapnik/datasource.hpp>
+#include <mapnik/box2d.hpp>
+#include "pbf_reader.hpp"
+
+namespace mapnik { namespace vector_tile_impl {
+
+    // TODO: consider using mapnik::value here instead
+    using pbf_attr_value_type = mapnik::util::variant<std::string, float, double, int64_t, uint64_t, bool>;
+    using layer_pbf_attr_type = std::vector<pbf_attr_value_type>;
+
+    class tile_datasource_pbf : public datasource
+    {
+    public:
+        tile_datasource_pbf(mapbox::util::pbf const& layer,
+                        unsigned x,
+                        unsigned y,
+                        unsigned z,
+                        unsigned tile_size);
+        virtual ~tile_datasource_pbf();
+        datasource::datasource_t type() const;
+        featureset_ptr features(query const& q) const;
+        featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const;
+        void set_envelope(box2d<double> const& bbox);
+        box2d<double> get_tile_extent() const;
+        box2d<double> envelope() const;
+        boost::optional<datasource_geometry_t> get_geometry_type() const;
+        layer_descriptor get_descriptor() const;
+        std::string const& get_name() { return name_; }
+    private:
+        mutable mapnik::layer_descriptor desc_;
+        mutable bool attributes_added_;
+        mapbox::util::pbf layer_;
+        unsigned x_;
+        unsigned y_;
+        unsigned z_;
+        unsigned tile_size_;
+        mutable bool extent_initialized_;
+        mutable mapnik::box2d<double> extent_;
+        double tile_x_;
+        double tile_y_;
+        double scale_;
+        uint32_t layer_extent_;
+
+        std::string name_;
+        std::vector<mapbox::util::pbf> features_;
+        std::vector<std::string> layer_keys_;
+        layer_pbf_attr_type layer_values_;
+    };
+
+}} // end ns
+
+#if !defined(MAPNIK_VECTOR_TILE_LIBRARY)
+#include "vector_tile_datasource_pbf.ipp"
+#endif
+
+#endif // __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__
diff --git a/src/vector_tile_datasource_pbf.ipp b/src/vector_tile_datasource_pbf.ipp
new file mode 100644
index 0000000..8976b4b
--- /dev/null
+++ b/src/vector_tile_datasource_pbf.ipp
@@ -0,0 +1,413 @@
+#include "vector_tile_projection.hpp"
+#include "vector_tile_geometry_decoder.hpp"
+
+#include <mapnik/box2d.hpp>
+#include <mapnik/coord.hpp>
+#include <mapnik/feature_layer_desc.hpp>
+#include <mapnik/geometry.hpp>
+#include <mapnik/params.hpp>
+#include <mapnik/query.hpp>
+#include <mapnik/unicode.hpp>
+#include <mapnik/version.hpp>
+#include <mapnik/value_types.hpp>
+#include <mapnik/well_known_srs.hpp>
+#include <mapnik/version.hpp>
+#include <mapnik/vertex.hpp>
+#include <mapnik/datasource.hpp>
+#include <mapnik/feature.hpp>
+#include <mapnik/feature_factory.hpp>
+#include <mapnik/geom_util.hpp>
+#include <mapnik/image.hpp>
+#include <mapnik/image_reader.hpp>
+#include <mapnik/raster.hpp>
+#include <mapnik/view_transform.hpp>
+#include <mapnik/util/variant.hpp>
+
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include <boost/optional.hpp>
+#include <unicode/unistr.h>
+
+namespace mapnik { namespace vector_tile_impl {
+
+    template <typename Filter>
+    class tile_featureset_pbf : public Featureset
+    {
+    public:
+        tile_featureset_pbf(Filter const& filter,
+                        mapnik::box2d<double> const& tile_extent,
+                        mapnik::box2d<double> const& unbuffered_query,
+                        std::set<std::string> const& attribute_names,
+                        std::vector<mapbox::util::pbf> const& features,
+                        double tile_x,
+                        double tile_y,
+                        double scale,
+                        std::vector<std::string> const& layer_keys,
+                        layer_pbf_attr_type const& layer_values)
+            : filter_(filter),
+              tile_extent_(tile_extent),
+              unbuffered_query_(unbuffered_query),
+              features_(features),
+              layer_keys_(layer_keys),
+              layer_values_(layer_values),
+              tile_x_(tile_x),
+              tile_y_(tile_y),
+              scale_(scale),
+              itr_(0),
+              tr_("utf-8"),
+              ctx_(std::make_shared<mapnik::context_type>())
+        {
+            std::set<std::string>::const_iterator pos = attribute_names.begin();
+            std::set<std::string>::const_iterator end = attribute_names.end();
+            for ( ;pos !=end; ++pos)
+            {
+                for (auto const& key : layer_keys_)
+                {
+                    if (key == *pos)
+                    {
+                        ctx_->push(*pos);
+                        break;
+                    }
+                }
+            }
+        }
+
+        virtual ~tile_featureset_pbf() {}
+
+        feature_ptr next()
+        {
+            while ( itr_ < features_.size() )
+            {
+                mapbox::util::pbf f = features_.at(itr_);
+                // TODO: auto-increment feature id counter here
+                mapnik::feature_ptr feature = mapnik::feature_factory::create(ctx_,itr_);
+                pbf_attr_value_type val;
+
+                ++itr_;
+                int tagcount=0;
+                uint32_t key_idx, val_idx;
+                int32_t geometry_type = 0; // vector_tile::Tile_GeomType_UNKNOWN
+                while (f.next())
+                {
+                    switch(f.tag())
+                    {
+                        case 1:
+                            feature->set_id(f.get_uint64());
+                            break;
+                        case 2:
+                            {
+                                auto tag_iterator = f.packed_uint32();
+
+                                for (auto _i = tag_iterator.first; _i != tag_iterator.second; ++_i)
+                                {
+                                    if (tagcount % 2 == 0)
+                                    {
+                                        key_idx = *_i;
+                                        ++tagcount;
+                                    }
+                                    else
+                                    {
+                                        val_idx = *_i;
+                                        val = layer_values_.at(val_idx);
+                                        std::string name = layer_keys_.at(key_idx);
+                                        if (feature->has_key(name))
+                                        {
+                                            if (val.is<std::string>())
+                                            {
+                                                feature->put(name, tr_.transcode(val.get<std::string>().data(), val.get<std::string>().length()));
+                                            }
+                                            else if (val.is<bool>())
+                                            {
+                                                feature->put(name, static_cast<mapnik::value_bool>(val.get<bool>()));
+                                            }
+                                            else if (val.is<int64_t>())
+                                            {
+                                                feature->put(name, static_cast<mapnik::value_integer>(val.get<int64_t>()));
+                                            }
+                                            else if (val.is<uint64_t>())
+                                            {
+                                                feature->put(name, static_cast<mapnik::value_integer>(val.get<uint64_t>()));
+                                            }
+                                            else if (val.is<double>())
+                                            {
+                                                feature->put(name, static_cast<mapnik::value_double>(val.get<double>()));
+                                            }
+                                            else if (val.is<float>())
+                                            {
+                                                feature->put(name, static_cast<mapnik::value_double>(val.get<float>()));
+                                            } else {
+                                                throw std::runtime_error("unknown attribute type while reading feature");
+                                            }
+                                            ++tagcount;
+                                        }
+                                    }
+                                }
+                            }
+                            break;
+                        case 3:
+                            geometry_type = f.get_enum();
+                            switch (geometry_type)
+                            {
+                              case 1: //vector_tile::Tile_GeomType_POINT
+                              case 2: // vector_tile::Tile_GeomType_LINESTRING
+                              case 3: // vector_tile::Tile_GeomType_POLYGON
+                                break;
+                              default:  // vector_tile::Tile_GeomType_UNKNOWN or any other value
+                                throw std::runtime_error("unknown geometry type " + std::to_string(geometry_type) + " in feature");
+                            }
+                            break;
+                        case 5:
+                            {
+                            std::string const& image_buffer = f.get_bytes();
+                            std::unique_ptr<mapnik::image_reader> reader(mapnik::get_image_reader(image_buffer.data(),image_buffer.size()));
+                            if (reader.get())
+                            {
+                                int image_width = reader->width();
+                                int image_height = reader->height();
+                                if (image_width > 0 && image_height > 0)
+                                {
+                                    mapnik::view_transform t(image_width, image_height, tile_extent_, 0, 0);
+                                    box2d<double> intersect = tile_extent_.intersect(unbuffered_query_);
+                                    box2d<double> ext = t.forward(intersect);
+                                    if (ext.width() > 0.5 && ext.height() > 0.5 )
+                                    {
+                                        // select minimum raster containing whole ext
+                                        int x_off = static_cast<int>(std::floor(ext.minx() +.5));
+                                        int y_off = static_cast<int>(std::floor(ext.miny() +.5));
+                                        int end_x = static_cast<int>(std::floor(ext.maxx() +.5));
+                                        int end_y = static_cast<int>(std::floor(ext.maxy() +.5));
+
+                                        // clip to available data
+                                        if (x_off < 0)
+                                            x_off = 0;
+                                        if (y_off < 0)
+                                            y_off = 0;
+                                        if (end_x > image_width)
+                                            end_x = image_width;
+                                        if (end_y > image_height)
+                                            end_y = image_height;
+                                        int width = end_x - x_off;
+                                        int height = end_y - y_off;
+                                        box2d<double> feature_raster_extent(x_off,
+                                                                            y_off,
+                                                                            x_off + width,
+                                                                            y_off + height);
+                                        intersect = t.backward(feature_raster_extent);
+                                        double filter_factor = 1.0;
+                                        mapnik::image_any data = reader->read(x_off, y_off, width, height);
+                                        mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(intersect,
+                                                                      data,
+                                                                      filter_factor
+                                                                      );
+                                        feature->set_raster(raster);
+                                        return feature;
+                                    }
+                                }
+                            }
+                          }
+                            break;
+                        case 4:
+                            {
+                                auto geom_itr = f.packed_uint32();
+                                mapnik::vector_tile_impl::GeometryPBF geoms(geom_itr, tile_x_,tile_y_,scale_,-1*scale_);
+                                mapnik::geometry::geometry<double> geom = decode_geometry(geoms, geometry_type);
+                                if (geom.is<mapnik::geometry::geometry_empty>())
+                                {
+                                    continue;
+                                }
+                                mapnik::box2d<double> envelope = mapnik::geometry::envelope(geom);
+                                if (!filter_.pass(envelope))
+                                {
+                                    continue;
+                                }
+                                feature->set_geometry(std::move(geom));
+                                return feature;
+                            }
+                            break;
+                        default:
+                            // NOTE:  The vector_tile.proto file technically allows for extension fields
+                            //        of values 16 to max here.  Technically, we should just skip() those
+                            //        fields.
+                            //        However, if we're fed a corrupt file (or random data), we don't
+                            //        want to just blindly follow the bytes, so we have made the decision
+                            //        to abort cleanly, rather than doing GIGO.
+                            throw std::runtime_error("unknown field type " + std::to_string(f.tag()) +" in feature");
+
+                    }
+                }
+            }
+            return feature_ptr();
+        }
+
+    private:
+        Filter filter_;
+        mapnik::box2d<double> tile_extent_;
+        mapnik::box2d<double> unbuffered_query_;
+        std::vector<mapbox::util::pbf> const& features_;
+        std::vector<std::string> const& layer_keys_;
+        layer_pbf_attr_type const& layer_values_;
+
+        double tile_x_;
+        double tile_y_;
+        double scale_;
+        unsigned itr_;
+        mapnik::transcoder tr_;
+        mapnik::context_ptr ctx_;
+
+    };
+
+    // tile_datasource impl
+    tile_datasource_pbf::tile_datasource_pbf(mapbox::util::pbf const& layer,
+                                     unsigned x,
+                                     unsigned y,
+                                     unsigned z,
+                                     unsigned tile_size)
+        : datasource(parameters()),
+          desc_("in-memory PBF encoded datasource","utf-8"),
+          attributes_added_(false),
+          layer_(layer),
+          x_(x),
+          y_(y),
+          z_(z),
+          tile_size_(tile_size),
+          extent_initialized_(false),
+          tile_x_(0.0),
+          tile_y_(0.0),
+          scale_(0.0),
+          layer_extent_(0)
+    {
+        double resolution = mapnik::EARTH_CIRCUMFERENCE/(1 << z_);
+        tile_x_ = -0.5 * mapnik::EARTH_CIRCUMFERENCE + x_ * resolution;
+        tile_y_ =  0.5 * mapnik::EARTH_CIRCUMFERENCE - y_ * resolution;
+
+        mapbox::util::pbf val_msg;
+
+        while (layer_.next())
+        {
+            switch(layer_.tag())
+            {
+                case 1:
+                    name_ = layer_.get_string();
+                    break;
+                case 2:
+                    features_.push_back(layer_.get_message());
+                    break;
+                case 3:
+                    layer_keys_.push_back(layer_.get_string());
+                    break;
+                case 4:
+                    val_msg = layer_.get_message();
+                    while (val_msg.next())
+                    {
+                        switch(val_msg.tag()) {
+                            case 1:
+                                layer_values_.push_back(val_msg.get_string());
+                                break;
+                            case 2:
+                                layer_values_.push_back(val_msg.get_float());
+                                break;
+                            case 3:
+                                layer_values_.push_back(val_msg.get_double());
+                                break;
+                            case 4:
+                                layer_values_.push_back(val_msg.get_int64());
+                                break;
+                            case 5:
+                                layer_values_.push_back(val_msg.get_uint64());
+                                break;
+                            case 6:
+                                layer_values_.push_back(val_msg.get_sint64());
+                                break;
+                            case 7:
+                                layer_values_.push_back(val_msg.get_bool());
+                                break;
+                            default:
+                                throw std::runtime_error("unknown Value type " + std::to_string(layer_.tag()) + " in layer.values");
+                        }
+                    }
+                    break;
+                case 5:
+                    layer_extent_ = layer_.get_uint32();
+                    break;
+                case 15:
+                    layer_.skip();
+                    break;
+                default:
+                    throw std::runtime_error("unknown field type " + std::to_string(layer_.tag()) + " in layer");
+            }
+        }
+        scale_ = (static_cast<double>(layer_extent_) / tile_size_) * tile_size_/resolution;
+    }
+
+    tile_datasource_pbf::~tile_datasource_pbf() {}
+
+    datasource::datasource_t tile_datasource_pbf::type() const
+    {
+        return datasource::Vector;
+    }
+
+    featureset_ptr tile_datasource_pbf::features(query const& q) const
+    {
+        mapnik::filter_in_box filter(q.get_bbox());
+        return std::make_shared<tile_featureset_pbf<mapnik::filter_in_box> >
+            (filter, get_tile_extent(), q.get_unbuffered_bbox(), q.property_names(), features_, tile_x_, tile_y_, scale_, layer_keys_, layer_values_);
+    }
+
+    featureset_ptr tile_datasource_pbf::features_at_point(coord2d const& pt, double tol) const
+    {
+        mapnik::filter_at_point filter(pt,tol);
+        std::set<std::string> names;
+        for (auto const& key : layer_keys_)
+        {
+            names.insert(key);
+        }
+        return std::make_shared<tile_featureset_pbf<filter_at_point> >
+            (filter, get_tile_extent(), get_tile_extent(), names, features_, tile_x_, tile_y_, scale_, layer_keys_, layer_values_);
+    }
+
+    void tile_datasource_pbf::set_envelope(box2d<double> const& bbox)
+    {
+        extent_initialized_ = true;
+        extent_ = bbox;
+    }
+
+    box2d<double> tile_datasource_pbf::get_tile_extent() const
+    {
+        spherical_mercator merc(tile_size_);
+        double minx,miny,maxx,maxy;
+        merc.xyz(x_,y_,z_,minx,miny,maxx,maxy);
+        return box2d<double>(minx,miny,maxx,maxy);
+    }
+
+    box2d<double> tile_datasource_pbf::envelope() const
+    {
+        if (!extent_initialized_)
+        {
+            extent_ = get_tile_extent();
+            extent_initialized_ = true;
+        }
+        return extent_;
+    }
+
+    boost::optional<mapnik::datasource_geometry_t> tile_datasource_pbf::get_geometry_type() const
+    {
+        return mapnik::datasource_geometry_t::Collection;
+    }
+
+    layer_descriptor tile_datasource_pbf::get_descriptor() const
+    {
+        if (!attributes_added_)
+        {
+            for (auto const& key : layer_keys_)
+            {
+                // Object type here because we don't know the precise value until features are unpacked
+                desc_.add_descriptor(attribute_descriptor(key, Object));
+            }
+            attributes_added_ = true;
+        }
+        return desc_;
+    }
+
+    }} // end ns
diff --git a/src/vector_tile_geometry_decoder.hpp b/src/vector_tile_geometry_decoder.hpp
index 938d2e4..c0f1510 100644
--- a/src/vector_tile_geometry_decoder.hpp
+++ b/src/vector_tile_geometry_decoder.hpp
@@ -2,6 +2,7 @@
 #define __MAPNIK_VECTOR_TILE_GEOMETRY_DECODER_H__
 
 #include "vector_tile.pb.h"
+#include "pbf_reader.hpp"
 
 #include <mapnik/util/is_clockwise.hpp>
 
@@ -10,6 +11,8 @@
 
 namespace mapnik { namespace vector_tile_impl {
 
+// NOTE: this object is for one-time use.  Once you've progressed to the end
+//       by calling next(), to re-iterate, you must construct a new object
 class Geometry {
 
 public:
@@ -38,6 +41,34 @@ private:
     double ox, oy;
 };
 
+// NOTE: this object is for one-time use.  Once you've progressed to the end
+//       by calling next(), to re-iterate, you must construct a new object
+class GeometryPBF {
+
+public:
+    inline explicit GeometryPBF(std::pair< mapbox::util::pbf::const_uint32_iterator, mapbox::util::pbf::const_uint32_iterator > const& geo_iterator,
+                             double tile_x, double tile_y,
+                             double scale_x, double scale_y);
+
+    enum command : uint8_t {
+        end = 0,
+            move_to = 1,
+            line_to = 2,
+            close = 7
+            };
+
+    inline command next(double& rx, double& ry);
+
+private:
+    std::pair< mapbox::util::pbf::const_uint32_iterator, mapbox::util::pbf::const_uint32_iterator > geo_iterator_;
+    double scale_x_;
+    double scale_y_;
+    uint8_t cmd;
+    uint32_t length;
+    double x, y;
+    double ox, oy;
+};
+
 Geometry::Geometry(vector_tile::Tile_Feature const& f,
                    double tile_x, double tile_y,
                    double scale_x, double scale_y)
@@ -90,22 +121,70 @@ Geometry::command Geometry::next(double& rx, double& ry) {
     }
 }
 
-inline mapnik::geometry::geometry<double> decode_geometry(vector_tile::Tile_Feature const& f,
-                                                  double tile_x, double tile_y,
-                                                  double scale_x, double scale_y,
+GeometryPBF::GeometryPBF(std::pair<mapbox::util::pbf::const_uint32_iterator, mapbox::util::pbf::const_uint32_iterator > const& geo_iterator,
+                   double tile_x, double tile_y,
+                   double scale_x, double scale_y)
+    : geo_iterator_(geo_iterator),
+      scale_x_(scale_x),
+      scale_y_(scale_y),
+      cmd(1),
+      length(0),
+      x(tile_x), y(tile_y),
+      ox(0), oy(0) {}
+
+GeometryPBF::command GeometryPBF::next(double& rx, double& ry) {
+    if (geo_iterator_.first != geo_iterator_.second) {
+        if (length == 0) {
+            uint32_t cmd_length = static_cast<uint32_t>(*geo_iterator_.first++);
+            cmd = cmd_length & 0x7;
+            length = cmd_length >> 3;
+        }
+
+        --length;
+
+        if (cmd == move_to || cmd == line_to) {
+            int32_t dx = *geo_iterator_.first++;
+            int32_t dy = *geo_iterator_.first++;
+            dx = ((dx >> 1) ^ (-(dx & 1)));
+            dy = ((dy >> 1) ^ (-(dy & 1)));
+            x += (static_cast<double>(dx) / scale_x_);
+            y += (static_cast<double>(dy) / scale_y_);
+            rx = x;
+            ry = y;
+            if (cmd == move_to) {
+                ox = x;
+                oy = y;
+                return move_to;
+            } else {
+                return line_to;
+            }
+        } else if (cmd == close) {
+            rx = ox;
+            ry = oy;
+            return close;
+        } else {
+            fprintf(stderr, "unknown command: %d\n", cmd);
+            return end;
+        }
+    } else {
+        return end;
+    }
+}
+
+template <typename T>
+inline mapnik::geometry::geometry<double> decode_geometry(T & geoms, int32_t geom_type,
                                                   bool treat_all_rings_as_exterior=false)
 {
-    Geometry::command cmd;
-    Geometry geoms(f,tile_x,tile_y,scale_x,scale_y);
+    typename T::command cmd;
     double x1, y1;
     mapnik::geometry::geometry<double> geom; // output geometry
 
-    switch (f.type())
+    switch (geom_type)
     {
     case vector_tile::Tile_GeomType_POINT:
     {
         mapnik::geometry::multi_point<double> mp;
-        while ((cmd = geoms.next(x1, y1)) != Geometry::end)
+        while ((cmd = geoms.next(x1, y1)) != T::end)
         {
             mp.emplace_back(mapnik::geometry::point<double>(x1,y1));
         }
@@ -129,9 +208,9 @@ inline mapnik::geometry::geometry<double> decode_geometry(vector_tile::Tile_Feat
         mapnik::geometry::multi_line_string<double> multi_line;
         multi_line.emplace_back();
         bool first = true;
-        while ((cmd = geoms.next(x1, y1)) != Geometry::end)
+        while ((cmd = geoms.next(x1, y1)) != T::end)
         {
-            if (cmd == Geometry::move_to)
+            if (cmd == T::move_to)
             {
                 if (first)
                 {
@@ -173,9 +252,9 @@ inline mapnik::geometry::geometry<double> decode_geometry(vector_tile::Tile_Feat
         rings.emplace_back();
         double x2,y2;
         bool first = true;
-        while ((cmd = geoms.next(x1, y1)) != Geometry::end)
+        while ((cmd = geoms.next(x1, y1)) != T::end)
         {
-            if (cmd == Geometry::move_to)
+            if (cmd == T::move_to)
             {
                 x2 = x1;
                 y2 = y1;
@@ -188,7 +267,7 @@ inline mapnik::geometry::geometry<double> decode_geometry(vector_tile::Tile_Feat
                     rings.emplace_back();
                 }
             }
-            else if (cmd == Geometry::close)
+            else if (cmd == T::close)
             {
                 rings.back().add_coord(x2,y2);
                 continue;
@@ -302,7 +381,7 @@ inline mapnik::geometry::geometry<double> decode_geometry(vector_tile::Tile_Feat
         else if (num_poly == 1)
         {
             auto itr = std::make_move_iterator(multi_poly.begin());
-            geom = std::move(mapnik::geometry::polygon<double>(std::move(*itr)));
+            geom = mapnik::geometry::polygon<double>(std::move(*itr));
             return geom;
         }
         else
diff --git a/src/vector_tile_geometry_encoder.hpp b/src/vector_tile_geometry_encoder.hpp
index f14f543..dd95f8e 100644
--- a/src/vector_tile_geometry_encoder.hpp
+++ b/src/vector_tile_geometry_encoder.hpp
@@ -21,8 +21,8 @@ inline unsigned encode_geometry(mapnik::geometry::point<std::int64_t> const& pt,
     int32_t dx = pt.x - start_x;
     int32_t dy = pt.y - start_y;
     // Manual zigzag encoding.
-    current_feature.add_geometry((dx << 1) ^ (dx >> 31));
-    current_feature.add_geometry((dy << 1) ^ (dy >> 31));
+    current_feature.add_geometry((static_cast<unsigned>(dx) << 1) ^ (dx >> 31));
+    current_feature.add_geometry((static_cast<unsigned>(dy) << 1) ^ (dy >> 31));
     start_x = pt.x;
     start_y = pt.y;
     return 1;
@@ -57,8 +57,8 @@ inline unsigned encode_geometry(mapnik::geometry::line_string<std::int64_t> cons
         int32_t dx = pt.x - start_x;
         int32_t dy = pt.y - start_y;
         // Manual zigzag encoding.
-        current_feature.add_geometry((dx << 1) ^ (dx >> 31));
-        current_feature.add_geometry((dy << 1) ^ (dy >> 31));
+        current_feature.add_geometry((static_cast<unsigned>(dx) << 1) ^ (dx >> 31));
+        current_feature.add_geometry((static_cast<unsigned>(dy) << 1) ^ (dy >> 31));
         start_x = pt.x;
         start_y = pt.y;
     }
@@ -109,8 +109,8 @@ inline unsigned encode_geometry(mapnik::geometry::linear_ring<std::int64_t> cons
         int32_t dx = pt.x - start_x;
         int32_t dy = pt.y - start_y;
         // Manual zigzag encoding.
-        current_feature.add_geometry((dx << 1) ^ (dx >> 31));
-        current_feature.add_geometry((dy << 1) ^ (dy >> 31));
+        current_feature.add_geometry((static_cast<unsigned>(dx) << 1) ^ (dx >> 31));
+        current_feature.add_geometry((static_cast<unsigned>(dy) << 1) ^ (dy >> 31));
         start_x = pt.x;
         start_y = pt.y;
         ++count;
diff --git a/src/vector_tile_processor.ipp b/src/vector_tile_processor.ipp
index 6d639a3..8851fd3 100644
--- a/src/vector_tile_processor.ipp
+++ b/src/vector_tile_processor.ipp
@@ -8,7 +8,6 @@
 #include <mapnik/datasource.hpp>
 #include <mapnik/projection.hpp>
 #include <mapnik/proj_transform.hpp>
-#include <mapnik/proj_strategy.hpp>
 #include <mapnik/scale_denominator.hpp>
 #include <mapnik/attribute_descriptor.hpp>
 #include <mapnik/feature_layer_desc.hpp>
@@ -22,13 +21,11 @@
 #include <mapnik/image_scaling.hpp>
 #include <mapnik/image_compositing.hpp>
 #include <mapnik/view_transform.hpp>
-#include <mapnik/view_strategy.hpp>
 #include <mapnik/util/noncopyable.hpp>
 #include <mapnik/transform_path_adapter.hpp>
 #include <mapnik/geometry_is_empty.hpp>
 #include <mapnik/geometry_envelope.hpp>
 #include <mapnik/geometry_adapters.hpp>
-#include <mapnik/geometry_strategy.hpp>
 #include <mapnik/geometry_transform.hpp>
 
 // agg
@@ -50,6 +47,7 @@
 #include <string>
 #include <stdexcept>
 
+#include "vector_tile_strategy.hpp"
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-parameter"
@@ -796,29 +794,12 @@ void processor<T>::apply_to_layer(mapnik::layer const& lay,
                 feature = features->next();
                 continue;
             }
-            if (geom.is<mapnik::geometry::geometry_collection<double> >())
+            if (handle_geometry(*feature,
+                                geom,
+                                prj_trans,
+                                buffered_query_ext) > 0)
             {
-                auto const& collection = mapnik::util::get<mapnik::geometry::geometry_collection<double> >(geom);
-                for (auto const& part : collection)
-                {
-                    if (handle_geometry(*feature,
-                                        part,
-                                        prj_trans,
-                                        buffered_query_ext) > 0)
-                    {
-                        painted_ = true;
-                    }
-                }
-            }
-            else
-            {
-                if (handle_geometry(*feature,
-                                    geom,
-                                    prj_trans,
-                                    buffered_query_ext) > 0)
-                {
-                    painted_ = true;
-                }
+                painted_ = true;
             }
             feature = features->next();
         }
@@ -885,10 +866,14 @@ struct encoder_visitor {
         return 0;
     }
 
-    unsigned operator() (mapnik::geometry::geometry_collection<std::int64_t> const& geom)
+    unsigned operator() (mapnik::geometry::geometry_collection<std::int64_t> & geom)
     {
-        //throw std::runtime_error("geometry_collections not supported in encoder_visitor");
-        return 0;
+        unsigned count = 0;
+        for (auto & g : geom)
+        {
+            count += mapnik::util::apply_visitor((*this), g);
+        }
+        return count;
     }
 
     unsigned operator() (mapnik::geometry::point<std::int64_t> const& geom)
@@ -1229,8 +1214,12 @@ struct simplify_visitor {
 
     unsigned operator() (mapnik::geometry::geometry_collection<std::int64_t> const& geom)
     {
-        //throw std::runtime_error("geometry_collection not supported in simplify_visitor");
-        return 0;
+        unsigned count = 0;
+        for (auto const& g : geom)
+        {
+            count += mapnik::util::apply_visitor((*this), g);
+        }
+        return count;
     }
 
     unsigned operator() (mapnik::geometry::geometry_empty const& geom)
@@ -1249,19 +1238,13 @@ unsigned processor<T>::handle_geometry(mapnik::feature_impl const& feature,
                                        mapnik::proj_transform const& prj_trans,
                                        mapnik::box2d<double> const& buffered_query_ext)
 {
-    mapnik::proj_backward_strategy proj_strat(prj_trans);
-    mapnik::view_strategy view_strat(t_);
-    mapnik::geometry::scale_strategy scale_strat(backend_.get_path_multiplier(), 0.5);
-    using sg_type = mapnik::geometry::strategy_group<mapnik::proj_backward_strategy, 
-                                                     mapnik::view_strategy, 
-                                                     mapnik::geometry::scale_strategy >;
-    sg_type sg(proj_strat, view_strat, scale_strat);
+    vector_tile_strategy vs(prj_trans, t_, backend_.get_path_multiplier());
     mapnik::geometry::point<double> p1_min(buffered_query_ext.minx(), buffered_query_ext.miny());
     mapnik::geometry::point<double> p1_max(buffered_query_ext.maxx(), buffered_query_ext.maxy());
-    mapnik::geometry::point<std::int64_t> p2_min = mapnik::geometry::transform<std::int64_t>(p1_min, sg);
-    mapnik::geometry::point<std::int64_t> p2_max = mapnik::geometry::transform<std::int64_t>(p1_max, sg);
+    mapnik::geometry::point<std::int64_t> p2_min = mapnik::geometry::transform<std::int64_t>(p1_min, vs);
+    mapnik::geometry::point<std::int64_t> p2_max = mapnik::geometry::transform<std::int64_t>(p1_max, vs);
     box2d<int> bbox(p2_min.x, p2_min.y, p2_max.x, p2_max.y);
-    mapnik::geometry::geometry<std::int64_t> new_geom = mapnik::geometry::transform<std::int64_t>(geom, sg);
+    mapnik::geometry::geometry<std::int64_t> new_geom = mapnik::geometry::transform<std::int64_t>(geom, vs);
     encoder_visitor<T> encoder(backend_,feature,bbox, area_threshold_);
     if (simplify_distance_ > 0)
     {
diff --git a/src/vector_tile_strategy.hpp b/src/vector_tile_strategy.hpp
new file mode 100644
index 0000000..1f8c045
--- /dev/null
+++ b/src/vector_tile_strategy.hpp
@@ -0,0 +1,88 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2014 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#ifndef MAPNIK_VECTOR_TILE_STRATEGY_HPP
+#define MAPNIK_VECTOR_TILE_STRATEGY_HPP
+
+// mapnik
+#include <mapnik/config.hpp>
+#include <mapnik/util/noncopyable.hpp>
+#include <mapnik/proj_transform.hpp>
+#include <mapnik/view_transform.hpp>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wunused-local-typedef"
+#include <boost/geometry/core/coordinate_type.hpp>
+#include <boost/geometry/core/access.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+#pragma GCC diagnostic pop
+
+
+namespace mapnik {
+
+namespace vector_tile_impl {
+
+struct vector_tile_strategy
+{
+    vector_tile_strategy(proj_transform const& prj_trans,
+                         view_transform const& tr,
+                         double scaling)
+        : prj_trans_(prj_trans),
+          tr_(tr),
+          scaling_(scaling) {}
+
+    template <typename P1, typename P2>
+    inline bool apply(P1 const& p1, P2 & p2) const
+    {
+        using p2_type = typename boost::geometry::coordinate_type<P2>::type;
+        double x = boost::geometry::get<0>(p1);
+        double y = boost::geometry::get<1>(p1);
+        double z = 0.0;
+        if (!prj_trans_.backward(x, y, z)) return false;
+        tr_.forward(&x,&y);
+        x = x * scaling_;
+        y = y * scaling_;
+        x = std::round(x);
+        y = std::round(y);
+        boost::geometry::set<0>(p2, static_cast<p2_type>(x));
+        boost::geometry::set<1>(p2, static_cast<p2_type>(y));
+        return true;
+    }
+    
+    template <typename P1, typename P2>
+    inline P2 execute(P1 const& p1, bool & status) const
+    {
+        P2 p2;
+        status = apply(p1, p2);
+        return p2;
+    }
+
+    proj_transform const& prj_trans_;
+    view_transform const& tr_;
+    double const scaling_;
+};
+
+}
+}
+
+#endif // MAPNIK_VECTOR_TILE_STRATEGY_HPP
diff --git a/test/clipper_test.cpp b/test/clipper_test.cpp
new file mode 100644
index 0000000..d06b2bc
--- /dev/null
+++ b/test/clipper_test.cpp
@@ -0,0 +1,143 @@
+#include <limits>
+#include <iostream>
+#include <mapnik/projection.hpp>
+#include <mapnik/geometry_transform.hpp>
+//#include <mapnik/util/geometry_to_geojson.hpp>
+
+#include "vector_tile_strategy.hpp"
+#include "vector_tile_projection.hpp"
+
+#include "catch.hpp"
+#include "clipper.hpp"
+
+TEST_CASE( "vector_tile_strategy", "should not overflow" ) {
+    mapnik::projection merc("+init=epsg:3857",true);
+    mapnik::proj_transform prj_trans(merc,merc); // no-op
+    unsigned tile_size = 256;
+    mapnik::vector_tile_impl::spherical_mercator merc_tiler(tile_size);
+    double minx,miny,maxx,maxy;
+    merc_tiler.xyz(9664,20435,15,minx,miny,maxx,maxy);
+    mapnik::box2d<double> z15_extent(minx,miny,maxx,maxy);
+    mapnik::view_transform tr(tile_size,tile_size,z15_extent,0,0);
+    {
+        mapnik::vector_tile_impl::vector_tile_strategy vs(prj_trans, tr, 16);
+        // even an invalid point is not expected to result in values beyond hirange
+        mapnik::geometry::point<std::int64_t> g(-20037508.342789*2,-20037508.342789*2);
+        mapnik::geometry::geometry<std::int64_t> new_geom = mapnik::geometry::transform<std::int64_t>(g, vs);
+        REQUIRE( new_geom.is<mapnik::geometry::point<std::int64_t>>() );
+        auto const& pt = mapnik::util::get<mapnik::geometry::point<std::int64_t>>(new_geom);
+        REQUIRE( (pt.x < ClipperLib::hiRange) );
+        REQUIRE( (pt.y < ClipperLib::hiRange) );
+        REQUIRE( (-pt.x < ClipperLib::hiRange) );
+        REQUIRE( (-pt.y < ClipperLib::hiRange) );
+    }
+    merc_tiler.xyz(0,0,0,minx,miny,maxx,maxy);
+    mapnik::geometry::polygon<std::int64_t> g;
+    g.exterior_ring.add_coord(minx,miny);
+    g.exterior_ring.add_coord(maxx,miny);
+    g.exterior_ring.add_coord(maxx,maxy);
+    g.exterior_ring.add_coord(minx,maxy);
+    g.exterior_ring.add_coord(minx,miny);
+    {
+        // absurdly large but still should not result in values beyond hirange
+        double path_multiplier = 100000000000.0;
+        mapnik::vector_tile_impl::vector_tile_strategy vs(prj_trans, tr, path_multiplier);
+        mapnik::geometry::geometry<std::int64_t> new_geom = mapnik::geometry::transform<std::int64_t>(g, vs);
+        REQUIRE( new_geom.is<mapnik::geometry::polygon<std::int64_t>>() );
+        auto const& poly = mapnik::util::get<mapnik::geometry::polygon<std::int64_t>>(new_geom);
+        for (auto const& pt : poly.exterior_ring)
+        {
+            INFO( pt.x )
+            INFO( ClipperLib::hiRange )
+            REQUIRE( (pt.x < ClipperLib::hiRange) );
+            REQUIRE( (pt.y < ClipperLib::hiRange) );
+            REQUIRE( (-pt.x < ClipperLib::hiRange) );
+            REQUIRE( (-pt.y < ClipperLib::hiRange) );
+        }
+    }
+    {
+        // expected to trigger values above hirange
+        double path_multiplier = 1000000000000.0;
+        mapnik::vector_tile_impl::vector_tile_strategy vs(prj_trans, tr, path_multiplier);
+        mapnik::geometry::geometry<std::int64_t> new_geom = mapnik::geometry::transform<std::int64_t>(g, vs);
+        REQUIRE( new_geom.is<mapnik::geometry::polygon<std::int64_t>>() );
+        auto const& poly = mapnik::util::get<mapnik::geometry::polygon<std::int64_t>>(new_geom);
+        for (auto const& pt : poly.exterior_ring)
+        {
+            INFO( pt.x )
+            INFO( ClipperLib::hiRange )
+            REQUIRE(( (pt.x > ClipperLib::hiRange) ||
+                       (pt.y > ClipperLib::hiRange) ||
+                       (-pt.x < ClipperLib::hiRange) ||
+                       (-pt.y < ClipperLib::hiRange)
+                    ));
+        }
+    }
+
+}
+
+TEST_CASE( "clipper IntPoint", "should accept 64bit values" ) {
+    std::int64_t x = 4611686018427387903;
+    std::int64_t y = 4611686018427387903;
+    auto x0 = std::numeric_limits<std::int64_t>::max();
+    auto y0 = std::numeric_limits<std::int64_t>::max();
+    REQUIRE( x == ClipperLib::hiRange );
+    REQUIRE( y == ClipperLib::hiRange );
+    REQUIRE( (x0/2) == ClipperLib::hiRange );
+    REQUIRE( (y0/2) == ClipperLib::hiRange );
+    auto pt = ClipperLib::IntPoint(x,y);
+    CHECK( pt.x == x );
+    CHECK( pt.y == y );
+    auto pt2 = ClipperLib::IntPoint(x,y);
+    CHECK( (pt == pt2) );
+    CHECK( !(pt != pt2) );
+    // this is invalid when passed to RangeTest but should
+    // still be able to be created
+    auto pt3 = ClipperLib::IntPoint(x0,y0);
+    REQUIRE( pt3.x == x0 );
+    REQUIRE( pt3.y == y0 );
+    REQUIRE( (pt3.x/2) == ClipperLib::hiRange );
+    REQUIRE( (pt3.y/2) == ClipperLib::hiRange );
+    CHECK( (pt != pt3) );
+}
+
+TEST_CASE( "clipper AddPath 1", "should not throw within range coords" ) {
+    ClipperLib::Clipper clipper;
+    ClipperLib::Path clip_box; // actually mapnik::geometry::line_string<std::int64_t>
+    // values that should just barely work since they are one below the
+    // threshold
+    auto x0 = (std::numeric_limits<std::int64_t>::min()/2)+1;
+    auto y0 = (std::numeric_limits<std::int64_t>::min()/2)+1;
+    auto x1 = (std::numeric_limits<std::int64_t>::max()/2);
+    auto y1 = (std::numeric_limits<std::int64_t>::max()/2);
+    clip_box.emplace_back(x0,y0);
+    clip_box.emplace_back(x1,y0);
+    clip_box.emplace_back(x1,y1);
+    clip_box.emplace_back(x0,y1);
+    clip_box.emplace_back(x0,y0);
+    CHECK( clipper.AddPath(clip_box,ClipperLib::ptClip,true) );
+}
+
+TEST_CASE( "clipper AddPath 2", "should throw on out of range coords" ) {
+    ClipperLib::Clipper clipper;
+    ClipperLib::Path clip_box; // actually mapnik::geometry::line_string<std::int64_t>
+    auto x0 = std::numeric_limits<std::int64_t>::min()+1;
+    auto y0 = std::numeric_limits<std::int64_t>::min()+1;
+    auto x1 = std::numeric_limits<std::int64_t>::max()-1;
+    auto y1 = std::numeric_limits<std::int64_t>::max()-1;
+    clip_box.emplace_back(x0,y0);
+    clip_box.emplace_back(x1,y0);
+    clip_box.emplace_back(x1,y1);
+    clip_box.emplace_back(x0,y1);
+    clip_box.emplace_back(x0,y0);
+    try
+    {
+      clipper.AddPath(clip_box,ClipperLib::ptClip,true);
+      FAIL( "expected exception" );
+    }
+    catch(std::exception const& ex)
+    {
+      REQUIRE(std::string(ex.what()) == "Coordinate outside allowed range: -9223372036854775807 -9223372036854775807 9223372036854775807 9223372036854775807");
+    }        
+}
+
diff --git a/test/data/natural_earth.tif b/test/data/natural_earth.tif
deleted file mode 100644
index 43500b0..0000000
Binary files a/test/data/natural_earth.tif and /dev/null differ
diff --git a/test/data/tile_with_extra_feature_field.pbf b/test/data/tile_with_extra_feature_field.pbf
new file mode 100644
index 0000000..484e656
--- /dev/null
+++ b/test/data/tile_with_extra_feature_field.pbf
@@ -0,0 +1,2 @@
+

+this is the name0c(��x
\ No newline at end of file
diff --git a/test/data/tile_with_extra_field.pbf b/test/data/tile_with_extra_field.pbf
new file mode 100644
index 0000000..0c08869
--- /dev/null
+++ b/test/data/tile_with_extra_field.pbf
@@ -0,0 +1,2 @@
+
+this is the name(��x c
\ No newline at end of file
diff --git a/test/data/tile_with_extra_layer_fields.pbf b/test/data/tile_with_extra_layer_fields.pbf
new file mode 100644
index 0000000..8dc5537
--- /dev/null
+++ b/test/data/tile_with_extra_layer_fields.pbf
@@ -0,0 +1,2 @@
+

+this is the name(��0cx�c
\ No newline at end of file
diff --git a/test/data/tile_with_invalid_layer_value_type.pbf b/test/data/tile_with_invalid_layer_value_type.pbf
new file mode 100644
index 0000000..8c8b9ef
--- /dev/null
+++ b/test/data/tile_with_invalid_layer_value_type.pbf
@@ -0,0 +1,2 @@
+

+this is the name"@c(��x
\ No newline at end of file
diff --git a/test/data/tile_with_unexpected_geomtype.pbf b/test/data/tile_with_unexpected_geomtype.pbf
new file mode 100644
index 0000000..f718cd2
--- /dev/null
+++ b/test/data/tile_with_unexpected_geomtype.pbf
@@ -0,0 +1,3 @@
+

+this is the name(��x"
+this is the name"cccc(��x
\ No newline at end of file
diff --git a/test/encoding_util.hpp b/test/encoding_util.hpp
index cf10bde..5568c48 100644
--- a/test/encoding_util.hpp
+++ b/test/encoding_util.hpp
@@ -150,6 +150,7 @@ template <typename T>
 std::string compare(mapnik::geometry::geometry<T> const& g)
 {
     vector_tile::Tile_Feature feature = geometry_to_feature(g);
-    auto g2 = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
+    mapnik::vector_tile_impl::Geometry geoms(feature,0.0,0.0,1.0,1.0);
+    auto g2 = mapnik::vector_tile_impl::decode_geometry(geoms,feature.type());
     return decode_to_path_string(g2);
 }
diff --git a/test/geometry_encoding.cpp b/test/geometry_encoding.cpp
index 31a8805..b599580 100644
--- a/test/geometry_encoding.cpp
+++ b/test/geometry_encoding.cpp
@@ -180,7 +180,8 @@ TEST_CASE( "polygon with degenerate exterior ring ", "should be culled" ) {
 
     vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
     // since first ring is degenerate the whole polygon should be culled
-    auto p1 = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
+    mapnik::vector_tile_impl::Geometry geoms(feature,0.0,0.0,1.0,1.0);
+    auto p1 = mapnik::vector_tile_impl::decode_geometry(geoms, feature.type());
     CHECK( p1.is<mapnik::geometry::geometry_empty>() );
 }
 
@@ -206,7 +207,8 @@ TEST_CASE( "polygon with degenerate exterior ring ", "should be culled" ) {
 
     vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
     // since first ring is degenerate the whole polygon should be culled
-    auto p1 = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
+    mapnik::vector_tile_impl::Geometry geoms(feature,0.0,0.0,1.0,1.0);
+    auto p1 = mapnik::vector_tile_impl::decode_geometry(geoms, feature.type());
     CHECK( p1.is<mapnik::geometry::geometry_empty>() );
 }*/
 
@@ -230,7 +232,8 @@ TEST_CASE( "polygon with valid exterior ring but degenerate interior ring", "sho
     CHECK( wkt0 == expected_wkt0);
 
     vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
-    auto p1 = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
+    mapnik::vector_tile_impl::Geometry geoms(feature,0.0,0.0,1.0,1.0);
+    auto p1 = mapnik::vector_tile_impl::decode_geometry(geoms, feature.type());
     CHECK( p1.is<mapnik::geometry::polygon<double> >() );
     auto const& poly = mapnik::util::get<mapnik::geometry::polygon<double> >(p1);
     // since interior ring is degenerate it should have been culled when decoded
@@ -270,7 +273,8 @@ TEST_CASE( "polygon with valid exterior ring but one degenerate interior ring of
     CHECK( wkt0 == expected_wkt0);
 
     vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
-    auto p1 = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
+    mapnik::vector_tile_impl::Geometry geoms(feature,0.0,0.0,1.0,1.0);
+    auto p1 = mapnik::vector_tile_impl::decode_geometry(geoms, feature.type());
     CHECK( p1.is<mapnik::geometry::polygon<double> >() );
     auto const& poly = mapnik::util::get<mapnik::geometry::polygon<double> >(p1);
     // since first interior ring is degenerate it should have been culled when decoded
@@ -311,7 +315,8 @@ TEST_CASE( "(multi)polygon with hole", "should round trip without changes" ) {
     CHECK( wkt0 == expected_wkt0);
 
     vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
-    auto p1 = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
+    mapnik::vector_tile_impl::Geometry geoms(feature,0.0,0.0,1.0,1.0);
+    auto p1 = mapnik::vector_tile_impl::decode_geometry(geoms, feature.type());
     CHECK( p1.is<mapnik::geometry::polygon<double> >() );
     CHECK( extent == mapnik::geometry::envelope(p1) );
 
@@ -323,7 +328,9 @@ TEST_CASE( "(multi)polygon with hole", "should round trip without changes" ) {
     // for polygons rings that were encoded correctly in vtiles (CCW exterior, CW interior)
     // then this should be unneeded, but for rings with incorrect order then this style of
     // decoding should allow them still to be queried correctly using the current mapnik hit_test algos
-    auto _p1 = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0,true);
+    // Note, we need a new Geometry here, the old object can't be rewound.
+    mapnik::vector_tile_impl::Geometry geoms2(feature,0.0,0.0,1.0,1.0);
+    auto _p1 = mapnik::vector_tile_impl::decode_geometry(geoms2, feature.type(), true);
     wkt0.clear();
     CHECK( mapnik::util::to_wkt(wkt0,_p1) );
     CHECK( _p1.is<mapnik::geometry::multi_polygon<double> >() );
@@ -378,7 +385,8 @@ TEST_CASE( "(multi)polygon with hole", "should round trip without changes" ) {
     mapnik::box2d<double> multi_extent = mapnik::geometry::envelope(multi_poly);
 
     vector_tile::Tile_Feature feature1 = geometry_to_feature<std::int64_t>(multi_poly);
-    auto mp = mapnik::vector_tile_impl::decode_geometry(feature1,0.0,0.0,1.0,1.0);
+    mapnik::vector_tile_impl::Geometry geoms1(feature1,0.0,0.0,1.0,1.0);
+    auto mp = mapnik::vector_tile_impl::decode_geometry(geoms1, feature1.type());
     CHECK( mp.is<mapnik::geometry::multi_polygon<double> >() );
 
     CHECK( multi_extent == mapnik::geometry::envelope(mp) );
diff --git a/test/test_main.cpp b/test/test_main.cpp
index 8d5a3a8..e12617c 100644
--- a/test/test_main.cpp
+++ b/test/test_main.cpp
@@ -6,7 +6,14 @@
 
 int main (int argc, char* const argv[])
 {
-    GOOGLE_PROTOBUF_VERIFY_VERSION;
+    try
+    {
+        GOOGLE_PROTOBUF_VERIFY_VERSION;
+    }
+    catch (std::exception const& ex) {
+        std::clog << ex.what() << "\n";
+        return -1;
+    }
     int result = Catch::Session().run( argc, argv );
     if (!result) printf("\x1b[1;32m ✓ \x1b[0m\n");
     google::protobuf::ShutdownProtobufLibrary();
diff --git a/test/test_utils.cpp b/test/test_utils.cpp
index 7378d5e..b156662 100644
--- a/test/test_utils.cpp
+++ b/test/test_utils.cpp
@@ -30,6 +30,14 @@ std::shared_ptr<mapnik::memory_datasource> build_ds(double x,double y, bool seco
     mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx,1));
     mapnik::transcoder tr("utf-8");
     feature->put("name",tr.transcode("null island"));
+    // NOTE: all types below that are not part of mapnik::value
+    // are likely getting converted, e.g. float -> double
+    feature->put_new("int",static_cast<int64_t>(-73));
+    feature->put_new("uint",static_cast<uint64_t>(37));
+    feature->put_new("float",static_cast<float>(99.2));
+    feature->put_new("double",static_cast<double>(83.4));
+    feature->put_new("bool",true);
+    feature->put_new("boolf",false);
     feature->set_geometry(mapnik::geometry::point<double>(x,y));
     ds->push(feature);
     if (second) {
@@ -46,13 +54,17 @@ std::shared_ptr<mapnik::memory_datasource> build_ds(double x,double y, bool seco
 
 std::shared_ptr<mapnik::memory_datasource> build_geojson_ds(std::string const& geojson_file) {
     mapnik::util::file input(geojson_file);
-    auto json = input.data();
+    if (!input.open())
+    {
+        throw std::runtime_error("failed to open geojson");
+    }
     mapnik::geometry::geometry<double> geom;
-    std::string json_string(json.get());
+    std::string json_string(input.data().get(), input.size());
     if (!mapnik::json::from_geojson(json_string, geom))
     {
         throw std::runtime_error("failed to parse geojson");
     }
+    mapnik::geometry::correct(geom);
     mapnik::parameters params;
     params["type"] = "memory";
     std::shared_ptr<mapnik::memory_datasource> ds = std::make_shared<mapnik::memory_datasource>(params);
diff --git a/test/vector_tile.cpp b/test/vector_tile.cpp
index 04ecb1c..3a15986 100644
--- a/test/vector_tile.cpp
+++ b/test/vector_tile.cpp
@@ -126,10 +126,10 @@ TEST_CASE( "vector tile output 1", "should create vector tile with two points" )
     CHECK(9 == f.geometry(0));
     CHECK(4096 == f.geometry(1));
     CHECK(4096 == f.geometry(2));
-    CHECK(95 == tile.ByteSize());
+    CHECK(194 == tile.ByteSize());
     std::string buffer;
     CHECK(tile.SerializeToString(&buffer));
-    CHECK(95 == buffer.size());
+    CHECK(194 == buffer.size());
 }
 
 TEST_CASE( "vector tile output 2", "adding empty layers should result in empty tile" ) {
@@ -232,7 +232,7 @@ TEST_CASE( "vector tile input", "should be able to parse message and render poin
     // serialize to message
     std::string buffer;
     CHECK(tile.SerializeToString(&buffer));
-    CHECK(52 == buffer.size());
+    CHECK(151 == buffer.size());
     // now create new objects
     mapnik::Map map2(tile_size,tile_size,"+init=epsg:3857");
     tile_type tile2;
@@ -254,7 +254,13 @@ TEST_CASE( "vector tile input", "should be able to parse message and render poin
     CHECK( ds->get_geometry_type() == mapnik::datasource_geometry_t::Collection );
     mapnik::layer_descriptor lay_desc = ds->get_descriptor();
     std::vector<std::string> expected_names;
+    expected_names.push_back("bool");
+    expected_names.push_back("boolf");
+    expected_names.push_back("double");
+    expected_names.push_back("float");
+    expected_names.push_back("int");
     expected_names.push_back("name");
+    expected_names.push_back("uint");
     std::vector<std::string> names;
     for (auto const& desc : lay_desc.get_descriptors())
     {
@@ -540,25 +546,11 @@ TEST_CASE( "encoding single line 1", "should maintain start/end vertex" ) {
 // ported from shapefile test in tilelive-bridge (a:should render a (1.0.1))
 TEST_CASE( "encoding single line 2", "should maintain start/end vertex" ) {
     unsigned path_multiplier = 16;
-    //unsigned tolerance = 5;
     vector_tile::Tile tile;
     mapnik::vector_tile_impl::backend_pbf backend(tile,path_multiplier);
     backend.start_tile_layer("layer");
     mapnik::feature_ptr feature(mapnik::feature_factory::create(std::make_shared<mapnik::context_type>(),1));
     backend.start_tile_feature(*feature);
-    /*
-    std::unique_ptr<mapnik::geometry_type> g(new mapnik::geometry_type(mapnik::geometry_type::types::Polygon));
-    g->move_to(168.267850,-24.576888);
-    g->line_to(167.982618,-24.697145);
-    g->line_to(168.114561,-24.783548);
-    g->line_to(168.267850,-24.576888);
-    g->line_to(168.267850,-24.576888);
-    g->close_path();
-    //g->push_vertex(256.000000,-0.00000, mapnik::SEG_CLOSE);
-    // todo - why does shape_io result in on-zero close path x,y?
-    mapnik::vertex_adapter va(*g);
-    backend.add_path(va, tolerance, g->type());
-    */
     mapnik::geometry::polygon<double> geom;
     {
         mapnik::geometry::linear_ring<double> ring;
@@ -569,7 +561,7 @@ TEST_CASE( "encoding single line 2", "should maintain start/end vertex" ) {
         ring.add_coord(168.267850,-24.576888);
         geom.set_exterior_ring(std::move(ring));
     }
-    mapnik::geometry::scale_strategy scale_strat(backend.get_path_multiplier(), 0.5);
+    mapnik::geometry::scale_rounding_strategy scale_strat(backend.get_path_multiplier());
     mapnik::geometry::polygon<std::int64_t> poly = mapnik::geometry::transform<std::int64_t>(geom, scale_strat);
     std::string foo;
     mapnik::util::to_wkt(foo, poly);
@@ -627,13 +619,44 @@ mapnik::geometry::geometry<double> round_trip(mapnik::geometry::geometry<double>
     }
     vector_tile::Tile_Feature const& f = layer.features(0);
     double scale = (double)path_multiplier;
-    return mapnik::vector_tile_impl::decode_geometry(f,0,0,scale,-1*scale);
+
+    mapnik::vector_tile_impl::Geometry geoms(f,0,0,scale,-1*scale);
+    return mapnik::vector_tile_impl::decode_geometry(geoms, f.type());
 }
 
 TEST_CASE( "vector tile point encoding", "should create vector tile with data" ) {
     mapnik::geometry::point<double> geom(0,0);
     mapnik::geometry::geometry<double> new_geom = round_trip(geom);
     CHECK( !mapnik::geometry::is_empty(new_geom) );
+    std::string wkt;
+    CHECK( mapnik::util::to_wkt(wkt, new_geom) );
+    CHECK( wkt == "POINT(128 -128)" );
+    CHECK( new_geom.is<mapnik::geometry::point<double> >() );
+}
+
+TEST_CASE( "vector tile geometry collection encoding", "should create vector tile with data" ) {
+    mapnik::geometry::point<double> geom_p(0,0);
+    mapnik::geometry::geometry_collection<double> geom;
+    geom.push_back(geom_p);
+    mapnik::geometry::geometry<double> new_geom = round_trip(geom);
+    CHECK( !mapnik::geometry::is_empty(new_geom) );
+    std::string wkt;
+    CHECK( mapnik::util::to_wkt(wkt, new_geom) );
+    CHECK( wkt == "POINT(128 -128)" );
+    CHECK( new_geom.is<mapnik::geometry::point<double> >() );
+}
+
+TEST_CASE( "vector tile geometry collection encoding x2", "should create vector tile with data" ) {
+    mapnik::geometry::point<double> geom_p(0,0);
+    mapnik::geometry::geometry_collection<double> geom_t;
+    geom_t.push_back(geom_p);
+    mapnik::geometry::geometry_collection<double> geom;
+    geom.push_back(std::move(geom_t));
+    mapnik::geometry::geometry<double> new_geom = round_trip(geom);
+    CHECK( !mapnik::geometry::is_empty(new_geom) );
+    std::string wkt;
+    CHECK( mapnik::util::to_wkt(wkt, new_geom) );
+    CHECK( wkt == "POINT(128 -128)" );
     CHECK( new_geom.is<mapnik::geometry::point<double> >() );
 }
 
@@ -667,7 +690,7 @@ TEST_CASE( "vector tile line_string encoding", "should create vector tile with d
     mapnik::geometry::geometry<double> new_geom = round_trip(geom);
     std::string wkt;
     CHECK( mapnik::util::to_wkt(wkt, new_geom) );
-    CHECK( wkt == "LINESTRING(128 -128,192.001 0)" );
+    CHECK( wkt == "LINESTRING(128 -128,192 0)" );
     CHECK( !mapnik::geometry::is_empty(new_geom) );
     CHECK( new_geom.is<mapnik::geometry::line_string<double> >() );
 }
@@ -681,7 +704,7 @@ TEST_CASE( "vector tile multi_line_string encoding of single line_string", "shou
     mapnik::geometry::geometry<double> new_geom = round_trip(geom);
     std::string wkt;
     CHECK( mapnik::util::to_wkt(wkt, new_geom) );
-    CHECK( wkt == "LINESTRING(128 -128,192.001 0)" );
+    CHECK( wkt == "LINESTRING(128 -128,192 0)" );
     CHECK( !mapnik::geometry::is_empty(new_geom) );
     CHECK( new_geom.is<mapnik::geometry::line_string<double> >() );
 }
@@ -699,7 +722,7 @@ TEST_CASE( "vector tile multi_line_string encoding of actual multi_line_string",
     mapnik::geometry::geometry<double> new_geom = round_trip(geom);
     std::string wkt;
     CHECK( mapnik::util::to_wkt(wkt, new_geom) );
-    CHECK( wkt == "MULTILINESTRING((128 -128,192.001 0),(120.889 -128,63.288 -256))" );
+    CHECK( wkt == "MULTILINESTRING((128 -128,192 0),(120.889 -128,63.288 -256))" );
     CHECK( !mapnik::geometry::is_empty(new_geom) );
     CHECK( new_geom.is<mapnik::geometry::multi_line_string<double> >() );
 }
@@ -808,7 +831,7 @@ TEST_CASE( "vector tile line_string is simplified", "should create vector tile w
     mapnik::geometry::geometry<double> new_geom = round_trip(line,500);
     std::string wkt;
     CHECK( mapnik::util::to_wkt(wkt, new_geom) );
-    CHECK( wkt == "LINESTRING(128 -128,192.001 0)" );
+    CHECK( wkt == "LINESTRING(128 -128,192 0)" );
     CHECK( !mapnik::geometry::is_empty(new_geom) );
     CHECK( new_geom.is<mapnik::geometry::line_string<double> >() );
     auto const& line2 = mapnik::util::get<mapnik::geometry::line_string<double> >(new_geom);
@@ -826,7 +849,7 @@ TEST_CASE( "vector tile multi_line_string is simplified", "should create vector
     mapnik::geometry::geometry<double> new_geom = round_trip(geom,500);
     std::string wkt;
     CHECK( mapnik::util::to_wkt(wkt, new_geom) );
-    CHECK( wkt == "LINESTRING(128 -128,192.001 0)" );
+    CHECK( wkt == "LINESTRING(128 -128,192 0)" );
     CHECK( !mapnik::geometry::is_empty(new_geom) );
     CHECK( new_geom.is<mapnik::geometry::line_string<double> >() );
     auto const& line2 = mapnik::util::get<mapnik::geometry::line_string<double> >(new_geom);
@@ -917,10 +940,7 @@ TEST_CASE( "vector tile from simplified geojson", "should create vector tile wit
     mapnik::box2d<double> bbox(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789);
     mapnik::Map map(tile_size,tile_size,"+init=epsg:3857");
     mapnik::layer lyr("layer","+init=epsg:4326");
-    // create a datasource with a feature outside the map
     std::shared_ptr<mapnik::memory_datasource> ds = testing::build_geojson_ds("./test/data/poly.geojson");
-    // but fake the overall envelope to ensure the layer is still processed
-    // and then removed given no intersecting features will be added
     ds->set_envelope(mapnik::box2d<double>(160.147311,11.047284,160.662858,11.423830));
     lyr.set_datasource(ds);
     map.add_layer(lyr);
@@ -941,7 +961,8 @@ TEST_CASE( "vector tile from simplified geojson", "should create vector tile wit
     double tile_x = -0.5 * mapnik::EARTH_CIRCUMFERENCE + x * resolution;
     double tile_y =  0.5 * mapnik::EARTH_CIRCUMFERENCE - y * resolution;
     double scale = (static_cast<double>(layer.extent()) / tile_size) * tile_size/resolution;
-    auto geom = mapnik::vector_tile_impl::decode_geometry(f,tile_x,tile_y,scale,-1*scale);
+    mapnik::vector_tile_impl::Geometry geoms(f,tile_x, tile_y,scale,-1*scale);
+    auto geom = mapnik::vector_tile_impl::decode_geometry(geoms,f.type());
 
     unsigned int n_err = 0;
     mapnik::projection wgs84("+init=epsg:4326",true);
@@ -951,7 +972,7 @@ TEST_CASE( "vector tile from simplified geojson", "should create vector tile wit
     CHECK( n_err == 0 );
     std::string geojson_string;
     CHECK( mapnik::util::to_geojson(geojson_string,projected_geom) );
-    CHECK( geojson_string == "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[160.42640625,11.4238608092025],[160.41375,11.404562686369],[160.3996875,11.3949131331061],[160.3996875,11.3990486960562],[160.39265625,11.4031841988239],[160.3940625,11.3976701817588],[160.38703125,11.3838846711709],[160.39265625,11.3825060833676],[160.39125,11.3618264654176],[160.3378125,11.3397665531013],[160.3434375,11.3604477708622],[160.26609375,11.3094313929343],[160.28296875,11.3011576095711],[160.29,11.2 [...]
+    CHECK( geojson_string == "{\"type\":\"Polygon\",\"coordinates\":[[[160.42359375,11.422482415387],[160.40671875,11.3976701817587],[160.396875,11.3935345987523],[160.39828125,11.4018057045895],[160.39265625,11.4004272036667],[160.38984375,11.3811274888866],[160.3940625,11.3838846711709],[160.3771875,11.3521754635814],[160.33921875,11.3590690696413],[160.35046875,11.3645838345287],[160.3575,11.3645838345287],[160.3575,11.3756130442004],[160.28859375,11.3480392200085],[160.295625,11.3287 [...]
 }
 
 mapnik::geometry::geometry<double> round_trip2(mapnik::geometry::geometry<double> const& geom,
@@ -1000,7 +1021,8 @@ mapnik::geometry::geometry<double> round_trip2(mapnik::geometry::geometry<double
     double tile_x = -0.5 * mapnik::EARTH_CIRCUMFERENCE + x * resolution;
     double tile_y =  0.5 * mapnik::EARTH_CIRCUMFERENCE - y * resolution;
     double scale = (static_cast<double>(layer.extent()) / tile_size) * tile_size/resolution;
-    return mapnik::vector_tile_impl::decode_geometry(f,tile_x,tile_y,scale,-1*scale);
+    mapnik::vector_tile_impl::Geometry geoms(f,tile_x, tile_y,scale,-1*scale);
+    return mapnik::vector_tile_impl::decode_geometry(geoms,f.type());
 }
 
 TEST_CASE( "vector tile line_string is verify direction", "should line string with proper directions" ) {
@@ -1028,7 +1050,7 @@ TEST_CASE( "vector tile line_string is verify direction", "should line string wi
     mapnik::geometry::geometry<double> xgeom = mapnik::geometry::transform<double>(new_geom, proj_strat);
     std::string wkt;
     mapnik::util::to_wkt(wkt, xgeom);
-    CHECK( wkt == "MULTILINESTRING((0 1.99992945603165,2.00006103515625 1.99992945603165,2.00006103515625 0),(7.99996948242188 0,7.99996948242188 1.99992945603165,59.9999084472656 1.99992945603165,59.9999084472656 7.99994115658818,7.99996948242188 7.99994115658818,7.99996948242188 59.9998101102059,2.00006103515625 59.9998101102059,2.00006103515625 7.99994115658817,0.0000000000000005 7.99994115658817))" );
+    CHECK( wkt == "MULTILINESTRING((0 1.99992945603165,2.00006103515625 1.99992945603165,2.00006103515625 0),(7.99996948242188 0,7.99996948242188 1.99992945603165,59.9999084472656 1.99992945603165,59.9999084472656 7.99994115658818,7.99996948242188 7.99994115658818,7.99996948242188 59.9999474398107,2.00006103515625 59.9999474398107,2.00006103515625 7.99994115658818,0.0000000000000005 7.99994115658818))" );
     REQUIRE( !mapnik::geometry::is_empty(xgeom) );
     REQUIRE( new_geom.is<mapnik::geometry::multi_line_string<double> >() );
     auto const& line2 = mapnik::util::get<mapnik::geometry::multi_line_string<double> >(new_geom);
diff --git a/test/vector_tile_pbf.cpp b/test/vector_tile_pbf.cpp
new file mode 100644
index 0000000..25482e5
--- /dev/null
+++ b/test/vector_tile_pbf.cpp
@@ -0,0 +1,557 @@
+#include "catch.hpp"
+
+// test utils
+#include "test_utils.hpp"
+#include <mapnik/memory_datasource.hpp>
+#include <mapnik/util/fs.hpp>
+#include <mapnik/agg_renderer.hpp>
+#include <mapnik/feature_factory.hpp>
+#include <mapnik/load_map.hpp>
+#include <mapnik/image_util.hpp>
+#include <mapnik/vertex_adapters.hpp>
+#include <mapnik/projection.hpp>
+#include <mapnik/proj_transform.hpp>
+#include <mapnik/geometry_is_empty.hpp>
+#include <mapnik/util/geometry_to_geojson.hpp>
+#include <mapnik/util/geometry_to_wkt.hpp>
+#include <mapnik/geometry_reprojection.hpp>
+#include <mapnik/geometry_transform.hpp>
+#include <mapnik/geometry_strategy.hpp>
+#include <mapnik/proj_strategy.hpp>
+#include <mapnik/geometry.hpp>
+#include <mapnik/datasource_cache.hpp>
+
+#include <boost/optional/optional_io.hpp>
+
+// vector output api
+#include "vector_tile_compression.hpp"
+#include "vector_tile_processor.hpp"
+#include "vector_tile_backend_pbf.hpp"
+#include "vector_tile_util.hpp"
+#include "vector_tile_projection.hpp"
+#include "vector_tile_geometry_decoder.hpp"
+
+// vector input api
+#include "vector_tile_datasource.hpp"
+#include "vector_tile_datasource_pbf.hpp"
+#include "pbf_reader.hpp"
+
+#include <string>
+#include <fstream>
+#include <streambuf>
+
+TEST_CASE( "pbf vector tile input", "should be able to parse message and render point" ) {
+    typedef mapnik::vector_tile_impl::backend_pbf backend_type;
+    typedef mapnik::vector_tile_impl::processor<backend_type> renderer_type;
+    typedef vector_tile::Tile tile_type;
+    tile_type tile;
+    backend_type backend(tile,16);
+    unsigned tile_size = 256;
+    mapnik::box2d<double> bbox(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789);
+    mapnik::Map map(tile_size,tile_size,"+init=epsg:3857");
+    mapnik::layer lyr("layer",map.srs());
+    lyr.set_datasource(testing::build_ds(0,0));
+    map.add_layer(lyr);
+    map.zoom_to_box(bbox);
+    mapnik::request m_req(map.width(),map.height(),map.get_current_extent());
+    renderer_type ren(backend,map,m_req);
+    ren.apply();
+    // serialize to message
+    std::string buffer;
+    CHECK(tile.SerializeToString(&buffer));
+    CHECK(151 == buffer.size());
+    // now create new objects
+    mapnik::Map map2(tile_size,tile_size,"+init=epsg:3857");
+    tile_type tile2;
+    CHECK(tile2.ParseFromString(buffer));
+    std::string key("");
+    CHECK(false == mapnik::vector_tile_impl::is_solid_extent(tile2,key));
+    CHECK("" == key);
+    CHECK(1 == tile2.layers_size());
+    vector_tile::Tile_Layer const& layer2 = tile2.layers(0);
+    CHECK(std::string("layer") == layer2.name());
+    CHECK(1 == layer2.features_size());
+
+    mapnik::layer lyr2("layer",map.srs());
+    
+    mapbox::util::pbf pbf_tile(buffer.c_str(), buffer.size());
+    pbf_tile.next();
+    mapbox::util::pbf layer3 = pbf_tile.get_message();
+    
+    std::shared_ptr<mapnik::vector_tile_impl::tile_datasource_pbf> ds = std::make_shared<
+                                    mapnik::vector_tile_impl::tile_datasource_pbf>(
+                                        layer3,0,0,0,map2.width());
+    CHECK(ds->get_name() == "layer");
+    ds->set_envelope(bbox);
+    CHECK( ds->type() == mapnik::datasource::Vector );
+    CHECK( ds->get_geometry_type() == mapnik::datasource_geometry_t::Collection );
+    mapnik::layer_descriptor lay_desc = ds->get_descriptor();
+    std::vector<std::string> expected_names;
+    expected_names.push_back("bool");
+    expected_names.push_back("boolf");
+    expected_names.push_back("double");
+    expected_names.push_back("float");
+    expected_names.push_back("int");
+    expected_names.push_back("name");
+    expected_names.push_back("uint");
+    std::vector<std::string> names;
+    for (auto const& desc : lay_desc.get_descriptors())
+    {
+        names.push_back(desc.get_name());
+    }
+    
+    CHECK(names == expected_names);
+    lyr2.set_datasource(ds);
+    lyr2.add_style("style");
+    map2.add_layer(lyr2);
+    mapnik::load_map(map2,"test/data/style.xml");
+    //std::clog << mapnik::save_map_to_string(map2) << "\n";
+    map2.zoom_to_box(bbox);
+    mapnik::image_rgba8 im(map2.width(),map2.height());
+    mapnik::agg_renderer<mapnik::image_rgba8> ren2(map2,im);
+    ren2.apply();
+    if (!mapnik::util::exists("test/fixtures/expected-1.png")) {
+        mapnik::save_to_file(im,"test/fixtures/expected-1.png","png32");
+    }
+    unsigned diff = testing::compare_images(im,"test/fixtures/expected-1.png");
+    CHECK(0 == diff);
+    if (diff > 0) {
+        mapnik::save_to_file(im,"test/fixtures/actual-1.png","png32");
+    }
+}
+
+
+TEST_CASE( "pbf vector tile datasource", "should filter features outside extent" ) {
+    typedef mapnik::vector_tile_impl::backend_pbf backend_type;
+    typedef mapnik::vector_tile_impl::processor<backend_type> renderer_type;
+    typedef vector_tile::Tile tile_type;
+    tile_type tile;
+    backend_type backend(tile,16);
+    unsigned tile_size = 256;
+    mapnik::box2d<double> bbox(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789);
+    mapnik::Map map(tile_size,tile_size,"+init=epsg:3857");
+    mapnik::layer lyr("layer",map.srs());
+    lyr.set_datasource(testing::build_ds(0,0));
+    map.add_layer(lyr);
+    mapnik::request m_req(tile_size,tile_size,bbox);
+    renderer_type ren(backend,map,m_req);
+    ren.apply();
+    std::string key("");
+    CHECK(false == mapnik::vector_tile_impl::is_solid_extent(tile,key));
+    CHECK("" == key);
+    CHECK(1 == tile.layers_size());
+    vector_tile::Tile_Layer const& layer = tile.layers(0);
+    CHECK(std::string("layer") == layer.name());
+    CHECK(1 == layer.features_size());
+    vector_tile::Tile_Feature const& f = layer.features(0);
+    CHECK(static_cast<mapnik::value_integer>(1) == static_cast<mapnik::value_integer>(f.id()));
+    CHECK(3 == f.geometry_size());
+    CHECK(9 == f.geometry(0));
+    CHECK(4096 == f.geometry(1));
+    CHECK(4096 == f.geometry(2));
+    
+    std::string buffer;
+    tile.SerializeToString(&buffer);
+    mapbox::util::pbf pbf_tile(buffer.c_str(), buffer.size());
+    pbf_tile.next();
+    mapbox::util::pbf layer2 = pbf_tile.get_message();
+    
+    // now actually start the meat of the test
+    mapnik::vector_tile_impl::tile_datasource_pbf ds(layer2,0,0,0,tile_size);
+    mapnik::featureset_ptr fs;
+
+    // ensure we can query single feature
+    fs = ds.features(mapnik::query(bbox));
+    mapnik::feature_ptr feat = fs->next();
+    CHECK(feat != mapnik::feature_ptr());
+    CHECK(feat->size() == 0);
+    CHECK(fs->next() == mapnik::feature_ptr());
+    mapnik::query qq = mapnik::query(mapnik::box2d<double>(-1,-1,1,1));
+    qq.add_property_name("name");
+    fs = ds.features(qq);
+    feat = fs->next();
+    CHECK(feat != mapnik::feature_ptr());
+    CHECK(feat->size() == 1);
+//    CHECK(feat->get("name") == "null island");
+
+    // now check that datasource api throws out feature which is outside extent
+    fs = ds.features(mapnik::query(mapnik::box2d<double>(-10,-10,-10,-10)));
+    CHECK(fs->next() == mapnik::feature_ptr());
+
+    // ensure same behavior for feature_at_point
+    fs = ds.features_at_point(mapnik::coord2d(0.0,0.0),0.0001);
+    CHECK(fs->next() != mapnik::feature_ptr());
+
+    fs = ds.features_at_point(mapnik::coord2d(1.0,1.0),1.0001);
+    CHECK(fs->next() != mapnik::feature_ptr());
+
+    fs = ds.features_at_point(mapnik::coord2d(-10,-10),0);
+    CHECK(fs->next() == mapnik::feature_ptr());
+
+    // finally, make sure attributes are also filtered
+    mapnik::feature_ptr f_ptr;
+    fs = ds.features(mapnik::query(bbox));
+    f_ptr = fs->next();
+    CHECK(f_ptr != mapnik::feature_ptr());
+    // no attributes
+    CHECK(f_ptr->context()->size() == 0);
+
+    mapnik::query q(bbox);
+    q.add_property_name("name");
+    fs = ds.features(q);
+    f_ptr = fs->next();
+    CHECK(f_ptr != mapnik::feature_ptr());
+    // one attribute
+    CHECK(f_ptr->context()->size() == 1);
+}
+
+// NOTE: encoding multiple lines as one path is technically incorrect
+// because in Mapnik the protocol is to split geometry parts into separate paths.
+// However this case should still be supported because keeping a single flat array is an
+// important optimization in the case that lines do not need to be labeled in custom ways
+// or represented as GeoJSON
+TEST_CASE( "pbf encoding multi line as one path", "should maintain second move_to command" ) {
+    // Options
+    // here we use a multiplier of 1 to avoid rounding numbers
+    // and stay in integer space for simplity
+    unsigned path_multiplier = 1;
+    // here we use an extreme tolerance to prove that all vertices are maintained no matter
+    // the tolerance because we never want to drop a move_to or the first line_to
+    //unsigned tolerance = 2000000;
+    // now create the testing data
+    vector_tile::Tile tile;
+    unsigned tile_size = 256;
+    mapnik::box2d<double> bbox(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789);
+    mapnik::vector_tile_impl::backend_pbf backend(tile,path_multiplier);
+    backend.start_tile_layer("layer");
+    mapnik::feature_ptr feature(mapnik::feature_factory::create(std::make_shared<mapnik::context_type>(),1));
+    backend.start_tile_feature(*feature);
+    mapnik::geometry::multi_line_string<std::int64_t> geom;
+    {
+        mapnik::geometry::linear_ring<std::int64_t> ring;
+        ring.add_coord(0,0);
+        ring.add_coord(2,2);
+        geom.emplace_back(std::move(ring));
+    }
+    {
+        mapnik::geometry::linear_ring<std::int64_t> ring;
+        ring.add_coord(1,1);
+        ring.add_coord(2,2);
+        geom.emplace_back(std::move(ring));
+    }
+    /*
+    g->move_to(0,0);        // takes 3 geoms: command length,x,y
+    g->line_to(2,2);        // new command, so again takes 3 geoms: command length,x,y | total 6
+    g->move_to(1,1);        // takes 3 geoms: command length,x,y
+    g->line_to(2,2);        // new command, so again takes 3 geoms: command length,x,y | total 6
+    */
+    backend.current_feature_->set_type(vector_tile::Tile_GeomType_LINESTRING);
+    for (auto const& line : geom)
+    {
+        backend.add_path(line);
+    }
+    backend.stop_tile_feature();
+    backend.stop_tile_layer();
+    // done encoding single feature/geometry
+    std::string key("");
+    CHECK(false == mapnik::vector_tile_impl::is_solid_extent(tile,key));
+    CHECK("" == key);
+    CHECK(1 == tile.layers_size());
+    vector_tile::Tile_Layer const& layer = tile.layers(0);
+    CHECK(1 == layer.features_size());
+    vector_tile::Tile_Feature const& f = layer.features(0);
+    CHECK(12 == f.geometry_size());
+    CHECK(9 == f.geometry(0)); // 1 move_to
+    CHECK(0 == f.geometry(1)); // x:0
+    CHECK(0 == f.geometry(2)); // y:0
+    CHECK(10 == f.geometry(3)); // 1 line_to
+    CHECK(4 == f.geometry(4)); // x:2
+    CHECK(4 == f.geometry(5)); // y:2
+    CHECK(9 == f.geometry(6)); // 1 move_to
+    CHECK(1 == f.geometry(7)); // x:1
+    CHECK(1 == f.geometry(8)); // y:1
+    CHECK(10 == f.geometry(9)); // 1 line_to
+    CHECK(2 == f.geometry(10)); // x:2
+    CHECK(2 == f.geometry(11)); // y:2
+
+    mapnik::featureset_ptr fs;
+    mapnik::feature_ptr f_ptr;
+    
+    
+    std::string buffer;
+    tile.SerializeToString(&buffer);
+    mapbox::util::pbf pbf_tile(buffer.c_str(), buffer.size());
+    pbf_tile.next();
+    mapbox::util::pbf layer2 = pbf_tile.get_message();
+    
+
+    mapnik::vector_tile_impl::tile_datasource_pbf ds(layer2,0,0,0,tile_size);
+    fs = ds.features(mapnik::query(bbox));
+    f_ptr = fs->next();
+    CHECK(f_ptr != mapnik::feature_ptr());
+    // no attributes
+    CHECK(f_ptr->context()->size() == 0);
+
+    CHECK(f_ptr->get_geometry().is<mapnik::geometry::multi_line_string<double> >());
+}
+
+
+// NOTE: encoding multiple lines as one path is technically incorrect
+// because in Mapnik the protocol is to split geometry parts into separate paths.
+// However this case should still be supported because keeping a single flat array is an
+// important optimization in the case that lines do not need to be labeled in custom ways
+// or represented as GeoJSON
+
+TEST_CASE( "pbf decoding empty buffer", "should throw exception" ) {
+    std::string buffer;
+    mapbox::util::pbf pbf_tile(buffer.c_str(), buffer.size());
+    pbf_tile.next();
+    mapbox::util::pbf layer2;
+    REQUIRE_THROWS(layer2 = pbf_tile.get_message());
+}
+
+TEST_CASE( "pbf decoding garbage buffer", "should throw exception" ) {
+    std::string buffer("daufyglwi3h7fseuhfas8w3h,dksufasdf");
+    mapbox::util::pbf pbf_tile(buffer.c_str(), buffer.size());
+    pbf_tile.next();
+    mapbox::util::pbf layer2;
+    REQUIRE_THROWS(layer2 = pbf_tile.get_message());
+}
+
+
+TEST_CASE( "pbf decoding some truncated buffers", "should throw exception" ) {
+  
+    typedef mapnik::vector_tile_impl::backend_pbf backend_type;
+    typedef mapnik::vector_tile_impl::processor<backend_type> renderer_type;
+    typedef vector_tile::Tile tile_type;
+    tile_type tile;
+    backend_type backend(tile,16);
+    unsigned tile_size = 256;
+    mapnik::box2d<double> bbox(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789);
+    mapnik::Map map(tile_size,tile_size,"+init=epsg:3857");
+    mapnik::layer lyr("layer",map.srs());
+    lyr.set_datasource(testing::build_ds(0,0));
+    map.add_layer(lyr);
+    mapnik::request m_req(tile_size,tile_size,bbox);
+    renderer_type ren(backend,map,m_req);
+    ren.apply();
+    std::string key("");
+    CHECK(false == mapnik::vector_tile_impl::is_solid_extent(tile,key));
+    CHECK("" == key);
+    CHECK(1 == tile.layers_size());
+    vector_tile::Tile_Layer const& layer = tile.layers(0);
+    CHECK(std::string("layer") == layer.name());
+    CHECK(1 == layer.features_size());
+    vector_tile::Tile_Feature const& f = layer.features(0);
+    CHECK(static_cast<mapnik::value_integer>(1) == static_cast<mapnik::value_integer>(f.id()));
+    CHECK(3 == f.geometry_size());
+    CHECK(9 == f.geometry(0));
+    CHECK(4096 == f.geometry(1));
+    CHECK(4096 == f.geometry(2));
+  
+
+    // We will test truncating the generated protobuf at every increment.
+    //  Most cases should fail, except for the lucky bites where we chop
+    // it off at a point that would be valid anyway.
+    std::string buffer;
+    tile.SerializeToString(&buffer);
+    for (int i=1; i< buffer.size(); i++) 
+    {
+      CHECK_THROWS({
+          mapbox::util::pbf pbf_tile(buffer.c_str(), i);
+          pbf_tile.next();
+          mapbox::util::pbf layer2 = pbf_tile.get_message();
+          mapnik::vector_tile_impl::tile_datasource_pbf ds(layer2,0,0,0,tile_size);
+          mapnik::featureset_ptr fs;
+          mapnik::feature_ptr f_ptr;
+          fs = ds.features(mapnik::query(bbox));
+          f_ptr = fs->next();
+          while (f_ptr != mapnik::feature_ptr()) {
+              f_ptr = fs->next();
+          }
+      });
+    }
+}
+
+TEST_CASE( "pbf vector tile from simplified geojson", "should create vector tile with data" ) {
+    typedef mapnik::vector_tile_impl::backend_pbf backend_type;
+    typedef mapnik::vector_tile_impl::processor<backend_type> renderer_type;
+    typedef vector_tile::Tile tile_type;
+    tile_type tile;
+    backend_type backend(tile,1000);
+    unsigned tile_size = 256;
+    mapnik::box2d<double> bbox(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789);
+    mapnik::Map map(tile_size,tile_size,"+init=epsg:3857");
+    mapnik::layer lyr("layer","+init=epsg:4326");
+    std::shared_ptr<mapnik::memory_datasource> ds = testing::build_geojson_ds("./test/data/poly.geojson");
+    ds->set_envelope(mapnik::box2d<double>(160.147311,11.047284,160.662858,11.423830));
+    lyr.set_datasource(ds);
+    map.add_layer(lyr);
+    map.zoom_to_box(bbox);
+    mapnik::request m_req(tile_size,tile_size,bbox);
+    renderer_type ren(backend,map,m_req);
+    ren.apply();
+    CHECK( ren.painted() == true );
+    CHECK(1 == tile.layers_size());
+    vector_tile::Tile_Layer const& layer = tile.layers(0);
+    CHECK(std::string("layer") == layer.name());
+    CHECK(1 == layer.features_size());
+    vector_tile::Tile_Feature const& f = layer.features(0);
+    unsigned z = 0;
+    unsigned x = 0;
+    unsigned y = 0;
+    double resolution = mapnik::EARTH_CIRCUMFERENCE/(1 << z);
+    double tile_x = -0.5 * mapnik::EARTH_CIRCUMFERENCE + x * resolution;
+    double tile_y =  0.5 * mapnik::EARTH_CIRCUMFERENCE - y * resolution;
+    double scale = (static_cast<double>(layer.extent()) / tile_size) * tile_size/resolution;
+
+    std::string buffer;
+    tile.SerializeToString(&buffer);
+    mapbox::util::pbf pbf_tile(buffer.c_str(), buffer.size());
+    pbf_tile.next();
+    mapbox::util::pbf pbf_layer = pbf_tile.get_message();
+    // Need to loop because they could be encoded in any order
+    bool found = false;
+    while (!found && pbf_layer.next()) {
+      // But there will be only one in our tile, so we'll break out of loop once we find it
+      if (pbf_layer.tag() == 2) {
+          mapbox::util::pbf pbf_feature = pbf_layer.get_message();
+          while (!found && pbf_feature.next()) {
+              if (pbf_feature.tag() == 4) {
+                  found = true;
+                  std::pair< mapbox::util::pbf::const_uint32_iterator, mapbox::util::pbf::const_uint32_iterator > geom_itr = pbf_feature.packed_uint32();
+                  mapnik::vector_tile_impl::GeometryPBF geoms(geom_itr, tile_x,tile_y,scale,-1*scale);
+                  auto geom = mapnik::vector_tile_impl::decode_geometry(geoms, f.type());
+
+                  unsigned int n_err = 0;
+                  mapnik::projection wgs84("+init=epsg:4326",true);
+                  mapnik::projection merc("+init=epsg:3857",true);
+                  mapnik::proj_transform prj_trans(merc,wgs84);
+                  mapnik::geometry::geometry<double> projected_geom = mapnik::geometry::reproject_copy(geom,prj_trans,n_err);
+                  CHECK( n_err == 0 );
+                  std::string geojson_string;
+                  CHECK( mapnik::util::to_geojson(geojson_string,projected_geom) );
+                  CHECK( geojson_string == "{\"type\":\"Polygon\",\"coordinates\":[[[160.42359375,11.422482415387],[160.40671875,11.3976701817587],[160.396875,11.3935345987523],[160.39828125,11.4018057045895],[160.39265625,11.4004272036667],[160.38984375,11.3811274888866],[160.3940625,11.3838846711709],[160.3771875,11.3521754635814],[160.33921875,11.3590690696413],[160.35046875,11.3645838345287],[160.3575,11.3645838345287],[160.3575,11.3756130442004],[160.28859375,11.3480392200085],[160. [...]
+                  break;
+              }
+          }
+      }
+    }
+    REQUIRE( found );
+}
+
+
+TEST_CASE( "pbf raster tile output", "should be able to overzoom raster" ) {
+    mapnik::datasource_cache::instance().register_datasources(MAPNIK_PLUGINDIR);
+    typedef vector_tile::Tile tile_type;
+    tile_type tile;
+    {
+        typedef mapnik::vector_tile_impl::backend_pbf backend_type;
+        typedef mapnik::vector_tile_impl::processor<backend_type> renderer_type;
+        double minx,miny,maxx,maxy;
+        mapnik::vector_tile_impl::spherical_mercator merc(256);
+        merc.xyz(0,0,0,minx,miny,maxx,maxy);
+        mapnik::box2d<double> bbox(minx,miny,maxx,maxy);
+        backend_type backend(tile,16);
+        mapnik::Map map(256,256,"+init=epsg:3857");
+        map.set_buffer_size(1024);
+        mapnik::layer lyr("layer",map.srs());
+        mapnik::parameters params;
+        params["type"] = "gdal";
+        std::ostringstream s;
+        s << std::fixed << std::setprecision(16)
+          << bbox.minx() << ',' << bbox.miny() << ','
+          << bbox.maxx() << ',' << bbox.maxy();
+        params["extent"] = s.str();
+        params["file"] = "test/data/256x256.png";
+        std::shared_ptr<mapnik::datasource> ds =
+            mapnik::datasource_cache::instance().create(params);
+        lyr.set_datasource(ds);
+        map.add_layer(lyr);
+        mapnik::request m_req(256,256,bbox);
+        m_req.set_buffer_size(map.buffer_size());
+        renderer_type ren(backend,map,m_req,1.0,0,0,1,"jpeg",mapnik::SCALING_BILINEAR);
+        ren.apply();
+    }
+    // Done creating test data, now test created tile
+    CHECK(1 == tile.layers_size());
+    vector_tile::Tile_Layer const& layer = tile.layers(0);
+    CHECK(std::string("layer") == layer.name());
+    CHECK(1 == layer.features_size());
+    vector_tile::Tile_Feature const& f = layer.features(0);
+    CHECK(static_cast<mapnik::value_integer>(1) == static_cast<mapnik::value_integer>(f.id()));
+    CHECK(0 == f.geometry_size());
+    CHECK(f.has_raster());
+    std::string const& ras_buffer = f.raster();
+    CHECK(!ras_buffer.empty());
+
+    // confirm tile looks correct as encoded
+    std::string buffer;
+    CHECK(tile.SerializeToString(&buffer));
+
+    mapbox::util::pbf pbf_tile(buffer.c_str(), buffer.size());
+    pbf_tile.next();
+    mapbox::util::pbf layer2 = pbf_tile.get_message();
+
+    // now read back and render image at larger size
+    // and zoomed in
+    double minx,miny,maxx,maxy;
+    mapnik::vector_tile_impl::spherical_mercator merc(256);
+    // 2/0/1.png
+    merc.xyz(0,1,2,minx,miny,maxx,maxy);
+    mapnik::box2d<double> bbox(minx,miny,maxx,maxy);
+    mapnik::Map map2(256,256,"+init=epsg:3857");
+    map2.set_buffer_size(1024);
+    mapnik::layer lyr2("layer",map2.srs());
+    std::shared_ptr<mapnik::vector_tile_impl::tile_datasource_pbf> ds2 = std::make_shared<
+                                    mapnik::vector_tile_impl::tile_datasource_pbf>(
+                                        layer2,0,0,0,256);
+    lyr2.set_datasource(ds2);
+    lyr2.add_style("style");
+    map2.add_layer(lyr2);
+    mapnik::load_map(map2,"test/data/raster_style.xml");
+    map2.zoom_to_box(bbox);
+    mapnik::image_rgba8 im(map2.width(),map2.height());
+    mapnik::agg_renderer<mapnik::image_rgba8> ren2(map2,im);
+    ren2.apply();
+    if (!mapnik::util::exists("test/fixtures/expected-3.png")) {
+        mapnik::save_to_file(im,"test/fixtures/expected-3.png","png32");
+    }
+    unsigned diff = testing::compare_images(im,"test/fixtures/expected-3.png");
+    CHECK(0 == diff);
+    if (diff > 0) {
+        mapnik::save_to_file(im,"test/fixtures/actual-3.png","png32");
+    }
+}
+
+TEST_CASE("Check that we throw on various valid-but-we-don't-handle PBF encoded files","Should be throwing exceptions")
+{
+    std::vector<std::string> filenames = {"test/data/tile_with_extra_feature_field.pbf",
+                                          "test/data/tile_with_extra_layer_fields.pbf",
+                                          "test/data/tile_with_invalid_layer_value_type.pbf",
+                                          "test/data/tile_with_unexpected_geomtype.pbf"};
+
+    for (auto const& f : filenames) {
+
+      CHECK_THROWS({
+        std::ifstream t(f);
+        std::string buffer((std::istreambuf_iterator<char>(t)),
+                            std::istreambuf_iterator<char>());
+
+        mapnik::box2d<double> bbox(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789);
+        unsigned tile_size = 256;
+          mapbox::util::pbf pbf_tile(buffer.c_str(), buffer.size());
+          pbf_tile.next();
+          mapbox::util::pbf layer2 = pbf_tile.get_message();
+          mapnik::vector_tile_impl::tile_datasource_pbf ds(layer2,0,0,0,tile_size);
+          mapnik::featureset_ptr fs;
+          mapnik::feature_ptr f_ptr;
+          fs = ds.features(mapnik::query(bbox));
+          f_ptr = fs->next();
+          while (f_ptr != mapnik::feature_ptr()) {
+              f_ptr = fs->next();
+          }
+      });
+   }
+
+}

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



More information about the Pkg-grass-devel mailing list