[mapnik-vector-tile] 08/15: Imported Upstream version 0.9.2+dfsg

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Sat Sep 12 11:06:51 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 9b742160076f0f9a097f81b891d4d3a2f8195b63
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Sep 11 22:55:35 2015 +0200

    Imported Upstream version 0.9.2+dfsg
---
 CHANGELOG.md                                |   6 +
 bench/enf.t5yd5cdi_14_13089_8506.vector.pbf | Bin 0 -> 1353537 bytes
 bench/multi_line_13_1310_3166.vector.pbf    | Bin 0 -> 265593 bytes
 bench/notes.md                              |  58 ++++++++
 bench/vtile-decode.cpp                      | 127 ++++++++++++++++++
 gyp/build.gyp                               |  16 +++
 package.json                                |   2 +-
 scripts/coverage.sh                         |   4 +-
 src/vector_tile_datasource.ipp              |   9 +-
 src/vector_tile_datasource_pbf.ipp          |   9 +-
 src/vector_tile_geometry_decoder.hpp        | 200 ++++++++++++++++++++++++----
 src/vector_tile_geometry_encoder.hpp        |  57 +++++++-
 src/vector_tile_processor.ipp               |  16 +--
 src/vector_tile_util.ipp                    |   3 +-
 test/encoding_util.hpp                      |  96 +++++--------
 test/geometry_encoding.cpp                  |  34 ++---
 16 files changed, 510 insertions(+), 127 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76c85ba..373fea1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## 0.9.2
+
+ - Fixed multipoint encoding (#144)
+ - Optimized decoding by filtering geometry parts not within bbox (#146)
+ - Optimized decoding by calling `vector.reserve` before `vector.emplace_back` (#119)
+
 ## 0.9.1
 
  - Added `is_solid_extent` implementation based on protozero decoder
diff --git a/bench/enf.t5yd5cdi_14_13089_8506.vector.pbf b/bench/enf.t5yd5cdi_14_13089_8506.vector.pbf
new file mode 100644
index 0000000..587020c
Binary files /dev/null and b/bench/enf.t5yd5cdi_14_13089_8506.vector.pbf differ
diff --git a/bench/multi_line_13_1310_3166.vector.pbf b/bench/multi_line_13_1310_3166.vector.pbf
new file mode 100644
index 0000000..00e258a
Binary files /dev/null and b/bench/multi_line_13_1310_3166.vector.pbf differ
diff --git a/bench/notes.md b/bench/notes.md
new file mode 100644
index 0000000..7948c7f
--- /dev/null
+++ b/bench/notes.md
@@ -0,0 +1,58 @@
+With reserve
+
+```
+$ ./build/Release/vtile-decode bench/multi_line_13_1310_3166.vector.pbf 13 1310 3166
+z:13 x:1310 y:3166 iterations:100
+message: zlib compressed
+4026.30ms (cpu 4020.96ms)   | decode as datasource_pbf: bench/multi_line_13_1310_3166.vector.pbf
+```
+
+without reserve code
+
+```
+$ ./build/Release/vtile-decode bench/multi_line_13_1310_3166.vector.pbf 13 1310 3166
+z:13 x:1310 y:3166 iterations:100
+message: zlib compressed
+4296.88ms (cpu 4289.05ms)   | decode as datasource_pbf: bench/multi_line_13_1310_3166.vector.pbf
+```
+
+
+---------
+
+
+baseline (using mapnik::geometry::envelope + filter.pass)
+
+$ .//build/Release/vtile-decode bench/enf.t5yd5cdi_14_13089_8506.vector.pbf 14 13089 8506 200
+z:14 x:13089 y:8506 iterations:200
+2822.66ms (cpu 2821.10ms)   | decode as datasource_pbf: bench/enf.t5yd5cdi_14_13089_8506.vector.pbf
+2070.90ms (cpu 2070.44ms)   | decode as datasource: bench/enf.t5yd5cdi_14_13089_8506.vector.pbf
+processed 6800 features
+
+$ ./build/Release/vtile-decode bench/multi_line_13_1310_3166.vector.pbf 13 1310 3166 100
+z:13 x:1310 y:3166 iterations:100
+message: zlib compressed
+4289.26ms (cpu 4275.29ms)   | decode as datasource_pbf: bench/multi_line_13_1310_3166.vector.pbf
+
+
+
+commenting filter.pass/geometry::envelope in datasource_pbf
+
+$ .//build/Release/vtile-decode bench/enf.t5yd5cdi_14_13089_8506.vector.pbf 14 13089 8506 200
+z:14 x:13089 y:8506 iterations:200
+2305.45ms (cpu 2301.07ms)   | decode as datasource_pbf: bench/enf.t5yd5cdi_14_13089_8506.vector.pbf
+2142.56ms (cpu 2140.40ms)   | decode as datasource: bench/enf.t5yd5cdi_14_13089_8506.vector.pbf
+processed 6800 features
+
+
+with bbox filter:
+
+$ .//build/Release/vtile-decode bench/enf.t5yd5cdi_14_13089_8506.vector.pbf 14 13089 8506 200
+z:14 x:13089 y:8506 iterations:200
+2497.01ms (cpu 2493.40ms)   | decode as datasource_pbf: bench/enf.t5yd5cdi_14_13089_8506.vector.pbf
+1753.55ms (cpu 1751.10ms)   | decode as datasource: bench/enf.t5yd5cdi_14_13089_8506.vector.pbf
+processed 6600 features
+
+$ ./build/Release/vtile-decode bench/multi_line_13_1310_3166.vector.pbf 13 1310 3166 100
+z:13 x:1310 y:3166 iterations:100
+message: zlib compressed
+4090.67ms (cpu 4083.65ms)   | decode as datasource_pbf: bench/multi_line_13_1310_3166.vector.pbf
diff --git a/bench/vtile-decode.cpp b/bench/vtile-decode.cpp
new file mode 100644
index 0000000..7dafbe5
--- /dev/null
+++ b/bench/vtile-decode.cpp
@@ -0,0 +1,127 @@
+#include <mapnik/timer.hpp>
+#include <mapnik/util/file_io.hpp>
+#include "vector_tile_datasource_pbf.hpp"
+#include "vector_tile_datasource.hpp"
+#include "vector_tile_compression.hpp"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#include "vector_tile.pb.h"
+#pragma GCC diagnostic pop
+
+#include <iostream>
+
+#include <protozero/pbf_reader.hpp>
+
+int main(int argc, char** argv)
+{
+    try
+    {
+
+        if (argc < 4)
+        {
+            std::clog << "usage: vtile-decode /path/to/tile.vector.pbf z x y [iterations]\n";
+            return -1;
+        }
+        std::string vtile(argv[1]);
+        mapnik::util::file input(vtile);
+        if (!input.open())
+        {
+            std::clog << std::string("failed to open ") + vtile << "\n";
+            return -1;
+        }
+
+        int z = std::stoi(argv[2]);
+        int x = std::stoi(argv[3]);
+        int y = std::stoi(argv[4]);
+
+        std::size_t iterations = 100;
+        if (argc > 5)
+        {
+            iterations = std::stoi(argv[5]);
+        }
+
+        std::clog << "z:" << z << " x:" << x << " y:" << y <<  " iterations:" << iterations << "\n";
+
+        std::string message(input.data().get(), input.size());
+        bool is_zlib = mapnik::vector_tile_impl::is_zlib_compressed(message);
+        bool is_gzip = mapnik::vector_tile_impl::is_gzip_compressed(message);
+        if (is_zlib || is_gzip)
+        {
+            if (is_zlib)
+            {
+                std::cout << "message: zlib compressed\n";
+            }
+            else if (is_gzip)
+            {
+                std::cout << "message: gzip compressed\n";
+            }
+            std::string uncompressed;
+            mapnik::vector_tile_impl::zlib_decompress(message,uncompressed);
+            message = uncompressed;
+        }
+
+        std::size_t feature_count = 0;
+        std::size_t layer_count = 0;
+        {
+            mapnik::progress_timer __stats__(std::clog, std::string("decode as datasource_pbf: ") + vtile);
+            for (std::size_t i=0;i<iterations;++i)
+            {
+                protozero::pbf_reader tile(message);
+                while (tile.next()) {
+                    if (tile.tag() == 3) {
+                        ++layer_count;
+                        protozero::pbf_reader layer = tile.get_message();
+                        auto ds = std::make_shared<mapnik::vector_tile_impl::tile_datasource_pbf>(layer,x,y,z,256);
+                        mapnik::query q(ds->get_tile_extent());
+                        auto fs = ds->features(q);
+                        while (fs->next()) {
+                            ++feature_count;
+                        }
+                    } else {
+                        tile.skip();
+                    }
+                }
+            }
+        }
+
+
+        std::size_t feature_count2 = 0;
+        std::size_t layer_count2 = 0;
+        {
+            mapnik::progress_timer __stats__(std::clog, std::string("decode as datasource: ") + vtile);
+            vector_tile::Tile tiledata;
+            tiledata.ParseFromString(message);
+            for (std::size_t i=0;i<iterations;++i)
+            {
+                for (int j=0;j<tiledata.layers_size(); ++j)
+                {
+                    ++layer_count2;
+                    auto const& layer = tiledata.layers(j);
+                    auto ds = std::make_shared<mapnik::vector_tile_impl::tile_datasource>(layer,x,y,z,256);
+                    mapnik::query q(ds->get_tile_extent());
+                    auto fs = ds->features(q);
+                    while (fs->next()) {
+                        ++feature_count2;
+                    }                    
+                }
+            }
+        }
+        if (feature_count!= feature_count2) {
+            std::clog << "error: tile datasource impl did not return same # of features " << feature_count << " vs " << feature_count2 << "\n";
+            return -1;
+        } else if (feature_count == 0) {
+            std::clog << "error: no features processed\n";
+            return -1;
+        } else {
+            std::clog << "processed " << feature_count << " features\n";
+        }
+    }
+    catch (std::exception const& ex)
+    {
+        std::clog << "error: " << ex.what() << "\n";
+        return -1;
+    }
+    return 0;
+}
diff --git a/gyp/build.gyp b/gyp/build.gyp
index a9d8a5f..1bfccf6 100644
--- a/gyp/build.gyp
+++ b/gyp/build.gyp
@@ -161,6 +161,22 @@
       ]
     },
     {
+      "target_name": "vtile-decode",
+      'dependencies': [ 'mapnik_vector_tile_impl' ],
+      "type": "executable",
+      "defines": [
+        "<@(common_defines)",
+        "MAPNIK_PLUGINDIR=<(MAPNIK_PLUGINDIR)"
+      ],
+      "sources": [
+        "../bench/vtile-decode.cpp"
+      ],
+      "include_dirs": [
+        "../src",
+        '../deps/protozero/include'
+      ]
+    },
+    {
       "target_name": "tileinfo",
       'dependencies': [ 'vector_tile' ],
       "type": "executable",
diff --git a/package.json b/package.json
index fb3cda9..b0b90a6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "mapnik-vector-tile",
-    "version": "0.9.1",
+    "version": "0.9.2",
     "description": "Mapnik vector tile API",
     "main": "./package.json",
     "repository"   :  {
diff --git a/scripts/coverage.sh b/scripts/coverage.sh
index 3d0e223..ff4e494 100755
--- a/scripts/coverage.sh
+++ b/scripts/coverage.sh
@@ -15,8 +15,10 @@ if [[ ${COVERAGE} == true ]]; then
     ./mason_packages/.link/bin/cpp-coveralls \
         --build-root build \
         --gcov-options '\-lp' \
-        --exclude test/catch.hpp \
         --exclude examples \
+        --exclude deps \
+        --exclude test \
+        --exclude bench \
         --exclude build/Debug/obj/gen/ \
         --exclude mason_packages \
         --exclude scripts > /dev/null
diff --git a/src/vector_tile_datasource.ipp b/src/vector_tile_datasource.ipp
index 994a46a..aa35119 100644
--- a/src/vector_tile_datasource.ipp
+++ b/src/vector_tile_datasource.ipp
@@ -35,6 +35,10 @@
 #include <boost/optional.hpp>
 #include <unicode/unistr.h>
 
+#if defined(DEBUG)
+#include <mapnik/debug.hpp>
+#endif
+
 namespace mapnik { namespace vector_tile_impl {
 
     void add_attributes(mapnik::feature_ptr feature,
@@ -196,16 +200,19 @@ namespace mapnik { namespace vector_tile_impl {
                     continue;
                 }
                 mapnik::vector_tile_impl::Geometry geoms(f,tile_x_, tile_y_, scale_, -1*scale_);
-                mapnik::geometry::geometry<double> geom = decode_geometry(geoms, f.type());
+                mapnik::geometry::geometry<double> geom = decode_geometry(geoms, f.type(), filter_.box_);
                 if (geom.is<mapnik::geometry::geometry_empty>())
                 {
                     continue;
                 }
+                #if defined(DEBUG)
                 mapnik::box2d<double> envelope = mapnik::geometry::envelope(geom);
                 if (!filter_.pass(envelope))
                 {
+                    MAPNIK_LOG_ERROR(tile_datasource_pbf) << "tile_datasource: filter:pass should not get here";
                     continue;
                 }
+                #endif
                 mapnik::feature_ptr feature = mapnik::feature_factory::create(ctx_,feature_id);
                 feature->set_geometry(std::move(geom));
                 add_attributes(feature,f,layer_,tr_);
diff --git a/src/vector_tile_datasource_pbf.ipp b/src/vector_tile_datasource_pbf.ipp
index 65ef644..3bc5a7a 100644
--- a/src/vector_tile_datasource_pbf.ipp
+++ b/src/vector_tile_datasource_pbf.ipp
@@ -32,6 +32,10 @@
 #include <boost/optional.hpp>
 #include <unicode/unistr.h>
 
+#if defined(DEBUG)
+#include <mapnik/debug.hpp>
+#endif
+
 namespace mapnik { namespace vector_tile_impl {
 
     template <typename Filter>
@@ -210,16 +214,19 @@ namespace mapnik { namespace vector_tile_impl {
                             {
                                 auto geom_itr = f.get_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);
+                                mapnik::geometry::geometry<double> geom = decode_geometry(geoms, geometry_type, filter_.box_);
                                 if (geom.is<mapnik::geometry::geometry_empty>())
                                 {
                                     continue;
                                 }
+                                #if defined(DEBUG)
                                 mapnik::box2d<double> envelope = mapnik::geometry::envelope(geom);
                                 if (!filter_.pass(envelope))
                                 {
+                                    MAPNIK_LOG_ERROR(tile_datasource_pbf) << "tile_datasource_pbf: filter:pass should not get here";
                                     continue;
                                 }
+                                #endif
                                 feature->set_geometry(std::move(geom));
                                 return feature;
                             }
diff --git a/src/vector_tile_geometry_decoder.hpp b/src/vector_tile_geometry_decoder.hpp
index 1de0441..e577014 100644
--- a/src/vector_tile_geometry_decoder.hpp
+++ b/src/vector_tile_geometry_decoder.hpp
@@ -13,6 +13,10 @@
 //std
 #include <algorithm>
 
+#if defined(DEBUG)
+#include <mapnik/debug.hpp>
+#endif
+
 namespace mapnik { namespace vector_tile_impl {
 
 // NOTE: this object is for one-time use.  Once you've progressed to the end
@@ -31,7 +35,7 @@ public:
             close = 7
             };
 
-    inline command next(double& rx, double& ry);
+    inline command next(double& rx, double& ry, std::uint32_t & len);
 
 private:
     vector_tile::Tile_Feature const& f_;
@@ -62,14 +66,14 @@ public:
         close = 7
     };
 
-    inline command next(double& rx, double& ry);
+    inline command next(double& rx, double& ry, std::uint32_t & len);
 
 private:
     std::pair< protozero::pbf_reader::const_uint32_iterator, protozero::pbf_reader::const_uint32_iterator > geo_iterator_;
     double scale_x_;
     double scale_y_;
     uint8_t cmd;
-    uint32_t length;
+    std::uint32_t length;
     double x, y;
     double ox, oy;
 };
@@ -87,7 +91,7 @@ Geometry::Geometry(vector_tile::Tile_Feature const& f,
       x(tile_x), y(tile_y),
       ox(0), oy(0) {}
 
-Geometry::command Geometry::next(double& rx, double& ry)
+Geometry::command Geometry::next(double& rx, double& ry, std::uint32_t & len)
 {
     if (k < geoms_)
     {
@@ -95,7 +99,7 @@ Geometry::command Geometry::next(double& rx, double& ry)
         {
             uint32_t cmd_length = static_cast<uint32_t>(f_.geometry(k++));
             cmd = cmd_length & 0x7;
-            length = cmd_length >> 3;
+            len = length = cmd_length >> 3;
         }
 
         --length;
@@ -145,7 +149,7 @@ GeometryPBF::GeometryPBF(std::pair<protozero::pbf_reader::const_uint32_iterator,
       x(tile_x), y(tile_y),
       ox(0), oy(0) {}
 
-GeometryPBF::command GeometryPBF::next(double& rx, double& ry)
+GeometryPBF::command GeometryPBF::next(double& rx, double& ry, std::uint32_t & len)
 {
     if (geo_iterator_.first != geo_iterator_.second)
     {
@@ -153,7 +157,7 @@ GeometryPBF::command GeometryPBF::next(double& rx, double& ry)
         {
             uint32_t cmd_length = static_cast<uint32_t>(*geo_iterator_.first++);
             cmd = cmd_length & 0x7;
-            length = cmd_length >> 3;
+            len = length = cmd_length >> 3;
         }
 
         --length;
@@ -200,17 +204,43 @@ GeometryPBF::command GeometryPBF::next(double& rx, double& ry)
 namespace detail {
 
 template <typename T>
-void decode_point(mapnik::geometry::geometry<double> & geom, T & paths)
+void decode_point(mapnik::geometry::geometry<double> & geom, T & paths, mapnik::box2d<double> const& bbox)
 {
     typename T::command cmd;
     double x1, y1;
     mapnik::geometry::multi_point<double> mp;
-    while ((cmd = paths.next(x1, y1)) != T::end)
+    bool first = true;
+    std::uint32_t len;
+    while ((cmd = paths.next(x1, y1, len)) != T::end)
     {
-        mp.emplace_back(mapnik::geometry::point<double>(x1,y1));
+        // TODO: consider profiling and trying to optimize this further
+        // when all points are within the bbox filter then the `mp.reserve` should be
+        // perfect, but when some points are thrown out we will allocate more than needed
+        // the "all points intersect" case I think is going to be more common/important
+        // however worth a future look to see if the "some or few points intersect" can be optimized
+        if (first)
+        {
+            first = false;
+            mp.reserve(len);
+        }
+        if (!bbox.intersects(x1,y1))
+        {
+            continue;
+        }
+        mp.emplace_back(x1,y1);
     }
     std::size_t num_points = mp.size();
-    if (num_points == 1)
+    #if defined(DEBUG)
+    if (num_points > 0 && len != num_points) {
+        // BUG: https://github.com/mapbox/mapnik-vector-tile/issues/144
+        MAPNIK_LOG_ERROR(decode_point) << "warning: encountered incorrectly encoded multipoint with " << num_points << " points but only " << len << " repeated commands";
+    }
+    #endif
+    if (num_points == 0)
+    {
+        geom = mapnik::geometry::geometry_empty();
+    }
+    else if (num_points == 1)
     {
         geom = std::move(mp[0]);
     }
@@ -222,24 +252,71 @@ void decode_point(mapnik::geometry::geometry<double> & geom, T & paths)
 }
 
 template <typename T>
-void decode_linestring(mapnik::geometry::geometry<double> & geom, T & paths)
+void decode_linestring(mapnik::geometry::geometry<double> & geom, T & paths, mapnik::box2d<double> const& bbox)
 {
     typename T::command cmd;
     double x1, y1;
     mapnik::geometry::multi_line_string<double> multi_line;
     multi_line.emplace_back();
     bool first = true;
-    while ((cmd = paths.next(x1, y1)) != T::end)
+    bool first_line_to = true;
+    std::uint32_t len;
+    #if defined(DEBUG)
+    std::uint32_t pre_len;
+    #endif
+    mapnik::box2d<double> part_env;
+    while ((cmd = paths.next(x1, y1, len)) != T::end)
     {
         if (cmd == T::move_to)
         {
-            if (first) first = false;
-            else multi_line.emplace_back();
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                #if defined(DEBUG)
+                if (multi_line.back().size() > 0 && pre_len != multi_line.back().size())
+                {
+                    MAPNIK_LOG_ERROR(decode_linestring) << "warning: encountered incorrectly encoded line with " << multi_line.back().size() << " points but only " << pre_len << " repeated commands";
+                }
+                #endif
+                first_line_to = true;
+                if (!bbox.intersects(part_env))
+                {
+                    // remove last line
+                    multi_line.pop_back();
+                }
+                // add fresh line to start adding to
+                multi_line.emplace_back();
+            }
+            part_env.init(x1,y1,x1,y1);
+        }
+        else if (first_line_to && cmd == T::line_to)
+        {
+            first_line_to = false;
+            multi_line.back().reserve(len+1);
+            #if defined(DEBUG)
+            pre_len = len+1;
+            #endif
+        }
+        if (!first)
+        {
+            part_env.expand_to_include(x1,y1);
         }
         multi_line.back().add_coord(x1,y1);
     }
+    if (!bbox.intersects(part_env))
+    {
+        // remove last line
+        multi_line.pop_back();
+    }
     std::size_t num_lines = multi_line.size();
-    if (num_lines == 1)
+    if (num_lines == 0)
+    {
+        geom = mapnik::geometry::geometry_empty();
+    }
+    else if (num_lines == 1)
     {
         auto itr = std::make_move_iterator(multi_line.begin());
         if (itr->size() > 1)
@@ -254,22 +331,56 @@ void decode_linestring(mapnik::geometry::geometry<double> & geom, T & paths)
 }
 
 template <typename T>
-std::vector<mapnik::geometry::linear_ring<double>> read_rings(T & paths)
+void read_rings(std::vector<mapnik::geometry::linear_ring<double>> & rings,
+                T & paths, mapnik::box2d<double> const& bbox)
 {
     typename T::command cmd;
     double x1, y1;
-    std::vector<mapnik::geometry::linear_ring<double>> rings;
     rings.emplace_back();
     double x2,y2;
     bool first = true;
-    while ((cmd = paths.next(x1, y1)) != T::end)
+    bool first_line_to = true;
+    std::uint32_t len;
+    #if defined(DEBUG)
+    std::uint32_t pre_len;
+    #endif
+    mapnik::box2d<double> part_env;
+    while ((cmd = paths.next(x1, y1, len)) != T::end)
     {
         if (cmd == T::move_to)
         {
             x2 = x1;
             y2 = y1;
-            if (first) first = false;
-            else rings.emplace_back();
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                #if defined(DEBUG)
+                // off by one is expected/okay in rare cases
+                if (rings.back().size() > 0 && (rings.back().size() > pre_len || std::fabs(pre_len - rings.back().size()) > 1))
+                {
+                    MAPNIK_LOG_ERROR(read_rings) << "warning: encountered incorrectly encoded ring with " << rings.back().size() << " points but " << pre_len << " repeated commands";
+                }
+                #endif
+                first_line_to = true;
+                if (!bbox.intersects(part_env))
+                {
+                    // remove last ring
+                    rings.pop_back();
+                }
+                rings.emplace_back();
+            }
+            part_env.init(x1,y1,x1,y1);
+        }
+        else if (first_line_to && cmd == T::line_to)
+        {
+            first_line_to = false;
+            rings.back().reserve(len+2);
+            #if defined(DEBUG)
+            pre_len = len+2;
+            #endif
         }
         else if (cmd == T::close)
         {
@@ -280,9 +391,17 @@ std::vector<mapnik::geometry::linear_ring<double>> read_rings(T & paths)
             }
             continue;
         }
+        if (!first)
+        {
+            part_env.expand_to_include(x1,y1);
+        }
         rings.back().add_coord(x1,y1);
     }
-    return rings;
+    if (!bbox.intersects(part_env))
+    {
+        // remove last ring
+        rings.pop_back();
+    }
 }
 
 template <typename T>
@@ -291,7 +410,11 @@ void decode_polygons(mapnik::geometry::geometry<double> & geom, T && rings)
     auto rings_itr = std::make_move_iterator(rings.begin());
     auto rings_end = std::make_move_iterator(rings.end());
     std::size_t num_rings = rings.size();
-    if (num_rings == 1)
+    if (num_rings == 0)
+    {
+        geom = mapnik::geometry::geometry_empty();
+    }
+    else if (num_rings == 1)
     {
         if (rings_itr->size() < 4) return;
         if (mapnik::util::is_clockwise(*rings_itr))
@@ -367,25 +490,33 @@ void decode_polygons(mapnik::geometry::geometry<double> & geom, T && rings)
 } // ns detail
 
 template <typename T>
-inline mapnik::geometry::geometry<double> decode_geometry(T & paths, int32_t geom_type)
+inline mapnik::geometry::geometry<double> decode_geometry(T & paths, int32_t geom_type, mapnik::box2d<double> const& bbox)
 {
     mapnik::geometry::geometry<double> geom; // output geometry
     switch (geom_type)
     {
     case vector_tile::Tile_GeomType_POINT:
     {
-        detail::decode_point(geom, paths);
+        detail::decode_point(geom, paths, bbox);
         break;
     }
     case vector_tile::Tile_GeomType_LINESTRING:
     {
-        detail::decode_linestring(geom, paths);
+        detail::decode_linestring(geom, paths, bbox);
         break;
     }
     case vector_tile::Tile_GeomType_POLYGON:
     {
-        auto rings = detail::read_rings(paths);
-        detail::decode_polygons(geom, rings);
+        std::vector<mapnik::geometry::linear_ring<double>> rings;
+        detail::read_rings(rings, paths, bbox);
+        if (rings.empty())
+        {
+            geom = mapnik::geometry::geometry_empty();
+        }
+        else
+        {
+            detail::decode_polygons(geom, rings);
+        }
         break;
     }
     case vector_tile::Tile_GeomType_UNKNOWN:
@@ -398,6 +529,19 @@ inline mapnik::geometry::geometry<double> decode_geometry(T & paths, int32_t geo
     return geom;
 }
 
+// For back compatibility in tests / for cases where performance is not critical
+// TODO: consider removing and always requiring bbox arg
+template <typename T>
+inline mapnik::geometry::geometry<double> decode_geometry(T & paths, int32_t geom_type)
+{
+
+    mapnik::box2d<double> bbox(-std::numeric_limits<double>::max(),
+                               -std::numeric_limits<double>::max(),
+                               std::numeric_limits<double>::max(),
+                               std::numeric_limits<double>::max());
+    return decode_geometry(paths,geom_type,bbox);
+}
+
 }} // end ns
 
 
diff --git a/src/vector_tile_geometry_encoder.hpp b/src/vector_tile_geometry_encoder.hpp
index 4a2c71d..ea6edd1 100644
--- a/src/vector_tile_geometry_encoder.hpp
+++ b/src/vector_tile_geometry_encoder.hpp
@@ -140,22 +140,71 @@ inline unsigned encode_geometry(mapnik::geometry::linear_ring<std::int64_t> cons
 
 inline unsigned encode_geometry(mapnik::geometry::polygon<std::int64_t> const& poly,
                                 vector_tile::Tile_Feature & current_feature,
-                                int32_t & x_,
-                                int32_t & y_)
+                                int32_t & start_x,
+                                int32_t & start_y)
 {
     unsigned count = 0;
-    count += encode_geometry(poly.exterior_ring, current_feature, x_, y_);
+    count += encode_geometry(poly.exterior_ring, current_feature, start_x, start_y);
     if (count == 0)
     {
         return count;
     }
     for (auto const& ring : poly.interior_rings)
     {
-        count += encode_geometry(ring, current_feature, x_, y_);
+        count += encode_geometry(ring, current_feature, start_x, start_y);
     }
     return count;
 }
 
+inline unsigned encode_geometry(mapnik::geometry::multi_point<std::int64_t> const& geom,
+                                vector_tile::Tile_Feature & current_feature,
+                                int32_t & start_x,
+                                int32_t & start_y)
+{
+    std::size_t geom_size = geom.size();
+    if (geom_size <= 0)
+    {
+        return 0;
+    }
+    current_feature.add_geometry(1u | (geom_size << 3)); // move_to | (len << 3)
+    for (auto const& pt : geom)
+    {
+        int32_t dx = pt.x - start_x;
+        int32_t dy = pt.y - start_y;
+        // Manual zigzag encoding.
+        current_feature.add_geometry(protozero::encode_zigzag32(dx));
+        current_feature.add_geometry(protozero::encode_zigzag32(dy));
+        start_x = pt.x;
+        start_y = pt.y;
+    }
+    return geom_size;
+}
+
+inline unsigned encode_geometry(mapnik::geometry::multi_line_string<std::int64_t> const& geom,
+                                vector_tile::Tile_Feature & current_feature,
+                                int32_t & start_x,
+                                int32_t & start_y)
+{
+    unsigned count = 0;
+    for (auto const& poly : geom)
+    {
+        count += encode_geometry(poly, current_feature, start_x, start_y);
+    }
+    return count;
+}
+
+inline unsigned encode_geometry(mapnik::geometry::multi_polygon<std::int64_t> const& geom,
+                                vector_tile::Tile_Feature & current_feature,
+                                int32_t & start_x,
+                                int32_t & start_y)
+{
+    unsigned count = 0;
+    for (auto const& poly : geom)
+    {
+        count += encode_geometry(poly, current_feature, start_x, start_y);
+    }
+    return count;
+}
 
 }} // end ns
 
diff --git a/src/vector_tile_processor.ipp b/src/vector_tile_processor.ipp
index bf820e4..08554fb 100644
--- a/src/vector_tile_processor.ipp
+++ b/src/vector_tile_processor.ipp
@@ -924,19 +924,11 @@ struct encoder_visitor {
     unsigned operator() (mapnik::geometry::multi_point<std::int64_t> const& geom)
     {
         unsigned path_count = 0;
-        bool first = true;
-        for (auto const& pt : geom)
-        {
-            if (first)
-            {
-                first = false;
-                backend_.start_tile_feature(feature_);
-                backend_.current_feature_->set_type(vector_tile::Tile_GeomType_POINT);
-            }
-            path_count += backend_.add_path(pt);
-        }
-        if (!first)
+        if (!geom.empty())
         {
+            backend_.start_tile_feature(feature_);
+            backend_.current_feature_->set_type(vector_tile::Tile_GeomType_POINT);
+            path_count = backend_.add_path(geom);
             backend_.stop_tile_feature();
         }
         return path_count;
diff --git a/src/vector_tile_util.ipp b/src/vector_tile_util.ipp
index 4f5976c..b877af1 100644
--- a/src/vector_tile_util.ipp
+++ b/src/vector_tile_util.ipp
@@ -207,7 +207,8 @@ namespace mapnik { namespace vector_tile_impl {
                             double x0, y0, x1, y1;
                             mapnik::box2d<int> box;
                             bool first = true;
-                            while ((cmd = paths.next(x1, y1)) != mapnik::vector_tile_impl::GeometryPBF::end)
+                            std::uint32_t len;
+                            while ((cmd = paths.next(x1, y1, len)) != mapnik::vector_tile_impl::GeometryPBF::end)
                             {
                                 if (cmd == mapnik::vector_tile_impl::GeometryPBF::move_to || cmd == mapnik::vector_tile_impl::GeometryPBF::line_to)
                                 {
diff --git a/test/encoding_util.hpp b/test/encoding_util.hpp
index 98b9fda..98e0466 100644
--- a/test/encoding_util.hpp
+++ b/test/encoding_util.hpp
@@ -29,59 +29,6 @@ struct print
 
 }
 
-struct encode_geometry
-{
-    vector_tile::Tile_Feature & feature_;
-    int32_t x_;
-    int32_t y_;
-    encode_geometry(vector_tile::Tile_Feature & feature) :
-      feature_(feature),
-      x_(0),
-      y_(0) { }
-
-    void operator() (geometry_empty const&)
-    {
-    }
-    
-    template <typename T>
-    void operator()(T const& path)
-    {
-        mapnik::vector_tile_impl::encode_geometry(path,feature_,x_,y_);
-    }
-    
-    void operator()(mapnik::geometry::multi_point<std::int64_t> const & path)
-    {
-        for (auto const& pt : path)
-        {
-            mapnik::vector_tile_impl::encode_geometry(pt,feature_,x_,y_);
-        }
-    }
-
-    void operator()(mapnik::geometry::multi_line_string<std::int64_t> const & path)
-    {
-        for (auto const& ls : path)
-        {
-            mapnik::vector_tile_impl::encode_geometry(ls,feature_,x_,y_);
-        }
-    }
-    
-    void operator()(mapnik::geometry::multi_polygon<std::int64_t> const& path)
-    {
-        for (auto const& p : path)
-        {
-            mapnik::vector_tile_impl::encode_geometry(p,feature_,x_,y_);
-        }
-    }
-    
-    void operator()(mapnik::geometry::geometry_collection<std::int64_t> const& path)
-    {
-        for (auto const& p : path)
-        {
-            mapnik::util::apply_visitor((*this), p);
-        }
-    }
-};
-
 struct show_path
 {
     std::string & str_;
@@ -111,20 +58,47 @@ struct show_path
     }
 };
 
-template <typename T>
-vector_tile::Tile_Feature geometry_to_feature(mapnik::geometry::geometry<T> const& g)
+struct encoder_visitor
+{
+    vector_tile::Tile_Feature & feature_;
+    int32_t x_;
+    int32_t y_;
+    encoder_visitor(vector_tile::Tile_Feature & feature) :
+      feature_(feature),
+      x_(0),
+      y_(0) { }
+
+    void operator() (geometry_empty const&)
+    {
+    }
+
+    void operator()(mapnik::geometry::geometry_collection<std::int64_t> const& path)
+    {
+        for (auto const& p : path)
+        {
+            mapnik::util::apply_visitor((*this), p);
+        }
+    }
+
+    template <typename T>
+    void operator()(T const& geom)
+    {
+        mapnik::vector_tile_impl::encode_geometry(geom,feature_,x_,y_);
+    }
+};
+
+vector_tile::Tile_Feature geometry_to_feature(mapnik::geometry::geometry<std::int64_t> const& g)
 {
     vector_tile::Tile_Feature feature;
-    encode_geometry ap(feature);
-    if (g.template is<mapnik::geometry::point<T> >() || g.template is<mapnik::geometry::multi_point<T> >())
+    if (g.template is<mapnik::geometry::point<std::int64_t>>() || g.template is<mapnik::geometry::multi_point<std::int64_t>>())
     {
         feature.set_type(vector_tile::Tile_GeomType_POINT);
     }
-    else if (g.template is<mapnik::geometry::line_string<T> >() || g.template is<mapnik::geometry::multi_line_string<T> >())
+    else if (g.template is<mapnik::geometry::line_string<std::int64_t>>() || g.template is<mapnik::geometry::multi_line_string<std::int64_t>>())
     {
         feature.set_type(vector_tile::Tile_GeomType_LINESTRING);
     }
-    else if (g.template is<mapnik::geometry::polygon<T> >() || g.template is<mapnik::geometry::multi_polygon<T> >())
+    else if (g.template is<mapnik::geometry::polygon<std::int64_t>>() || g.template is<mapnik::geometry::multi_polygon<std::int64_t>>())
     {
         feature.set_type(vector_tile::Tile_GeomType_POLYGON);
     }
@@ -132,6 +106,7 @@ vector_tile::Tile_Feature geometry_to_feature(mapnik::geometry::geometry<T> cons
     {
         throw std::runtime_error("could not detect valid geometry type");
     }
+    encoder_visitor ap(feature);
     mapnik::util::apply_visitor(ap,g);
     return feature;
 }
@@ -147,8 +122,7 @@ std::string decode_to_path_string(mapnik::geometry::geometry<T> const& g)
     return out;
 }
 
-template <typename T>
-std::string compare(mapnik::geometry::geometry<T> const& g)
+std::string compare(mapnik::geometry::geometry<std::int64_t> const& g)
 {
     vector_tile::Tile_Feature feature = geometry_to_feature(g);
     mapnik::vector_tile_impl::Geometry geoms(feature,0.0,0.0,1.0,1.0);
diff --git a/test/geometry_encoding.cpp b/test/geometry_encoding.cpp
index 4520f0e..948fb09 100644
--- a/test/geometry_encoding.cpp
+++ b/test/geometry_encoding.cpp
@@ -20,7 +20,7 @@ TEST_CASE( "point", "should round trip without changes" ) {
     std::string expected(
     "move_to(0,0)\n"
     );
-    CHECK(compare<std::int64_t>(g) == expected);
+    CHECK(compare(g) == expected);
 }
 
 TEST_CASE( "multi_point", "should round trip without changes" ) {
@@ -33,7 +33,7 @@ TEST_CASE( "multi_point", "should round trip without changes" ) {
     "move_to(1,1)\n"
     "move_to(2,2)\n"
     );
-    CHECK(compare<std::int64_t>(g) == expected);
+    CHECK(compare(g) == expected);
 }
 
 TEST_CASE( "line_string", "should round trip without changes" ) {
@@ -46,7 +46,7 @@ TEST_CASE( "line_string", "should round trip without changes" ) {
     "line_to(1,1)\n"
     "line_to(100,100)\n"
     );
-    CHECK(compare<std::int64_t>(g) == expected);
+    CHECK(compare(g) == expected);
 }
 
 TEST_CASE( "multi_line_string", "should round trip without changes" ) {
@@ -73,7 +73,7 @@ TEST_CASE( "multi_line_string", "should round trip without changes" ) {
     "line_to(-20,-20)\n"
     "line_to(-100,-100)\n"
     );
-    CHECK(compare<std::int64_t>(g) == expected);
+    CHECK(compare(g) == expected);
 }
 
 /*TEST_CASE( "degenerate line_string", "should be culled" ) {
@@ -86,7 +86,7 @@ TEST_CASE( "multi_line_string", "should round trip without changes" ) {
     std::string expected_wkt0("LINESTRING(10 10)");
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(line);
+    vector_tile::Tile_Feature feature = geometry_to_feature(line);
     CHECK( feature.geometry_size() == 0 );
     auto geom = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
     CHECK( geom.is<mapnik::geometry::geometry_empty>() );
@@ -109,7 +109,7 @@ TEST_CASE( "multi_line_string with degenerate first part", "should be culled" )
     std::string expected_wkt0("MULTILINESTRING((0 0),(2 2,3 3))");
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(g);
+    vector_tile::Tile_Feature feature = geometry_to_feature(g);
     CHECK( feature.geometry_size() == 6 );
     auto geom = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
 
@@ -140,7 +140,7 @@ TEST_CASE( "multi_line_string with degenerate first part", "should be culled" )
     std::string expected_wkt0("MULTILINESTRING((0 0,1 1,100 100),(-10 -10))");
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(g);
+    vector_tile::Tile_Feature feature = geometry_to_feature(g);
     CHECK( feature.type() == vector_tile::Tile_GeomType_LINESTRING );
     CHECK( feature.geometry_size() == 8 );
     auto geom = mapnik::vector_tile_impl::decode_geometry(feature,0.0,0.0,1.0,1.0);
@@ -163,7 +163,7 @@ TEST_CASE( "polygon", "should round trip without changes" ) {
     "line_to(100,100)\n"
     "close_path(0,0)\n"
     );
-    CHECK(compare<std::int64_t>(g) == expected);
+    CHECK(compare(g) == expected);
 }
 
 TEST_CASE( "polygon with degenerate exterior ring ", "should be culled" ) {
@@ -178,7 +178,7 @@ TEST_CASE( "polygon with degenerate exterior ring ", "should be culled" ) {
     std::string expected_wkt0("POLYGON((0 0,0 10))");
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
+    vector_tile::Tile_Feature feature = geometry_to_feature(p0);
     // since first ring is degenerate the whole polygon should be culled
     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());
@@ -205,7 +205,7 @@ TEST_CASE( "polygon with degenerate exterior ring ", "should be culled" ) {
     std::string expected_wkt0("POLYGON((0 0,0 10),(-7 7,-3 7,-3 3,-7 3,-7 7))");
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
+    vector_tile::Tile_Feature feature = geometry_to_feature(p0);
     // since first ring is degenerate the whole polygon should be culled
     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());
@@ -231,7 +231,7 @@ TEST_CASE( "polygon with valid exterior ring but degenerate interior ring", "sho
     std::string expected_wkt0("POLYGON((0 0,0 10,-10 10,-10 0,0 0),(-7 7,-3 7))");
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
+    vector_tile::Tile_Feature feature = geometry_to_feature(p0);
     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> >() );
@@ -272,7 +272,7 @@ TEST_CASE( "polygon with valid exterior ring but one degenerate interior ring of
     std::string expected_wkt0("POLYGON((0 0,0 10,-10 10,-10 0,0 0),(-7 7,-3 7),(-6 4,-6 6,-4 6,-4 4,-6 4))");
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
+    vector_tile::Tile_Feature feature = geometry_to_feature(p0);
     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> >() );
@@ -306,7 +306,7 @@ TEST_CASE( "Polygon with de-generate ring(s)", "should skip invalid ring(s)" )
     std::string expected_wkt1("POLYGON((-7 7,-7 3,-3 3,-3 7,-7 7))");
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
+    vector_tile::Tile_Feature feature = geometry_to_feature(p0);
     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> >() );
@@ -346,7 +346,7 @@ TEST_CASE( "(multi)polygon with hole", "should round trip without changes" ) {
     CHECK( mapnik::util::to_wkt(wkt0,mapnik::geometry::geometry<std::int64_t>(p0)) );
     CHECK( wkt0 == expected_wkt0);
 
-    vector_tile::Tile_Feature feature = geometry_to_feature<std::int64_t>(p0);
+    vector_tile::Tile_Feature feature = geometry_to_feature(p0);
     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> >() );
@@ -373,7 +373,7 @@ TEST_CASE( "(multi)polygon with hole", "should round trip without changes" ) {
     "line_to(3,3)\n"
     "line_to(4,4)\n"
     );
-    CHECK( compare<std::int64_t>(g) == expected);
+    CHECK( compare(g) == expected);
 }*/
 
 /*
@@ -387,7 +387,7 @@ TEST_CASE( "test 2b", "should drop vertices" ) {
     "line_to(0,0)\n" // TODO - should we try to drop this?
     "line_to(1,1)\n"
     );
-    CHECK(compare<std::int64_t>(g) == expected);
+    CHECK(compare(g) == expected);
 }*/
 
 TEST_CASE( "test 3", "should not drop first move_to or last vertex in line" ) {
@@ -407,7 +407,7 @@ TEST_CASE( "test 3", "should not drop first move_to or last vertex in line" ) {
     "move_to(2,2)\n"
     "line_to(3,3)\n"
     );
-    CHECK(compare<std::int64_t>(g) == expected);
+    CHECK(compare(g) == expected);
 }
 
 /*

-- 
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