[mapnik-vector-tile] 04/14: Imported Upstream version 0.5.1+dfsg
Jérémy Lal
kapouer at moszumanska.debian.org
Wed Aug 20 21:26:20 UTC 2014
This is an automated email from the git hooks/post-receive script.
kapouer pushed a commit to branch master
in repository mapnik-vector-tile.
commit 16e0df8b187dd21d40d3326ac3828252b4298cb7
Author: Jérémy Lal <kapouer at melix.org>
Date: Wed Aug 20 19:03:09 2014 +0200
Imported Upstream version 0.5.1+dfsg
---
.gitignore | 5 +-
.travis.yml | 16 +-
CHANGELOG.md | 43 +++++
Makefile | 34 +++-
README.md | 46 +----
examples/c++/Makefile | 11 +-
examples/c++/README.md | 28 +++
examples/c++/tileinfo.cpp | 100 ++++++++++-
package.json | 5 +-
proto/vector_tile.proto | 3 +
src/hash_variant.hpp | 36 ----
src/mapnik3x_compatibility.hpp | 34 ++++
src/vector_tile_backend_pbf.hpp | 100 +++--------
src/vector_tile_datasource.hpp | 205 ++++++++++++---------
src/vector_tile_geometry_encoder.hpp | 191 ++++++++++++++++++++
src/vector_tile_processor.hpp | 153 +++++++++++++---
src/vector_tile_util.hpp | 29 ++-
test/encoding_util.hpp | 86 +++++++++
test/geometry_encoding.cpp | 338 +++++++++++++++++++++++++++++++++++
test/raster_style.xml | 7 +
test/raster_tile.cpp | 125 +++++++++++++
test/style.xml | 7 +
test/test_utils.hpp | 23 ++-
test/vector_tile.cpp | 257 ++++++++++++++++++++++----
24 files changed, 1549 insertions(+), 333 deletions(-)
diff --git a/.gitignore b/.gitignore
index f0fe95c..19f5f78 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,9 @@ src/vector_tile.pb.cc
src/vector_tile.pb.h
python/vector_tile_pb2.py
test/run-test
+test/run-geom-test
+test/run-raster-test
*pyc
archive
-TODO.md
\ No newline at end of file
+TODO.md
+examples/c++/tileinfo
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index a422bfc..04883b0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,22 +7,28 @@ compiler:
before_install:
- sudo apt-add-repository --yes ppa:mapnik/v2.2.0
- sudo apt-add-repository --yes ppa:mapnik/nightly-2.3
- - sudo apt-get update -qq
+ #- sudo apt-add-repository --yes ppa:mapnik/nightly-trunk
+ - sudo apt-get update -y
install:
- - sudo apt-get -qq install libprotobuf7 libprotobuf-dev protobuf-compiler
+ - sudo apt-get -y install libprotobuf7 libprotobuf-dev protobuf-compiler g++ gcc
+ #- sudo apt-get -y install gcc-4.8 g++-4.8
before_script:
- - sudo apt-get -qq install libmapnik=2.2.0* mapnik-utils=2.2.0* libmapnik-dev=2.2.0*
+ - sudo apt-get -y install libmapnik=2.2.0* mapnik-utils=2.2.0* libmapnik-dev=2.2.0*
- make clean
- make test
- sudo apt-get purge libmapnik=2.2.0* mapnik-utils=2.2.0* libmapnik-dev=2.2.0*
script:
- - sudo apt-get -qq install libmapnik=2.3.0* mapnik-utils=2.3.0* libmapnik-dev=2.3.0*
+ - sudo apt-get -y install libmapnik=2.3.0* mapnik-utils=2.3.0* libmapnik-dev=2.3.0* mapnik-input-plugin*=2.3.0*
- make clean
- make test
- - sudo apt-get purge libmapnik=2.3.0* mapnik-utils=2.3.0* libmapnik-dev=2.3.0*
+ #- sudo apt-get purge libmapnik=2.3.0* mapnik-utils=2.3.0* libmapnik-dev=2.3.0* mapnik-input-plugin*=2.3.0*
+ #- sudo apt-get -qq install libmapnik=3.0.0* mapnik-utils=3.0.0* libmapnik-dev=3.0.0* mapnik-input-plugin*=3.0.0*
+ #- if [ "${CXX}" = 'g++' ]; then export CXX="g++-4.8" && export CC="gcc-4.8"; fi;
+ #- make clean
+ #- make test
notifications:
irc:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60593b4..c22ad80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,50 @@
# Changlog
+## 0.5.1
+
+ - Minor build fixes
+
+## 0.5.0
+
+ - Experimental support for encoding images in vector tile features.
+ - Fixed potential hang if trying to render a feature without geometries
+
+## 0.4.2
+
+ - Additional optimizations and fixes to geometry encoding (#38 - avoid dropping vertex that forms horizontal or vertical right angle)
+
+## 0.4.1
+
+ - Added initial support for Mapnik 3.x
+
+## 0.4.0
+
+ - Refactored geometry encoder with fixes to drop duplicated/no-op verticies and/or
+ close commands
+ - `npm install` no longer runs `protoc` - the responsibility for this is now up to `node-mapnik`
+ - Optimized is_solid check
+
+## 0.3.7
+
+ - Add back protoc running to avoid unintended node-mapnik breakages with older versions.
+
+## 0.3.6
+
+ - Avoided 'Unknown command type (is_solid_extent): 0'
+
+## 0.3.5
+
+ - `npm install` no longer runs `protoc` - the responsibility for this is now up to `node-mapnik`
+ - Improved tile encoding: empty layers are no longer added
+ - All move_to commands and the last vertex in lines is no longer thrown out even with high `tolerance`
+ - Rolled back the change from v0.3.4 - multipart geometries are now again not decoded correctly, but this
+ needs to stay this way for performance reasons at the cost of correct marker/labeling placement on each
+ geometry part - long term solutions tracked at mapnik/mapnik#2151. Re-enabled to v0.3.4 behavior by setting
+ `-DEXPLODE_PARTS` in `CXXFLAGS`.
+
## 0.3.4
+ - Fixed tile_datasource geometry decoding such that it polygons are closed (for hit_test results)
- Fixed tile_datasource geometry decoding such that it respects multipart geometries (#19)
## 0.3.3
diff --git a/Makefile b/Makefile
index 33179c3..2b2203f 100755
--- a/Makefile
+++ b/Makefile
@@ -2,12 +2,17 @@ PROTOBUF_CXXFLAGS=$(shell pkg-config protobuf --cflags)
PROTOBUF_LDFLAGS=$(shell pkg-config protobuf --libs-only-L) -lprotobuf-lite
MAPNIK_CXXFLAGS=$(shell mapnik-config --cflags) -Wsign-compare
MAPNIK_LDFLAGS=$(shell mapnik-config --libs --ldflags --dep-libs)
-CXXFLAGS := $(CXXFLAGS) # inherit from env
-LDFLAGS := $(LDFLAGS) # inherit from env
+COMMON_FLAGS = -Wall -Wshadow -pedantic -Wno-c++11-long-long -Wno-c++11-extensions
+#-Wsign-compare -Wsign-conversion -Wunused-parameter
+# inherit from env
+CXX := $(CXX)
+CXXFLAGS := $(CXXFLAGS)
+LDFLAGS := $(LDFLAGS)
+MAPNIK_PLUGINDIR := $(shell mapnik-config --input-plugins)
all: mapnik-vector-tile
-mapnik-vector-tile: src/vector_tile.pb.cc
+mapnik-vector-tile: src/vector_tile.pb.cc Makefile
src/vector_tile.pb.cc: proto/vector_tile.proto
protoc -Iproto/ --cpp_out=./src proto/vector_tile.proto
@@ -17,11 +22,26 @@ python/vector_tile_pb2.py: proto/vector_tile.proto
python: python/vector_tile_pb2.py
-test/run-test: src/vector_tile.pb.cc test/vector_tile.cpp test/test_utils.hpp src/*
- $(CXX) -o ./test/run-test test/vector_tile.cpp src/vector_tile.pb.cc -I./src $(CXXFLAGS) $(MAPNIK_CXXFLAGS) $(PROTOBUF_CXXFLAGS) $(MAPNIK_LDFLAGS) $(PROTOBUF_LDFLAGS) $(LDFLAGS) -Wno-unused-private-field
+test/run-test: Makefile src/vector_tile.pb.cc test/vector_tile.cpp test/test_utils.hpp src/*
+ @$(CXX) -o ./test/run-test test/vector_tile.cpp src/vector_tile.pb.cc -I./src $(CXXFLAGS) $(MAPNIK_CXXFLAGS) $(PROTOBUF_CXXFLAGS) $(COMMON_FLAGS) $(MAPNIK_LDFLAGS) $(PROTOBUF_LDFLAGS) $(LDFLAGS) -Wno-unused-private-field
-test: test/run-test src/vector_tile.pb.cc
+test/test-cfg.h:
+ echo "#define MAPNIK_PLUGINDIR \"$(MAPNIK_PLUGINDIR)\"" > test/test-cfg.h
+
+test: test/test-cfg.h test/run-test test/run-geom-test ./test/run-raster-test src/vector_tile.pb.cc test/catch.hpp
./test/run-test
+ ./test/run-geom-test
+ ./test/run-raster-test
+
+./test/run-raster-test: Makefile src/vector_tile.pb.cc test/raster_tile.cpp test/encoding_util.hpp test/catch.hpp
+ @$(CXX) -o ./test/run-raster-test test/raster_tile.cpp src/vector_tile.pb.cc -I./src $(CXXFLAGS) $(MAPNIK_CXXFLAGS) $(PROTOBUF_CXXFLAGS) $(COMMON_FLAGS) $(MAPNIK_LDFLAGS) $(PROTOBUF_LDFLAGS) $(LDFLAGS) -Wno-unused-private-field
+ ./test/run-raster-test
+
+./test/run-geom-test: Makefile src/vector_tile.pb.cc test/geometry_encoding.cpp test/encoding_util.hpp src/vector_tile_geometry_encoder.hpp test/catch.hpp
+ @$(CXX) -o ./test/run-geom-test test/geometry_encoding.cpp src/vector_tile.pb.cc -I./src $(CXXFLAGS) $(MAPNIK_CXXFLAGS) $(PROTOBUF_CXXFLAGS) $(COMMON_FLAGS) $(MAPNIK_LDFLAGS) $(PROTOBUF_LDFLAGS) $(LDFLAGS) -Wno-unused-private-field
+
+geom: test/run-geom-test src/vector_tile.pb.cc
+ ./test/run-geom-test
python-test: python/vector_tile_pb2.py
python ./test/python/test.py
@@ -30,6 +50,8 @@ clean:
@rm -f ./src/vector_tile.pb.cc
@rm -f ./src/vector_tile.pb.h
@rm -f ./test/run-test
+ @rm -f ./test/run-geom-test
+ @rm -f ./test/run-raster-test
@rm -f ./python/vector_tile_pb2.py
@rm -f ./python/*pyc
diff --git a/README.md b/README.md
index 445fbf5..f2f9142 100644
--- a/README.md
+++ b/README.md
@@ -2,55 +2,21 @@
[![Build Status](https://secure.travis-ci.org/mapbox/mapnik-vector-tile.png)](http://travis-ci.org/mapbox/mapnik-vector-tile)
-A high performance library for working with vector tiles with Mapnik.
+A high performance library for working with vector tiles.
Provides C++ headers that support rendering geodata into vector tiles
and rendering vector tiles into images.
-Many efforts at vector tiles use [GeoJSON](http://www.geojson.org/) or other
-text based formats that are easy to parse in the browser.
-
-This approach is different: tiles are encoded as protobuf messages and
-storage is tightly designed around the [Mapnik rendering engine](http://mapnik.org).
-In this case Mapnik is the client rather than the browser. This provides a geodata
-format that is a drop-in replacement for datasources like postgis and shapefiles
-without compromising on speed and can be styled using existing design tools
-like [TileMill](http://tilemill.com).
+## Depends
+ - Mapnik > = v2.2.x: `libmapnik` and `mapnik-config`
+ - Protobuf: `libprotobuf` and `protoc`
## Implementation details
-Vector tiles in this code represent a direct serialization of Mapnik layers
-optimized for space efficient storage and fast deserialization directly back
-into Mapnik objects. For those familiar with the Mapnik API vector tiles
-here can be considered a named array of `mapnik::featureset_ptr` whose geometries
-have been pre-tiled.
-
-A vector tile can consist of one or more ordered layers identified by name
-and containing one or more features.
-
-Features contain attributes and geometries: either point, linestring, or polygon.
-Features expect single geometries so multipolygons or multilinestrings are represented
-as multiple features, each storing a single geometry part.
-
-Geometries are stored as an x,y,command stream (where `command` is a rendering command
-like move_to or line_to). Geometries are clipped, reprojected into the map srs,
-converted to screen coordinates, and [delta](http://en.wikipedia.org/wiki/Delta_encoding)
-and [zigzag](https://developers.google.com/protocol-buffers/docs/encoding#types) encoded.
-
-Feature attributes are encoded as key:value pairs which are dictionary encoded
-at the layer level for compact storage of any repeated keys or values. Values use variant
-type encoding supporting both unicode strings, boolean values, and various integer and
-floating point types.
-
-Vector tiles are serialized as protobuf messages which are then zlib compressed.
-
-The assumed projection is Spherical Mercator (`epsg:3857`).
-
-## Requires
+Vector tiles in this code represent a direct serialization of Mapnik layers optimized for space efficient storage and fast deserialization. For those familiar with the Mapnik API vector tiles here can be considered a named array of `mapnik::featureset_ptr` whose geometries have been pre-tiled.
-- Mapnik v2.2.0: `libmapnik` and `mapnik-config`
-- Protobuf: `libprotobuf` and `protoc`
+For more details see [vector-tile-spec](https://github.com/mapbox/vector-tile-spec).
### Ubuntu Dependencies Installation
diff --git a/examples/c++/Makefile b/examples/c++/Makefile
index d2870c9..14f21fd 100755
--- a/examples/c++/Makefile
+++ b/examples/c++/Makefile
@@ -6,16 +6,17 @@ LDFLAGS := $(LDFLAGS) # inherit from env
all: tileinfo
tileinfo: tileinfo.cpp ../../src/vector_tile.pb.cc
- $(CXX) tileinfo.cpp ../../src/vector_tile.pb.cc -o tileinfo -lprotobuf-lite -lz
+ $(CXX) $(CXXFLAGS) $(PROTOBUF_CXXFLAGS) $(LDFLAGS) $(PROTOBUF_LDFLAGS) tileinfo.cpp ../../src/vector_tile.pb.cc -o tileinfo -lprotobuf-lite -lz
+
+install: tileinfo
+ mkdir -p /usr/local/bin
+ cp ./tileinfo /usr/local/bin/tileinfo
+ chmod +x /usr/local/bin/tileinfo
test:
./tileinfo ../data/14_8716_8015.vector.pbf
./tileinfo ../data/14_2620_6331.vector.pbf.z
-install:
- cp ./tileinfo /usr/local/bin/tileinfo
- chmod +x /usr/local/bin/tileinfo
-
clean:
@rm -f ./tileinfo
diff --git a/examples/c++/README.md b/examples/c++/README.md
new file mode 100644
index 0000000..695b826
--- /dev/null
+++ b/examples/c++/README.md
@@ -0,0 +1,28 @@
+
+# tileinfo
+
+A commandline tool to dump details about what layers, features, and geometries exist in a vector tile.
+
+Vector tiles can be either zlib deflated (compressed) or not.
+
+
+## Depends
+
+ - C++ compiler
+ - libprotobuf
+
+Install these dependencies on Ubuntu:
+
+ apt-get install pkg-config libprotobuf7 libprotobuf-dev protobuf-compiler build-essential g++
+
+Install these dependencies on OS X:
+
+ brew install pkg-config protobuf
+
+## Installation
+
+ make
+
+## Usage
+
+ tileinfo ../data/14_8716_8015.vector.pbf
diff --git a/examples/c++/tileinfo.cpp b/examples/c++/tileinfo.cpp
index 65b90c2..222d6ee 100644
--- a/examples/c++/tileinfo.cpp
+++ b/examples/c++/tileinfo.cpp
@@ -4,6 +4,21 @@
#include <iostream>
#include <fstream>
#include <stdexcept>
+#include <sstream>
+
+enum CommandType {
+ SEG_END = 0,
+ SEG_MOVETO = 1,
+ SEG_LINETO = 2,
+ SEG_CLOSE = (0x40 | 0x0f)
+};
+
+enum eGeomType {
+ Unknown = 0,
+ Point = 1,
+ LineString = 2,
+ Polygon = 3
+};
int main(int argc, char** argv)
{
@@ -67,6 +82,70 @@ int main(int argc, char** argv)
std::cout << " features: " << layer.features_size() << "\n";
std::cout << " keys: " << layer.keys_size() << "\n";
std::cout << " values: " << layer.values_size() << "\n";
+ unsigned total_repeated = 0;
+ unsigned num_commands = 0;
+ unsigned num_move_to = 0;
+ unsigned num_line_to = 0;
+ unsigned num_close = 0;
+ unsigned num_empty = 0;
+ unsigned degenerate = 0;
+ for (unsigned j=0;j<layer.features_size();++j)
+ {
+ mapnik::vector::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;
+ unsigned g_length = 0;
+ for (int k = 0; k < f.geometry_size();)
+ {
+ if (!length) {
+ unsigned cmd_length = f.geometry(k++);
+ cmd = cmd_length & ((1 << cmd_bits) - 1);
+ length = cmd_length >> cmd_bits;
+ if (length <= 0) num_empty++;
+ num_commands++;
+ g_length = 0;
+ }
+ if (length > 0) {
+ length--;
+ if (cmd == SEG_MOVETO || cmd == SEG_LINETO)
+ {
+ f.geometry(k++);
+ f.geometry(k++);
+ g_length++;
+ if (cmd == SEG_MOVETO)
+ {
+ num_move_to++;
+ }
+ else if (cmd == SEG_LINETO)
+ {
+ num_line_to++;
+ }
+ }
+ else if (cmd == (SEG_CLOSE & ((1 << cmd_bits) - 1)))
+ {
+ if (g_length <= 2) degenerate++;
+ num_close++;
+ }
+ else
+ {
+ std::stringstream s;
+ s << "Unknown command type: " << cmd;
+ throw std::runtime_error(s.str());
+ }
+ }
+ }
+ }
+ std::cout << " geometry summary:\n";
+ std::cout << " total: " << total_repeated << "\n";
+ std::cout << " commands: " << num_commands << "\n";
+ std::cout << " move_to: " << num_move_to << "\n";
+ std::cout << " line_to: " << num_line_to << "\n";
+ std::cout << " close: " << num_close << "\n";
+ std::cout << " degenerate polygons: " << degenerate << "\n";
+ std::cout << " empty geoms: " << num_empty << "\n";
}
} else {
for (unsigned i=0;i<tile.layers_size();++i)
@@ -79,7 +158,10 @@ int main(int argc, char** argv)
for (unsigned i=0;i<layer.keys_size();++i)
{
std::string const& key = layer.keys(i);
- std::cout << key << ",";
+ std::cout << key;
+ if (i<layer.keys_size()-1) {
+ std::cout << ",";
+ }
}
std::cout << "\n";
std::cout << " values: ";
@@ -87,7 +169,7 @@ int main(int argc, char** argv)
{
mapnik::vector::tile_value const & value = layer.values(i);
if (value.has_string_value()) {
- std::cout << "'" << value.string_value();
+ std::cout << value.string_value();
} else if (value.has_int_value()) {
std::cout << value.int_value();
} else if (value.has_double_value()) {
@@ -111,7 +193,19 @@ int main(int argc, char** argv)
for (unsigned i=0;i<layer.features_size();++i)
{
mapnik::vector::tile_feature const & feat = layer.features(i);
- std::cout << " feature: " << feat.id() << " " << feat.type() << "\n";
+ std::cout << " feature: " << feat.id() << "\n";
+ std::cout << " type: ";
+ unsigned feat_type = feat.type();
+ if (feat_type == 0) {
+ std::cout << "Unknown";
+ } else if (feat_type == 1) {
+ std::cout << "Point";
+ } else if (feat_type == 2) {
+ std::cout << "LineString";
+ } else if (feat_type == 3) {
+ std::cout << "Polygon";
+ }
+ std::cout << "\n";
std::cout << " tags: ";
for (unsigned j=0;j<feat.tags_size();++j)
{
diff --git a/package.json b/package.json
index 41602ee..82f8ffe 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,10 @@
{
"name": "mapnik-vector-tile",
- "version": "0.3.4",
+ "version": "0.5.1",
"description": "Mapnik vector tile API",
"main": "./package.json",
"repository" : {
"type" : "git",
"url" : "git://github.com/mapbox/mapnik-vector-tile.git"
- },
- "scripts": {
- "install" : "protoc -Iproto/ --cpp_out=./src/ ./proto/vector_tile.proto"
}
}
diff --git a/proto/vector_tile.proto b/proto/vector_tile.proto
index 110f37c..d17e1f1 100644
--- a/proto/vector_tile.proto
+++ b/proto/vector_tile.proto
@@ -60,6 +60,9 @@ message tile {
// also encoded as deltas to the previous position. The original
// position is (0,0)
repeated uint32 geometry = 4 [ packed = true ];
+
+ optional bytes raster = 5;
+
}
message layer {
diff --git a/src/hash_variant.hpp b/src/hash_variant.hpp
deleted file mode 100644
index 57af0da..0000000
--- a/src/hash_variant.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// TODO: Remove this file once the minimum Boost version is bumped to 1.50
-
-#ifndef BOOST_HASH_VARIANT_FUNCTION_HPP
-#define BOOST_HASH_VARIANT_FUNCTION_HPP
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1020)
-# pragma once
-#endif
-
-#include <boost/variant/variant_fwd.hpp>
-#include <boost/variant/static_visitor.hpp>
-#include <boost/variant/apply_visitor.hpp>
-#include <boost/functional/hash_fwd.hpp>
-
-namespace boost {
-
-namespace detail { namespace variant {
- struct variant_hasher: public boost::static_visitor<std::size_t> {
- template <class T>
- std::size_t operator()(T const& val) const {
- using namespace boost;
- hash<T> hasher;
- return hasher(val);
- }
- };
- }}
-
-template < BOOST_VARIANT_ENUM_PARAMS(typename T) >
-std::size_t hash_value(variant< BOOST_VARIANT_ENUM_PARAMS(T) > const& val) {
- std::size_t seed = boost::apply_visitor(detail::variant::variant_hasher(), val);
- hash_combine(seed, val.which());
- return seed;
-}
-}
-
-#endif
diff --git a/src/mapnik3x_compatibility.hpp b/src/mapnik3x_compatibility.hpp
new file mode 100644
index 0000000..d2d88ec
--- /dev/null
+++ b/src/mapnik3x_compatibility.hpp
@@ -0,0 +1,34 @@
+#ifndef __MAPNIK_VECTOR_TILE_COMPATIBILITY_H__
+#define __MAPNIK_VECTOR_TILE_COMPATIBILITY_H__
+
+#include <mapnik/version.hpp>
+
+#if MAPNIK_VERSION >= 300000
+ #define MAPNIK_UNIQUE_PTR std::unique_ptr
+ #define MAPNIK_SHARED_INCLUDE <memory>
+ #define MAPNIK_MAKE_SHARED_INCLUDE <memory>
+ #define MAPNIK_MAKE_SHARED std::make_shared
+ #define MAPNIK_SHARED_PTR std::shared_ptr
+ #define MAPNIK_ADD_LAYER add_layer
+ #define MAPNIK_GET std::get
+ #define MAPNIK_GEOM_TYPE mapnik::geometry_type::types
+ #define MAPNIK_POINT mapnik::geometry_type::types::Point
+ #define MAPNIK_POLYGON mapnik::geometry_type::types::Polygon
+ #define MAPNIK_LINESTRING mapnik::geometry_type::types::LineString
+ #define MAPNIK_UNKNOWN mapnik::geometry_type::types::Unknown
+#else
+ #define MAPNIK_UNIQUE_PTR std::auto_ptr
+ #define MAPNIK_SHARED_INCLUDE <boost/shared_ptr.hpp>
+ #define MAPNIK_MAKE_SHARED_INCLUDE <boost/make_shared.hpp>
+ #define MAPNIK_MAKE_SHARED boost::make_shared
+ #define MAPNIK_SHARED_PTR boost::shared_ptr
+ #define MAPNIK_ADD_LAYER addLayer
+ #define MAPNIK_GET boost::get
+ #define MAPNIK_GEOM_TYPE mapnik::eGeomType
+ #define MAPNIK_POINT mapnik::Point
+ #define MAPNIK_POLYGON mapnik::Polygon
+ #define MAPNIK_LINESTRING mapnik::LineString
+ #define MAPNIK_UNKNOWN mapnik::Unknown
+#endif
+
+#endif
diff --git a/src/vector_tile_backend_pbf.hpp b/src/vector_tile_backend_pbf.hpp
index 717c9be..f49d0c4 100644
--- a/src/vector_tile_backend_pbf.hpp
+++ b/src/vector_tile_backend_pbf.hpp
@@ -3,16 +3,18 @@
// mapnik
#include <mapnik/feature.hpp>
-#include <mapnik/version.hpp>
#include <mapnik/value_types.hpp>
// vector tile
#include "vector_tile.pb.h"
+#include "vector_tile_geometry_encoder.hpp"
// boost
#include <boost/unordered_map.hpp>
#include <boost/foreach.hpp>
+#include "mapnik3x_compatibility.hpp"
+
namespace mapnik { namespace vector {
struct to_tile_value: public boost::static_visitor<>
@@ -75,6 +77,14 @@ namespace mapnik { namespace vector {
{
}
+ void add_tile_feature_raster(std::string const& image_buffer)
+ {
+ if (current_feature_)
+ {
+ current_feature_->set_raster(image_buffer);
+ }
+ }
+
void stop_tile_feature()
{
if (current_feature_)
@@ -99,8 +109,8 @@ namespace mapnik { namespace vector {
feature_kv_iterator end = feature.end();
for ( ;itr!=end; ++itr)
{
- std::string const& name = boost::get<0>(*itr);
- mapnik::value const& val = boost::get<1>(*itr);
+ std::string const& name = MAPNIK_GET<0>(*itr);
+ mapnik::value const& val = MAPNIK_GET<1>(*itr);
if (!val.is_null())
{
// Insert the key index
@@ -156,90 +166,28 @@ namespace mapnik { namespace vector {
current_layer_->set_extent(256 * path_multiplier_);
}
- void stop_tile_layer()
+ inline void stop_tile_layer()
{
- // NOTE: we intentionally do not remove layers without features
- // since the re-rendering logic expects a layer entry no matter what
//std::cerr << "stop_tile_layer()" << std::endl;
}
template <typename T>
- unsigned add_path(T & path, unsigned tolerance, mapnik::eGeomType type)
+ inline unsigned add_path(T & path, unsigned tolerance, MAPNIK_GEOM_TYPE type)
{
- unsigned count = 0;
-
if (current_feature_)
{
- path.rewind(0);
- current_feature_->set_type(mapnik::vector::tile_GeomType(type));
-
- vertex2d vtx(vertex2d::no_init);
- int cmd = -1;
- int cmd_idx = -1;
- const int cmd_bits = 3;
- unsigned length = 0;
-
- // See vector_tile.proto for a description of how vertex command
- // encoding works.
- while ((vtx.cmd = path.vertex(&vtx.x, &vtx.y)) != SEG_END)
- {
- if (static_cast<int>(vtx.cmd) != cmd)
- {
- if (cmd_idx >= 0)
- {
- // Encode the previous length/command value.
- current_feature_->set_geometry(cmd_idx, (length << cmd_bits) | (cmd & ((1 << cmd_bits) - 1)));
- }
- cmd = static_cast<int>(vtx.cmd);
- length = 0;
- cmd_idx = current_feature_->geometry_size();
- current_feature_->add_geometry(0); // placeholder
- }
-
- if (cmd == SEG_MOVETO || cmd == SEG_LINETO)
- {
- // Compute delta to the previous coordinate.
- int32_t cur_x = static_cast<int32_t>(std::floor((vtx.x * path_multiplier_)+.5));
- int32_t cur_y = static_cast<int32_t>(std::floor((vtx.y * path_multiplier_)+.5));
- int32_t dx = cur_x - x_;
- int32_t dy = cur_y - y_;
-
- // Omit movements that are no-ops.
- unsigned x_floor = static_cast<unsigned>(std::abs(dx));
- unsigned y_floor = static_cast<unsigned>(std::abs(dy));
- if (x_floor >= tolerance ||
- y_floor >= tolerance ||
- length == 0)
- {
- // Manual zigzag encoding.
- current_feature_->add_geometry((dx << 1) ^ (dx >> 31));
- current_feature_->add_geometry((dy << 1) ^ (dy >> 31));
- x_ = cur_x;
- y_ = cur_y;
-
- length++;
- }
- }
- else if (cmd == SEG_CLOSE) {
- length++;
- }
- else {
- throw std::runtime_error("Unknown command type");
- }
-
- ++count;
- }
-
- // Update the last length/command value.
- if (cmd_idx >= 0)
- {
- current_feature_->set_geometry(cmd_idx, (length << cmd_bits) | (cmd & ((1 << cmd_bits) - 1)));
- }
+ return encode_geometry(path,
+ static_cast<tile_GeomType>(type),
+ *current_feature_,
+ x_,
+ y_,
+ tolerance,
+ path_multiplier_);
}
- return count;
+ return 0;
}
};
- }} // end ns
+}} // end ns
#endif // __MAPNIK_VECTOR_TILE_BACKEND_PBF_H__
diff --git a/src/vector_tile_datasource.hpp b/src/vector_tile_datasource.hpp
index 362e8e9..001d291 100644
--- a/src/vector_tile_datasource.hpp
+++ b/src/vector_tile_datasource.hpp
@@ -14,12 +14,14 @@
#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_reader.hpp>
+#include <mapnik/raster.hpp>
#include <memory>
#include <stdexcept>
@@ -27,24 +29,79 @@
#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
-#include <boost/make_shared.hpp>
-#include <boost/shared_ptr.hpp>
#include <unicode/unistr.h>
-#include <boost/algorithm/string.hpp>
+
+#include "mapnik3x_compatibility.hpp"
+#include MAPNIK_MAKE_SHARED_INCLUDE
+#include MAPNIK_SHARED_INCLUDE
namespace mapnik { namespace vector {
+ void add_attributes(mapnik::feature_ptr feature,
+ mapnik::vector::tile_feature const& f,
+ mapnik::vector::tile_layer const& layer,
+ mapnik::transcoder const& tr)
+ {
+ std::size_t num_keys = static_cast<std::size_t>(layer.keys_size());
+ std::size_t num_values = static_cast<std::size_t>(layer.values_size());
+ for (int m = 0; m < f.tags_size(); m += 2)
+ {
+ std::size_t key_name = f.tags(m);
+ std::size_t key_value = f.tags(m + 1);
+ if (key_name < num_keys
+ && key_value < num_values)
+ {
+ std::string const& name = layer.keys(key_name);
+ if (feature->has_key(name))
+ {
+ mapnik::vector::tile_value const& value = layer.values(key_value);
+ if (value.has_string_value())
+ {
+ std::string str = value.string_value();
+ feature->put(name, tr.transcode(str.data(), str.length()));
+ }
+ else if (value.has_int_value())
+ {
+ feature->put(name, static_cast<mapnik::value_integer>(value.int_value()));
+ }
+ else if (value.has_double_value())
+ {
+ feature->put(name, static_cast<mapnik::value_double>(value.double_value()));
+ }
+ else if (value.has_float_value())
+ {
+ feature->put(name, static_cast<mapnik::value_double>(value.float_value()));
+ }
+ else if (value.has_bool_value())
+ {
+ feature->put(name, static_cast<mapnik::value_bool>(value.bool_value()));
+ }
+ else if (value.has_sint_value())
+ {
+ feature->put(name, static_cast<mapnik::value_integer>(value.sint_value()));
+ }
+ else if (value.has_uint_value())
+ {
+ feature->put(name, static_cast<mapnik::value_integer>(value.uint_value()));
+ }
+ }
+ }
+ }
+ }
+
template <typename Filter>
class tile_featureset : public Featureset
{
public:
tile_featureset(Filter const& filter,
+ mapnik::box2d<double> const& tile_extent,
std::set<std::string> const& attribute_names,
mapnik::vector::tile_layer const& layer,
double tile_x,
double tile_y,
double scale)
: filter_(filter),
+ tile_extent_(tile_extent),
layer_(layer),
tile_x_(tile_x),
tile_y_(tile_y),
@@ -52,7 +109,7 @@ namespace mapnik { namespace vector {
itr_(0),
end_(layer_.features_size()),
tr_("utf-8"),
- ctx_(boost::make_shared<mapnik::context_type>())
+ ctx_(MAPNIK_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();
@@ -77,18 +134,43 @@ namespace mapnik { namespace vector {
{
mapnik::vector::tile_feature const& f = layer_.features(itr_);
mapnik::value_integer feature_id = itr_++;
- // if encoded feature was given an id, respect it
- // https://github.com/mapbox/mapnik-vector-tile/issues/17
- // https://github.com/mapbox/mapnik-vector-tile/issues/18
- if (f.has_id())
+ if (f.has_raster())
{
- feature_id = f.id();
+ std::string const& image_buffer = f.raster();
+ MAPNIK_UNIQUE_PTR<mapnik::image_reader> reader(mapnik::get_image_reader(image_buffer.data(),image_buffer.size()));
+ if (reader.get())
+ {
+ if (f.has_id())
+ {
+ feature_id = f.id();
+ }
+ #if MAPNIK_VERSION >= 300000
+ double filter_factor = 1.0;
+ #endif
+ bool premultiplied = false;
+ mapnik::feature_ptr feature = mapnik::feature_factory::create(ctx_,feature_id);
+ mapnik::raster_ptr raster = MAPNIK_MAKE_SHARED<mapnik::raster>(
+ tile_extent_,
+ reader->width(),
+ reader->height(),
+ #if MAPNIK_VERSION >= 300000
+ filter_factor,
+ #endif
+ premultiplied
+ );
+ reader->read(0,0,raster->data_);
+ feature->set_raster(raster);
+ add_attributes(feature,f,layer_,tr_);
+ return feature;
+ }
+ }
+ if (f.geometry_size() <= 0)
+ {
+ continue;
}
- mapnik::feature_ptr feature(
- mapnik::feature_factory::create(ctx_,feature_id));
- feature->paths().push_back(new mapnik::geometry_type(
- mapnik::eGeomType(f.type())));
- mapnik::geometry_type * geom = &feature->paths().front();
+ MAPNIK_UNIQUE_PTR<mapnik::geometry_type> geom(
+ new mapnik::geometry_type(
+ MAPNIK_GEOM_TYPE(f.type())));
int cmd = -1;
const int cmd_bits = 3;
unsigned length = 0;
@@ -116,11 +198,6 @@ namespace mapnik { namespace vector {
y -= (static_cast<double>(dy) / scale_);
if (cmd == mapnik::SEG_MOVETO)
{
- if (!first) {
- feature->paths().push_back(new mapnik::geometry_type(
- mapnik::eGeomType(f.type())));
- geom = &feature->paths().back();
- }
first_x = x;
first_y = y;
}
@@ -142,7 +219,10 @@ namespace mapnik { namespace vector {
}
else
{
- throw std::runtime_error("Unknown command type");
+ std::stringstream msg;
+ msg << "Unknown command type (tile_featureset): "
+ << cmd;
+ throw std::runtime_error(msg.str());
}
}
}
@@ -150,63 +230,13 @@ namespace mapnik { namespace vector {
{
continue;
}
-
- // attributes
- for (int m = 0; m < f.tags_size(); m += 2)
+ if (f.has_id())
{
- std::size_t key_name = f.tags(m);
- std::size_t key_value = f.tags(m + 1);
-
- if (key_name < static_cast<std::size_t>(layer_.keys_size())
- && key_value < static_cast<std::size_t>(layer_.values_size()))
- {
- std::string const& name = layer_.keys(key_name);
- if (feature->has_key(name))
- {
- mapnik::vector::tile_value const& value = layer_.values(key_value);
- if (value.has_string_value())
- {
- std::string str = value.string_value();
- feature->put(name, tr_.transcode(str.data(), str.length()));
- }
- else if (value.has_int_value())
- {
- mapnik::value_integer val = value.int_value();
- feature->put(name, val);
- }
- else if (value.has_double_value())
- {
- mapnik::value_double val = value.double_value();
- feature->put(name, val);
- }
- else if (value.has_float_value())
- {
- mapnik::value_double val = value.float_value();
- feature->put(name, val);
- }
- else if (value.has_bool_value())
- {
- mapnik::value_bool val = value.bool_value();
- feature->put(name, val);
- }
- else if (value.has_sint_value())
- {
- mapnik::value_integer val = value.sint_value();
- feature->put(name, val);
- }
- else if (value.has_uint_value())
- {
- mapnik::value_integer val = value.uint_value();
- feature->put(name, val);
- }
- else
- {
- // Do nothing
- //feature->put_new(name, mapnik::value_null());
- }
- }
- }
+ feature_id = f.id();
}
+ mapnik::feature_ptr feature = mapnik::feature_factory::create(ctx_,feature_id);
+ feature->paths().push_back(geom.release());
+ add_attributes(feature,f,layer_,tr_);
return feature;
}
return feature_ptr();
@@ -214,6 +244,7 @@ namespace mapnik { namespace vector {
private:
Filter filter_;
+ mapnik::box2d<double> tile_extent_;
mapnik::vector::tile_layer const& layer_;
double tile_x_;
double tile_y_;
@@ -237,6 +268,7 @@ namespace mapnik { namespace vector {
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<geometry_t> get_geometry_type() const;
layer_descriptor get_descriptor() const;
@@ -286,8 +318,8 @@ namespace mapnik { namespace vector {
inline featureset_ptr tile_datasource::features(query const& q) const
{
mapnik::filter_in_box filter(q.get_bbox());
- return boost::make_shared<tile_featureset<mapnik::filter_in_box> >
- (filter, q.property_names(), layer_, tile_x_, tile_y_, scale_);
+ return MAPNIK_MAKE_SHARED<tile_featureset<mapnik::filter_in_box> >
+ (filter, get_tile_extent(), q.property_names(), layer_, tile_x_, tile_y_, scale_);
}
inline featureset_ptr tile_datasource::features_at_point(coord2d const& pt, double tol) const
@@ -298,8 +330,8 @@ namespace mapnik { namespace vector {
{
names.insert(layer_.keys(i));
}
- return boost::make_shared<tile_featureset<filter_at_point> >
- (filter, names, layer_, tile_x_, tile_y_, scale_);
+ return MAPNIK_MAKE_SHARED<tile_featureset<filter_at_point> >
+ (filter, get_tile_extent(), names, layer_, tile_x_, tile_y_, scale_);
}
inline void tile_datasource::set_envelope(box2d<double> const& bbox)
@@ -308,14 +340,19 @@ namespace mapnik { namespace vector {
extent_ = bbox;
}
+ inline box2d<double> tile_datasource::get_tile_extent() const
+ {
+ mapnik::vector::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);
+ }
+
inline box2d<double> tile_datasource::envelope() const
{
if (!extent_initialized_)
{
- mapnik::vector::spherical_mercator merc(tile_size_);
- double minx,miny,maxx,maxy;
- merc.xyz(x_,y_,z_,minx,miny,maxx,maxy);
- extent_.init(minx,miny,maxx,maxy);
+ extent_ = get_tile_extent();
extent_initialized_ = true;
}
return extent_;
diff --git a/src/vector_tile_geometry_encoder.hpp b/src/vector_tile_geometry_encoder.hpp
new file mode 100644
index 0000000..67bbd32
--- /dev/null
+++ b/src/vector_tile_geometry_encoder.hpp
@@ -0,0 +1,191 @@
+#ifndef __MAPNIK_VECTOR_TILE_GEOMETRY_ENCODER_H__
+#define __MAPNIK_VECTOR_TILE_GEOMETRY_ENCODER_H__
+
+// vector tile
+#include "vector_tile.pb.h"
+#include <mapnik/vertex.hpp>
+#include <mapnik/version.hpp>
+
+namespace mapnik { namespace vector {
+
+inline void handle_skipped_last(tile_feature & current_feature,
+ int32_t skipped_index,
+ int32_t cur_x,
+ int32_t cur_y,
+ int32_t & x_,
+ int32_t & y_)
+{
+ uint32_t last_x = current_feature.geometry(skipped_index - 2);
+ uint32_t last_y = current_feature.geometry(skipped_index - 1);
+ int32_t last_dx = ((last_x >> 1) ^ (-(last_x & 1)));
+ int32_t last_dy = ((last_y >> 1) ^ (-(last_y & 1)));
+ int32_t dx = cur_x - x_ + last_dx;
+ int32_t dy = cur_y - y_ + last_dy;
+ x_ = cur_x;
+ y_ = cur_y;
+ current_feature.set_geometry(skipped_index - 2, ((dx << 1) ^ (dx >> 31)));
+ current_feature.set_geometry(skipped_index - 1, ((dy << 1) ^ (dy >> 31)));
+}
+
+template <typename T>
+unsigned encode_geometry(T & path,
+ tile_GeomType type,
+ tile_feature & current_feature,
+ int32_t & x_,
+ int32_t & y_,
+ unsigned tolerance,
+ unsigned path_multiplier)
+{
+ unsigned count = 0;
+ path.rewind(0);
+ current_feature.set_type(type);
+
+ vertex2d vtx(vertex2d::no_init);
+ int cmd = -1;
+ int prev_cmd = -1;
+ int cmd_idx = -1;
+ const int cmd_bits = 3;
+ unsigned length = 0;
+ bool skipped_last = false;
+ int32_t skipped_index = -1;
+ int32_t cur_x = 0;
+ int32_t cur_y = 0;
+
+ // See vector_tile.proto for a description of how vertex command
+ // encoding works.
+
+ std::vector<vertex2d> output;
+ const std::size_t buffer_size = 8;
+ output.reserve(buffer_size);
+ bool done = false;
+ bool cache = true;
+ while (true)
+ {
+ if (cache)
+ {
+ // read
+ vertex2d v(vertex2d::no_init);
+ while ((v.cmd = path.vertex(&v.x, &v.y)) != SEG_END)
+ {
+#if MAPNIK_VERSION >= 300000
+ output.push_back(std::move(v));
+#else
+ output.push_back(v);
+#endif
+ if (output.size() == buffer_size) break;
+ }
+ cache = false;
+ if (v.cmd == SEG_END)
+ {
+ done = true;
+ }
+ }
+ else
+ {
+ if (done && output.empty()) break;
+ // process
+ vtx = output.front();
+ {
+ if (static_cast<int>(vtx.cmd) != cmd)
+ {
+ if (cmd_idx >= 0)
+ {
+ // Encode the previous length/command value.
+ current_feature.set_geometry(cmd_idx, (length << cmd_bits) | (cmd & ((1 << cmd_bits) - 1)));
+ }
+ cmd = static_cast<int>(vtx.cmd);
+ length = 0;
+ cmd_idx = current_feature.geometry_size();
+ current_feature.add_geometry(0); // placeholder added in first pass
+ }
+
+ switch (vtx.cmd)
+ {
+ case SEG_MOVETO:
+ case SEG_LINETO:
+ {
+ if (cmd == SEG_MOVETO && skipped_last && skipped_index > 1) // at least one vertex + cmd/length
+ {
+ // if we skipped previous vertex we just update it to the last one here.
+ handle_skipped_last(current_feature, skipped_index, cur_x, cur_y, x_, y_);
+ }
+
+ // Compute delta to the previous coordinate.
+ cur_x = static_cast<int32_t>(std::floor((vtx.x * path_multiplier) + 0.5));
+ cur_y = static_cast<int32_t>(std::floor((vtx.y * path_multiplier) + 0.5));
+ int32_t dx = cur_x - x_;
+ int32_t dy = cur_y - y_;
+ bool sharp_turn_ahead = false;
+ if (output.size() > 1)
+ {
+ vertex2d const& next_vtx = output[1];
+ if (next_vtx.cmd == SEG_LINETO)
+ {
+ uint32_t next_dx = std::abs(cur_x - static_cast<int32_t>(std::floor((next_vtx.x * path_multiplier) + 0.5)));
+ uint32_t next_dy = std::abs(cur_y - static_cast<int32_t>(std::floor((next_vtx.y * path_multiplier) + 0.5)));
+ if ((next_dx == 0 && next_dy >= tolerance) || (next_dy == 0 && next_dx >= tolerance))
+ {
+ sharp_turn_ahead = true;
+ }
+ }
+ }
+ // Keep all move_to commands, but omit other movements that are
+ // not >= the tolerance threshold and should be considered no-ops.
+ // NOTE: length == 0 indicates the command has changed and will
+ // preserve any non duplicate move_to or line_to
+ if ( length == 0 || sharp_turn_ahead ||
+ (static_cast<unsigned>(std::abs(dx)) >= tolerance) ||
+ (static_cast<unsigned>(std::abs(dy)) >= tolerance)
+ )
+ {
+ // Manual zigzag encoding.
+ current_feature.add_geometry((dx << 1) ^ (dx >> 31));
+ current_feature.add_geometry((dy << 1) ^ (dy >> 31));
+ x_ = cur_x;
+ y_ = cur_y;
+ skipped_last = false;
+ ++length;
+ }
+ else
+ {
+ skipped_last = true;
+ skipped_index = current_feature.geometry_size();
+ }
+ break;
+ }
+
+ case SEG_CLOSE:
+ {
+ if (prev_cmd != SEG_CLOSE) ++length;
+ break;
+ }
+ default:
+ std::stringstream msg;
+ msg << "Unknown command type (backend_pbf): "
+ << cmd;
+ throw std::runtime_error(msg.str());
+ break;
+ }
+ }
+ ++count;
+ prev_cmd = cmd;
+ output.erase(output.begin());
+ if (output.size() < 2) cache = true;
+ }
+ }
+ if (skipped_last && skipped_index > 1) // at least one vertex + cmd/length
+ {
+ // if we skipped previous vertex we just update it to the last one here.
+ handle_skipped_last(current_feature, skipped_index, cur_x, cur_y, x_, y_);
+ }
+ // Update the last length/command value.
+ if (cmd_idx >= 0)
+ {
+ current_feature.set_geometry(cmd_idx, (length << cmd_bits) | (cmd & ((1 << cmd_bits) - 1)));
+ }
+ return count;
+}
+
+}} // end ns
+
+#endif // __MAPNIK_VECTOR_TILE_GEOMETRY_ENCODER_H__
diff --git a/src/vector_tile_processor.hpp b/src/vector_tile_processor.hpp
index bdf0d9a..bb1f97e 100644
--- a/src/vector_tile_processor.hpp
+++ b/src/vector_tile_processor.hpp
@@ -18,6 +18,12 @@
#include <mapnik/box2d.hpp>
#include <mapnik/version.hpp>
#include <mapnik/noncopyable.hpp>
+#include <mapnik/image_util.hpp>
+#include <mapnik/raster.hpp>
+#include <mapnik/warp.hpp>
+#include <mapnik/version.hpp>
+#include <mapnik/image_scaling.hpp>
+#include <mapnik/image_compositing.hpp>
// agg
#ifdef CONV_CLIPPER
@@ -28,6 +34,8 @@
#endif
#include "agg_conv_clip_polyline.h"
+#include "agg_rendering_buffer.h"
+#include "agg_pixfmt_rgba.h"
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
@@ -37,6 +45,10 @@
#include <string>
#include <stdexcept>
+#include "mapnik3x_compatibility.hpp"
+#include MAPNIK_MAKE_SHARED_INCLUDE
+#include MAPNIK_SHARED_INCLUDE
+
namespace mapnik { namespace vector {
@@ -61,6 +73,8 @@ namespace mapnik { namespace vector {
double scale_factor_;
mapnik::CoordTransform t_;
unsigned tolerance_;
+ std::string image_format_;
+ scaling_method_e scaling_method_;
bool painted_;
public:
processor(T & backend,
@@ -69,13 +83,18 @@ namespace mapnik { namespace vector {
double scale_factor=1.0,
unsigned offset_x=0,
unsigned offset_y=0,
- unsigned tolerance=1)
+ unsigned tolerance=1,
+ std::string const& image_format="jpeg",
+ scaling_method_e scaling_method=SCALING_NEAR
+ )
: backend_(backend),
m_(map),
m_req_(m_req),
scale_factor_(scale_factor),
t_(m_req.width(),m_req.height(),m_req.extent(),offset_x,offset_y),
tolerance_(tolerance),
+ image_format_(image_format),
+ scaling_method_(scaling_method),
painted_(false) {}
void apply(double scale_denom=0.0)
@@ -88,7 +107,6 @@ namespace mapnik { namespace vector {
scale_denom *= scale_factor_;
BOOST_FOREACH ( mapnik::layer const& lay, m_.layers() )
{
- backend_.start_tile_layer(lay.name());
if (lay.visible(scale_denom))
{
apply_to_layer(lay,
@@ -100,7 +118,6 @@ namespace mapnik { namespace vector {
m_req_.extent(),
m_req_.buffer_size());
}
- backend_.stop_tile_layer();
}
}
@@ -218,27 +235,121 @@ namespace mapnik { namespace vector {
{
return;
}
- mapnik::feature_ptr feature;
- while ((feature = features->next()))
- {
- boost::ptr_vector<mapnik::geometry_type> & paths = feature->paths();
- if (paths.empty()) continue;
- backend_.start_tile_feature(*feature);
- BOOST_FOREACH( mapnik::geometry_type & geom, paths)
+ mapnik::feature_ptr feature = features->next();
+ if (feature) {
+ backend_.start_tile_layer(lay.name());
+ raster_ptr const& source = feature->get_raster();
+ if (source)
{
- mapnik::box2d<double> geom_box = geom.envelope();
- if (!geom_box.intersects(buffered_query_ext))
+ box2d<double> target_ext = box2d<double>(source->ext_);
+ prj_trans.backward(target_ext, PROJ_ENVELOPE_POINTS);
+ box2d<double> ext = t_.forward(target_ext);
+ int start_x = static_cast<int>(std::floor(ext.minx()+.5));
+ int start_y = 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));
+ int raster_width = end_x - start_x;
+ int raster_height = end_y - start_y;
+ if (raster_width > 0 && raster_height > 0)
{
+ #if MAPNIK_VERSION >= 300000
+ raster target(target_ext, raster_width, raster_height, source->get_filter_factor());
+ #else
+ raster target(target_ext, raster_width, raster_height);
+ #endif
+ if (!source->premultiplied_alpha_)
+ {
+ agg::rendering_buffer buffer(source->data_.getBytes(),
+ source->data_.width(),
+ source->data_.height(),
+ source->data_.width() * 4);
+ agg::pixfmt_rgba32 pixf(buffer);
+ pixf.premultiply();
+ }
+ if (!prj_trans.equal())
+ {
+ double offset_x = ext.minx() - start_x;
+ double offset_y = ext.miny() - start_y;
+ #if MAPNIK_VERSION >= 300000
+ reproject_and_scale_raster(target, *source, prj_trans,
+ offset_x, offset_y,
+ width,
+ scaling_method_);
+ #else
+ reproject_and_scale_raster(target, *source, prj_trans,
+ offset_x, offset_y,
+ width,
+ 2.0,
+ scaling_method_);
+ #endif
+ }
+ else
+ {
+ double image_ratio_x = ext.width() / source->data_.width();
+ double image_ratio_y = ext.height() / source->data_.height();
+ #if MAPNIK_VERSION >= 300000
+ scale_image_agg<image_data_32>(target.data_,
+ source->data_,
+ scaling_method_,
+ image_ratio_x,
+ image_ratio_y,
+ 0.0,
+ 0.0,
+ source->get_filter_factor());
+ #else
+ scale_image_agg<image_data_32>(target.data_,
+ source->data_,
+ scaling_method_,
+ image_ratio_x,
+ image_ratio_y,
+ 0.0,
+ 0.0,
+ 2.0);
+ #endif
+ }
+ mapnik::image_data_32 im_tile(width,height);
+ composite(im_tile, target.data_,
+ src_over, 1,
+ start_x, start_y, false);
+ agg::rendering_buffer buffer(im_tile.getBytes(),
+ im_tile.width(),
+ im_tile.height(),
+ im_tile.width() * 4);
+ agg::pixfmt_rgba32 pixf(buffer);
+ pixf.demultiply();
+ backend_.start_tile_feature(*feature);
+ backend_.add_tile_feature_raster(mapnik::save_to_string(im_tile,image_format_));
+ }
+ backend_.stop_tile_layer();
+ return;
+ }
+ // vector pathway
+ while (feature)
+ {
+ boost::ptr_vector<mapnik::geometry_type> & paths = feature->paths();
+ if (paths.empty()) {
+ feature = features->next();
continue;
}
- if (handle_geometry(geom,
- prj_trans,
- buffered_query_ext) > 0)
+ backend_.start_tile_feature(*feature);
+ BOOST_FOREACH( mapnik::geometry_type & geom, paths)
{
- painted_ = true;
+ mapnik::box2d<double> geom_box = geom.envelope();
+ if (!geom_box.intersects(buffered_query_ext))
+ {
+ continue;
+ }
+ if (handle_geometry(geom,
+ prj_trans,
+ buffered_query_ext) > 0)
+ {
+ painted_ = true;
+ }
}
+ backend_.stop_tile_feature();
+ feature = features->next();
}
- backend_.stop_tile_feature();
+ backend_.stop_tile_layer();
}
}
@@ -249,7 +360,7 @@ namespace mapnik { namespace vector {
unsigned path_count = 0;
switch (geom.type())
{
- case mapnik::Point:
+ case MAPNIK_POINT:
{
if (geom.size() > 0)
{
@@ -260,7 +371,7 @@ namespace mapnik { namespace vector {
}
break;
}
- case mapnik::LineString:
+ case MAPNIK_LINESTRING:
{
if (geom.size() > 1)
{
@@ -277,7 +388,7 @@ namespace mapnik { namespace vector {
}
break;
}
- case mapnik::Polygon:
+ case MAPNIK_POLYGON:
{
if (geom.size() > 2)
{
@@ -310,7 +421,7 @@ namespace mapnik { namespace vector {
}
break;
}
- case mapnik::Unknown:
+ case MAPNIK_UNKNOWN:
default:
{
throw std::runtime_error("unhandled geometry type");
diff --git a/src/vector_tile_util.hpp b/src/vector_tile_util.hpp
index 25611f7..abc3581 100644
--- a/src/vector_tile_util.hpp
+++ b/src/vector_tile_util.hpp
@@ -87,7 +87,10 @@ namespace mapnik { namespace vector {
}
else
{
- throw std::runtime_error("Unknown command type");
+ std::stringstream msg;
+ msg << "Unknown command type (is_solid_clipper): "
+ << cmd;
+ throw std::runtime_error(msg.str());
}
}
}
@@ -136,11 +139,11 @@ namespace mapnik { namespace vector {
for (int j = 0; j < layer.features_size(); j++)
{
mapnik::vector::tile_feature const& feature = layer.features(j);
- mapnik::eGeomType g_type = static_cast<mapnik::eGeomType>(feature.type());
- mapnik::geometry_type geom(g_type);
int cmd = -1;
const int cmd_bits = 3;
unsigned length = 0;
+ bool first = true;
+ mapnik::box2d<double> box;
int32_t x = 0, y = 0;
for (int k = 0; k < feature.geometry_size();)
{
@@ -149,10 +152,8 @@ namespace mapnik { namespace vector {
cmd = cmd_length & ((1 << cmd_bits) - 1);
length = cmd_length >> cmd_bits;
}
-
if (length > 0) {
length--;
-
if (cmd == mapnik::SEG_MOVETO || cmd == mapnik::SEG_LINETO)
{
int32_t dx = feature.geometry(k++);
@@ -164,21 +165,31 @@ namespace mapnik { namespace vector {
if ((x > 0 && x < static_cast<int>(side)) && (y > 0 && y < static_cast<int>(side))) {
return false;
}
- geom.push_vertex(x, y, static_cast<mapnik::CommandType>(cmd));
+ if (first)
+ {
+ box.init(x,y,x,y);
+ first = false;
+ }
+ else
+ {
+ box.expand_to_include(x,y);
+ }
}
else if (cmd == (mapnik::SEG_CLOSE & ((1 << cmd_bits) - 1)))
{
- geom.push_vertex(0, 0, mapnik::SEG_CLOSE);
+ // pass
}
else
{
- throw std::runtime_error("Unknown command type");
+ std::stringstream msg;
+ msg << "Unknown command type (is_solid_extent): "
+ << cmd;
+ throw std::runtime_error(msg.str());
}
}
}
// Once we have only one clipped result polygon, we can compare the
// areas and return early if they don't match.
- mapnik::box2d<double> box = geom.envelope();
double geom_area = box.width() * box.height();
if (geom_area < (extent_area - 32) )
{
diff --git a/test/encoding_util.hpp b/test/encoding_util.hpp
new file mode 100644
index 0000000..ad69bda
--- /dev/null
+++ b/test/encoding_util.hpp
@@ -0,0 +1,86 @@
+#include <mapnik/vertex.hpp>
+#include <mapnik/geometry.hpp>
+#include "vector_tile_geometry_encoder.hpp"
+
+void decode_geometry(mapnik::vector::tile_feature const& f,
+ mapnik::geometry_type & geom,
+ double & x,
+ double & y,
+ double scale)
+{
+ int cmd = -1;
+ const int cmd_bits = 3;
+ unsigned length = 0;
+ for (int k = 0; k < f.geometry_size();)
+ {
+ if (!length) {
+ unsigned cmd_length = f.geometry(k++);
+ cmd = cmd_length & ((1 << cmd_bits) - 1);
+ length = cmd_length >> cmd_bits;
+ }
+ if (length > 0) {
+ length--;
+ if (cmd == mapnik::SEG_MOVETO || cmd == mapnik::SEG_LINETO)
+ {
+ int32_t dx = f.geometry(k++);
+ int32_t dy = f.geometry(k++);
+ dx = ((dx >> 1) ^ (-(dx & 1)));
+ dy = ((dy >> 1) ^ (-(dy & 1)));
+ x += (static_cast<double>(dx) / scale);
+ y += (static_cast<double>(dy) / scale);
+ geom.push_vertex(x, y, static_cast<mapnik::CommandType>(cmd));
+ }
+ else if (cmd == (mapnik::SEG_CLOSE & ((1 << cmd_bits) - 1)))
+ {
+ geom.push_vertex(0, 0, mapnik::SEG_CLOSE);
+ }
+ else
+ {
+ std::stringstream msg;
+ msg << "Unknown command type (decode_geometry): "
+ << cmd;
+ throw std::runtime_error(msg.str());
+ }
+ }
+ }
+}
+
+template <typename T>
+std::string show_path(T & path)
+{
+ unsigned cmd = -1;
+ double x = 0;
+ double y = 0;
+ std::ostringstream s;
+ path.rewind(0);
+ while ((cmd = path.vertex(&x, &y)) != mapnik::SEG_END)
+ {
+ switch (cmd)
+ {
+ case mapnik::SEG_MOVETO: s << "move_to("; break;
+ case mapnik::SEG_LINETO: s << "line_to("; break;
+ case mapnik::SEG_CLOSE: s << "close_path("; break;
+ default: std::clog << "unhandled cmd " << cmd << "\n"; break;
+ }
+ s << x << "," << y << ")\n";
+ }
+ return s.str();
+}
+
+std::string compare(mapnik::geometry_type const & g,
+ unsigned tolerance=0,
+ unsigned path_multiplier=1)
+{
+ using namespace mapnik::vector;
+ // encode
+ tile_feature feature;
+ int32_t x = 0;
+ int32_t y = 0;
+ encode_geometry(g,(tile_GeomType)g.type(),feature,x,y,tolerance,path_multiplier);
+ // decode
+ mapnik::geometry_type g2(MAPNIK_POLYGON);
+ double x0 = 0;
+ double y0 = 0;
+ decode_geometry(feature,g2,x0,y0,path_multiplier);
+ return show_path(g2);
+}
diff --git a/test/geometry_encoding.cpp b/test/geometry_encoding.cpp
new file mode 100644
index 0000000..1533e49
--- /dev/null
+++ b/test/geometry_encoding.cpp
@@ -0,0 +1,338 @@
+// https://github.com/philsquared/Catch/wiki/Supplying-your-own-main()
+// http://www.levelofindirection.com/journal/2013/6/28/catch-10.html
+#define CATCH_CONFIG_RUNNER
+#include "catch.hpp"
+
+#include "mapnik3x_compatibility.hpp"
+#include "encoding_util.hpp"
+
+// https://github.com/mapbox/mapnik-vector-tile/issues/36
+
+TEST_CASE( "test 1", "should round trip without changes" ) {
+ mapnik::geometry_type g(MAPNIK_POLYGON);
+ g.move_to(0,0);
+ g.line_to(1,1);
+ g.line_to(100,100);
+ g.close_path();
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(1,1)\n"
+ "line_to(100,100)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(compare(g) == expected);
+}
+
+TEST_CASE( "test 2", "should drop coincident line_to moves" ) {
+ mapnik::geometry_type g(MAPNIK_LINESTRING);
+ g.move_to(0,0);
+ g.line_to(3,3);
+ g.line_to(3,3);
+ g.line_to(3,3);
+ g.line_to(3,3);
+ g.line_to(4,4);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(3,3)\n"
+ "line_to(4,4)\n"
+ );
+ CHECK(compare(g,1) == expected);
+}
+
+TEST_CASE( "test 2b", "should drop vertices" ) {
+ mapnik::geometry_type g(MAPNIK_LINESTRING);
+ g.move_to(0,0);
+ g.line_to(0,0);
+ g.line_to(1,1);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(0,0)\n" // TODO - should we try to drop this?
+ "line_to(1,1)\n"
+ );
+ CHECK(compare(g,1) == expected);
+}
+
+TEST_CASE( "test 3", "should not drop first move_to or last vertex in line" ) {
+ mapnik::geometry_type g(MAPNIK_LINESTRING);
+ g.move_to(0,0);
+ g.line_to(1,1);
+ g.move_to(0,0);
+ g.line_to(1,1);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(1,1)\n"
+ "move_to(0,0)\n"
+ "line_to(1,1)\n"
+ );
+ CHECK(compare(g,1000) == expected);
+}
+
+TEST_CASE( "test 4", "should not drop first move_to or last vertex in polygon" ) {
+ mapnik::geometry_type g(MAPNIK_POLYGON);
+ g.move_to(0,0);
+ g.line_to(1,1);
+ g.move_to(0,0);
+ g.line_to(1,1);
+ g.close_path();
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(1,1)\n"
+ "move_to(0,0)\n"
+ "line_to(1,1)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(compare(g,1000) == expected);
+}
+
+TEST_CASE( "test 5", "can drop duplicate move_to" ) {
+ mapnik::geometry_type g(MAPNIK_LINESTRING);
+ g.move_to(0,0);
+ g.move_to(1,1); // skipped
+ g.line_to(4,4); // skipped
+ g.line_to(5,5);
+ std::string expected(
+ "move_to(0,0)\n" // TODO - should we keep move_to(1,1) instead?
+ "line_to(5,5)\n"
+ );
+ CHECK(compare(g,2) == expected);
+}
+
+TEST_CASE( "test 5b", "can drop duplicate move_to" ) {
+ mapnik::geometry_type g(MAPNIK_LINESTRING);
+ g.move_to(0,0);
+ g.move_to(1,1);
+ g.line_to(2,2);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(2,2)\n"
+ );
+ CHECK(compare(g,3) == expected);
+}
+
+TEST_CASE( "test 5c", "can drop duplicate move_to but not second" ) {
+ mapnik::geometry_type g(MAPNIK_LINESTRING);
+ g.move_to(0,0);
+ g.move_to(1,1);
+ g.line_to(2,2);
+ g.move_to(3,3);
+ g.line_to(4,4);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(2,2)\n"
+ "move_to(3,3)\n"
+ "line_to(4,4)\n"
+ );
+ CHECK(compare(g,3) == expected);
+}
+
+TEST_CASE( "test 6", "should not drop last line_to if repeated" ) {
+ mapnik::geometry_type g(MAPNIK_LINESTRING);
+ g.move_to(0,0);
+ g.line_to(2,2);
+ g.line_to(1000,1000); // skipped
+ g.line_to(1001,1001); // skipped
+ g.line_to(1001,1001);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(2,2)\n"
+ "line_to(1001,1001)\n"
+ );
+ CHECK(compare(g,2) == expected);
+}
+
+TEST_CASE( "test 7", "ensure proper handling of skipping + close commands" ) {
+ mapnik::geometry_type g(MAPNIK_POLYGON);
+ g.move_to(0,0);
+ g.line_to(2,2);
+ g.close_path();
+ g.move_to(5,5);
+ g.line_to(10,10); // skipped
+ g.line_to(21,21);
+ g.close_path();
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(2,2)\n"
+ "close_path(0,0)\n"
+ "move_to(5,5)\n"
+ "line_to(21,21)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(compare(g,100) == expected);
+}
+
+TEST_CASE( "test 8", "should drop repeated close commands" ) {
+ mapnik::geometry_type g(MAPNIK_POLYGON);
+ g.move_to(0,0);
+ g.line_to(2,2);
+ g.close_path();
+ g.close_path();
+ g.close_path();
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(2,2)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(compare(g,100) == expected);
+}
+
+TEST_CASE( "test 9a", "should not drop last vertex" ) {
+ mapnik::geometry_type g(MAPNIK_LINESTRING);
+ g.move_to(0,0);
+ g.line_to(9,0); // skipped
+ g.line_to(0,10);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(0,10)\n"
+ );
+ CHECK(compare(g,11) == expected);
+}
+
+TEST_CASE( "test 9b", "should not drop last vertex" ) {
+ mapnik::geometry_type g(MAPNIK_POLYGON);
+ g.move_to(0,0);
+ g.line_to(10,0); // skipped
+ g.line_to(0,10);
+ g.close_path();
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(0,10)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(compare(g,11) == expected);
+}
+
+TEST_CASE( "test 9c", "should not drop last vertex" ) {
+ mapnik::geometry_type g(MAPNIK_POLYGON);
+ g.move_to(0,0);
+ g.line_to(0,10);
+ g.close_path();
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(0,10)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(compare(g,11) == expected);
+}
+
+TEST_CASE( "test 10", "should skip repeated close and coincident line_to commands" ) {
+ mapnik::geometry_type g(MAPNIK_POLYGON);
+ g.move_to(0,0);
+ g.line_to(10,10);
+ g.line_to(10,10); // skipped
+ g.line_to(20,20);
+ g.line_to(20,20); // skipped, but added back and replaces previous
+ g.close_path();
+ g.close_path(); // skipped
+ g.close_path(); // skipped
+ g.close_path(); // skipped
+ g.move_to(0,0);
+ g.line_to(10,10);
+ g.line_to(20,20);
+ g.close_path();
+ g.close_path(); // skipped
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(10,10)\n"
+ "line_to(20,20)\n"
+ "close_path(0,0)\n"
+ "move_to(0,0)\n"
+ "line_to(10,10)\n"
+ "line_to(20,20)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(compare(g,1) == expected);
+}
+
+TEST_CASE( "test 11", "should correctly encode multiple paths" ) {
+ using namespace mapnik::vector;
+ tile_feature feature0;
+ int32_t x = 0;
+ int32_t y = 0;
+ unsigned path_multiplier = 1;
+ unsigned tolerance = 10000;
+ mapnik::geometry_type g0(MAPNIK_POLYGON);
+ g0.move_to(0,0);
+ g0.line_to(-10,-10);
+ g0.line_to(-20,-20);
+ g0.close_path();
+ encode_geometry(g0,(tile_GeomType)g0.type(),feature0,x,y,tolerance,path_multiplier);
+ CHECK(x == -20);
+ CHECK(y == -20);
+ mapnik::geometry_type g1(MAPNIK_POLYGON);
+ g1.move_to(1000,1000);
+ g1.line_to(1010,1010);
+ g1.line_to(1020,1020);
+ g1.close_path();
+ encode_geometry(g1,(tile_GeomType)g1.type(),feature0,x,y,tolerance,path_multiplier);
+ CHECK(x == 1020);
+ CHECK(y == 1020);
+ mapnik::geometry_type g2(MAPNIK_POLYGON);
+ double x0 = 0;
+ double y0 = 0;
+ decode_geometry(feature0,g2,x0,y0,path_multiplier);
+ std::string actual = show_path(g2);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(-20,-20)\n"
+ "close_path(0,0)\n"
+ "move_to(1000,1000)\n"
+ "line_to(1020,1020)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(actual == expected);
+}
+
+TEST_CASE( "test 12", "should correctly encode multiple paths" ) {
+ using namespace mapnik::vector;
+ tile_feature feature0;
+ int32_t x = 0;
+ int32_t y = 0;
+ unsigned path_multiplier = 1;
+ unsigned tolerance = 10;
+ mapnik::geometry_type g(MAPNIK_POLYGON);
+ g.move_to(0,0);
+ g.line_to(100,0);
+ g.line_to(100,100);
+ g.line_to(0,100);
+ g.line_to(0,5);
+ g.line_to(0,0);
+ g.close_path();
+ g.move_to(20,20);
+ g.line_to(20,60);
+ g.line_to(60,60);
+ g.line_to(60,20);
+ g.line_to(25,20);
+ g.line_to(20,20);
+ g.close_path();
+ encode_geometry(g,(tile_GeomType)g.type(),feature0,x,y,tolerance,path_multiplier);
+
+ mapnik::geometry_type g2(MAPNIK_POLYGON);
+ double x0 = 0;
+ double y0 = 0;
+ decode_geometry(feature0,g2,x0,y0,path_multiplier);
+ std::string actual = show_path(g2);
+ std::string expected(
+ "move_to(0,0)\n"
+ "line_to(100,0)\n"
+ "line_to(100,100)\n"
+ "line_to(0,100)\n"
+ "line_to(0,0)\n"
+ "close_path(0,0)\n"
+ "move_to(20,20)\n"
+ "line_to(20,60)\n"
+ "line_to(60,60)\n"
+ "line_to(60,20)\n"
+ "line_to(20,20)\n"
+ "close_path(0,0)\n"
+ );
+ CHECK(actual == expected);
+}
+
+int main (int argc, char* const argv[])
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+ int result = Catch::Session().run( argc, argv );
+ if (!result) printf("\x1b[1;32m ✓ \x1b[0m\n");
+ google::protobuf::ShutdownProtobufLibrary();
+ return result;
+}
diff --git a/test/raster_style.xml b/test/raster_style.xml
new file mode 100644
index 0000000..36e34e4
--- /dev/null
+++ b/test/raster_style.xml
@@ -0,0 +1,7 @@
+<Map>
+<Style name="style">
+ <Rule>
+ <RasterSymbolizer />
+ </Rule>
+</Style>
+</Map>
\ No newline at end of file
diff --git a/test/raster_tile.cpp b/test/raster_tile.cpp
new file mode 100644
index 0000000..47e3bbb
--- /dev/null
+++ b/test/raster_tile.cpp
@@ -0,0 +1,125 @@
+// https://github.com/philsquared/Catch/wiki/Supplying-your-own-main()
+#define CATCH_CONFIG_RUNNER
+#include "catch.hpp"
+
+// test utils
+#include "test_utils.hpp"
+#include "test-cfg.h"
+
+#include "vector_tile_projection.hpp"
+
+// vector output api
+#include "vector_tile_processor.hpp"
+#include "vector_tile_backend_pbf.hpp"
+#include "vector_tile_util.hpp"
+#include "vector_tile_datasource.hpp"
+
+
+#include <mapnik/graphics.hpp>
+#include <mapnik/datasource_cache.hpp>
+
+TEST_CASE( "vector tile output 1", "should create vector tile with one point" ) {
+ mapnik::datasource_cache::instance().register_datasources(MAPNIK_PLUGINDIR);
+ typedef mapnik::vector::backend_pbf backend_type;
+ typedef mapnik::vector::processor<backend_type> renderer_type;
+ typedef mapnik::vector::tile tile_type;
+ unsigned _x=0,_y=0,_z=1;
+ double minx,miny,maxx,maxy;
+ mapnik::vector::spherical_mercator merc(512);
+ merc.xyz(_x,_y,_z,minx,miny,maxx,maxy);
+ mapnik::box2d<double> bbox;
+ bbox.init(minx,miny,maxx,maxy);
+ unsigned tile_size = 512;
+ tile_type tile;
+ backend_type backend(tile,16);
+ mapnik::Map map(tile_size,tile_size,"+init=epsg:3857");
+ map.set_buffer_size(256);
+ mapnik::layer lyr("layer",map.srs());
+ mapnik::parameters params;
+ params["type"] = "gdal";
+ // created with:
+ // wget http://www.nacis.org/naturalearth/50m/raster/NE2_50m_SR_W.zip
+ // gdalwarp -t_srs EPSG:3857 -ts 1048 1048 -r bilinear NE2_50M_SR_W.tif natural_earth.tif
+ params["file"] = "test/natural_earth.tif";
+ MAPNIK_SHARED_PTR<mapnik::datasource> ds =
+ mapnik::datasource_cache::instance().create(params);
+ lyr.set_datasource(ds);
+ map.MAPNIK_ADD_LAYER(lyr);
+ mapnik::request m_req(tile_size,tile_size,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();
+ CHECK(1 == tile.layers_size());
+ mapnik::vector::tile_layer const& layer = tile.layers(0);
+ CHECK(std::string("layer") == layer.name());
+ CHECK(1 == layer.features_size());
+ mapnik::vector::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());
+ // debug
+ bool debug = false;
+ if (debug) {
+ std::ofstream file("out.jpeg", std::ios::out|std::ios::trunc|std::ios::binary);
+ file << ras_buffer;
+ file.close();
+ }
+
+ std::size_t expected_image_size = 45660;
+ int expected_vtile_size = expected_image_size + 26;
+ if (!debug) {
+ CHECK(expected_image_size == ras_buffer.size());
+ CHECK(expected_vtile_size == tile.ByteSize());
+ }
+ std::string buffer;
+ CHECK(tile.SerializeToString(&buffer));
+ if (!debug) {
+ CHECK(expected_vtile_size == buffer.size());
+ }
+ // now read back and render image
+ mapnik::Map map2(tile_size,tile_size,"+init=epsg:3857");
+ map2.set_buffer_size(256);
+ tile_type tile2;
+ CHECK(tile2.ParseFromString(buffer));
+ CHECK(1 == tile2.layers_size());
+ mapnik::vector::tile_layer const& layer2 = tile2.layers(0);
+ CHECK(std::string("layer") == layer2.name());
+ CHECK(1 == layer2.features_size());
+ mapnik::vector::tile_feature const& f2 = layer2.features(0);
+ CHECK(static_cast<mapnik::value_integer>(1) == static_cast<mapnik::value_integer>(f2.id()));
+ CHECK(0 == f2.geometry_size());
+ CHECK(f2.has_raster());
+ CHECK(!f2.raster().empty());
+ if (!debug) {
+ CHECK(expected_image_size == f2.raster().size());
+ }
+ mapnik::layer lyr2("layer",map2.srs());
+ MAPNIK_SHARED_PTR<mapnik::vector::tile_datasource> ds2 = MAPNIK_MAKE_SHARED<
+ mapnik::vector::tile_datasource>(
+ layer2,_x,_y,_z,map2.width());
+ ds2->set_envelope(bbox);
+ lyr2.set_datasource(ds2);
+ lyr2.add_style("style");
+ map2.MAPNIK_ADD_LAYER(lyr2);
+ mapnik::load_map(map2,"test/raster_style.xml");
+ map2.zoom_to_box(bbox);
+ mapnik::image_32 im(map2.width(),map2.height());
+ mapnik::agg_renderer<mapnik::image_32> ren2(map2,im);
+ ren2.apply();
+ if (debug) {
+ mapnik::save_to_file(im,"image.png");
+ }
+ unsigned rgba = im.data()(128,128);
+ CHECK(rgba != 0);
+}
+
+int main (int argc, char* const argv[])
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+ int result = Catch::Session().run( argc, argv );
+ if (!result) printf("\x1b[1;32m ✓ \x1b[0m\n");
+ google::protobuf::ShutdownProtobufLibrary();
+ return result;
+}
diff --git a/test/style.xml b/test/style.xml
new file mode 100644
index 0000000..0b403c3
--- /dev/null
+++ b/test/style.xml
@@ -0,0 +1,7 @@
+<Map>
+<Style name="style">
+ <Rule>
+ <MarkersSymbolizer fill="red" />
+ </Rule>
+</Style>
+</Map>
\ No newline at end of file
diff --git a/test/test_utils.hpp b/test/test_utils.hpp
index 55f680a..3e91716 100644
--- a/test/test_utils.hpp
+++ b/test/test_utils.hpp
@@ -1,10 +1,7 @@
// mapnik
+#include "mapnik3x_compatibility.hpp"
#include <mapnik/map.hpp>
#include <mapnik/layer.hpp>
-#include <mapnik/rule.hpp>
-#include <mapnik/feature_type_style.hpp>
-#include <mapnik/rule.hpp>
-#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/agg_renderer.hpp>
@@ -15,25 +12,25 @@
#include <mapnik/unicode.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/datasource.hpp>
+#include <mapnik/load_map.hpp>
#include <mapnik/memory_datasource.hpp>
// boost
-#include <boost/make_shared.hpp>
-#include <boost/shared_ptr.hpp>
+#include MAPNIK_SHARED_INCLUDE
+#include MAPNIK_MAKE_SHARED_INCLUDE
#include <string>
-boost::shared_ptr<mapnik::memory_datasource> build_ds() {
- mapnik::context_ptr ctx = boost::make_shared<mapnik::context_type>();
+MAPNIK_SHARED_PTR<mapnik::memory_datasource> build_ds(double x,double y) {
+ mapnik::context_ptr ctx = MAPNIK_MAKE_SHARED<mapnik::context_type>();
ctx->push("name");
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx,1));
mapnik::transcoder tr("utf-8");
- UnicodeString ustr = tr.transcode("null island");
- feature->put("name",ustr);
- mapnik::geometry_type * pt = new mapnik::geometry_type(mapnik::Point);
- pt->move_to(0,0);
+ feature->put("name",tr.transcode("null island"));
+ mapnik::geometry_type * pt = new mapnik::geometry_type(MAPNIK_POINT);
+ pt->move_to(x,y);
feature->add_geometry(pt);
- boost::shared_ptr<mapnik::memory_datasource> ds = boost::make_shared<mapnik::memory_datasource>();
+ MAPNIK_SHARED_PTR<mapnik::memory_datasource> ds = MAPNIK_MAKE_SHARED<mapnik::memory_datasource>();
ds->push(feature);
return ds;
}
diff --git a/test/vector_tile.cpp b/test/vector_tile.cpp
index 8b4d8a6..4ea681e 100644
--- a/test/vector_tile.cpp
+++ b/test/vector_tile.cpp
@@ -7,21 +7,22 @@
#include "vector_tile_projection.hpp"
-const unsigned x=0,y=0,z=0;
+const unsigned _x=0,_y=0,_z=0;
const unsigned tile_size = 256;
mapnik::box2d<double> bbox;
// vector output api
#include "vector_tile_processor.hpp"
#include "vector_tile_backend_pbf.hpp"
+#include "vector_tile_util.hpp"
+
+// vector input api
+#include "vector_tile_datasource.hpp"
TEST_CASE( "vector tile projection 1", "should support z/x/y to bbox conversion at 0/0/0" ) {
mapnik::vector::spherical_mercator merc(256);
- int x = 0;
- int y = 0;
- int z = 0;
double minx,miny,maxx,maxy;
- merc.xyz(x,y,z,minx,miny,maxx,maxy);
+ merc.xyz(_x,_y,_z,minx,miny,maxx,maxy);
mapnik::box2d<double> map_extent(minx,miny,maxx,maxy);
mapnik::box2d<double> e(-20037508.342789,-20037508.342789,20037508.342789,20037508.342789);
double epsilon = 0.000001;
@@ -47,7 +48,7 @@ TEST_CASE( "vector tile projection 2", "should support z/x/y to bbox conversion
CHECK(std::fabs(map_extent.maxy() - e.maxy()) < epsilon);
}
-TEST_CASE( "vector tile output", "should create vector tile with one point" ) {
+TEST_CASE( "vector tile output 1", "should create vector tile with one point" ) {
typedef mapnik::vector::backend_pbf backend_type;
typedef mapnik::vector::processor<backend_type> renderer_type;
typedef mapnik::vector::tile tile_type;
@@ -55,8 +56,8 @@ TEST_CASE( "vector tile output", "should create vector tile with one point" ) {
backend_type backend(tile,16);
mapnik::Map map(tile_size,tile_size);
mapnik::layer lyr("layer");
- lyr.set_datasource(build_ds());
- map.addLayer(lyr);
+ lyr.set_datasource(build_ds(0,0));
+ map.MAPNIK_ADD_LAYER(lyr);
mapnik::request m_req(tile_size,tile_size,bbox);
renderer_type ren(backend,map,m_req);
ren.apply();
@@ -76,9 +77,67 @@ TEST_CASE( "vector tile output", "should create vector tile with one point" ) {
CHECK(52 == buffer.size());
}
-// vector input api
-#include "vector_tile_datasource.hpp"
-#include "vector_tile_backend_pbf.hpp"
+TEST_CASE( "vector tile output 2", "adding empty layers should result in empty tile" ) {
+ typedef mapnik::vector::backend_pbf backend_type;
+ typedef mapnik::vector::processor<backend_type> renderer_type;
+ typedef mapnik::vector::tile tile_type;
+ tile_type tile;
+ backend_type backend(tile,16);
+ mapnik::Map map(tile_size,tile_size);
+ map.MAPNIK_ADD_LAYER(mapnik::layer("layer"));
+ map.zoom_to_box(bbox);
+ mapnik::request m_req(tile_size,tile_size,bbox);
+ renderer_type ren(backend,map,m_req);
+ ren.apply();
+ CHECK(0 == tile.layers_size());
+}
+
+TEST_CASE( "vector tile output 3", "adding layers with geometries outside rendering extent should not add layer" ) {
+ typedef mapnik::vector::backend_pbf backend_type;
+ typedef mapnik::vector::processor<backend_type> renderer_type;
+ typedef mapnik::vector::tile tile_type;
+ tile_type tile;
+ backend_type backend(tile,16);
+ mapnik::Map map(tile_size,tile_size);
+ mapnik::layer lyr("layer");
+ mapnik::context_ptr ctx = MAPNIK_MAKE_SHARED<mapnik::context_type>();
+ mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx,1));
+ MAPNIK_UNIQUE_PTR<mapnik::geometry_type> g(new mapnik::geometry_type(MAPNIK_LINESTRING));
+ g->move_to(0,0);
+ g->line_to(1,1);
+ feature->add_geometry(g.release());
+ MAPNIK_SHARED_PTR<mapnik::memory_datasource> ds = MAPNIK_MAKE_SHARED<mapnik::memory_datasource>();
+ ds->push(feature);
+ lyr.set_datasource(ds);
+ map.MAPNIK_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(1 == tile.layers_size());
+}
+
+TEST_CASE( "vector tile output 4", "adding layers with degenerate geometries should not add layer" ) {
+ typedef mapnik::vector::backend_pbf backend_type;
+ typedef mapnik::vector::processor<backend_type> renderer_type;
+ typedef mapnik::vector::tile tile_type;
+ tile_type tile;
+ backend_type backend(tile,16);
+ mapnik::Map map(tile_size,tile_size);
+ mapnik::layer lyr("layer");
+ // create a datasource with a feature outside the map
+ MAPNIK_SHARED_PTR<mapnik::memory_datasource> ds = build_ds(bbox.minx()-1,bbox.miny()-1);
+ // 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(bbox);
+ lyr.set_datasource(ds);
+ map.MAPNIK_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(0 == tile.layers_size());
+}
TEST_CASE( "vector tile input", "should be able to parse message and render point" ) {
typedef mapnik::vector::backend_pbf backend_type;
@@ -88,8 +147,8 @@ TEST_CASE( "vector tile input", "should be able to parse message and render poin
backend_type backend(tile,16);
mapnik::Map map(tile_size,tile_size);
mapnik::layer lyr("layer");
- lyr.set_datasource(build_ds());
- map.addLayer(lyr);
+ lyr.set_datasource(build_ds(0,0));
+ map.MAPNIK_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);
@@ -106,9 +165,9 @@ TEST_CASE( "vector tile input", "should be able to parse message and render poin
mapnik::vector::tile_layer const& layer2 = tile2.layers(0);
CHECK(std::string("layer") == layer2.name());
mapnik::layer lyr2("layer");
- boost::shared_ptr<mapnik::vector::tile_datasource> ds = boost::make_shared<
+ MAPNIK_SHARED_PTR<mapnik::vector::tile_datasource> ds = MAPNIK_MAKE_SHARED<
mapnik::vector::tile_datasource>(
- layer2,x,y,z,map2.width());
+ layer2,_x,_y,_z,map2.width());
ds->set_envelope(bbox);
mapnik::layer_descriptor lay_desc = ds->get_descriptor();
std::vector<std::string> expected_names;
@@ -120,22 +179,14 @@ TEST_CASE( "vector tile input", "should be able to parse message and render poin
}
CHECK(names == expected_names);
lyr2.set_datasource(ds);
- lyr2.add_style("style");
- map2.addLayer(lyr2);
- mapnik::feature_type_style s;
- mapnik::rule r;
- mapnik::color red(255,0,0);
- mapnik::markers_symbolizer sym;
- sym.set_fill(red);
- r.append(sym);
- s.add_rule(r);
- map2.insert_style("style",s);
+ map2.MAPNIK_ADD_LAYER(lyr2);
+ mapnik::load_map(map2,"test/style.xml");
map2.zoom_to_box(bbox);
mapnik::image_32 im(map2.width(),map2.height());
mapnik::agg_renderer<mapnik::image_32> ren2(map2,im);
ren2.apply();
unsigned rgba = im.data()(128,128);
- CHECK(red.rgba() == rgba);
+ CHECK(0 == rgba);
//mapnik::save_to_file(im,"test.png");
}
@@ -147,8 +198,8 @@ TEST_CASE( "vector tile datasource", "should filter features outside extent" ) {
backend_type backend(tile,16);
mapnik::Map map(tile_size,tile_size);
mapnik::layer lyr("layer");
- lyr.set_datasource(build_ds());
- map.addLayer(lyr);
+ lyr.set_datasource(build_ds(0,0));
+ map.MAPNIK_ADD_LAYER(lyr);
mapnik::request m_req(tile_size,tile_size,bbox);
renderer_type ren(backend,map,m_req);
ren.apply();
@@ -163,7 +214,7 @@ TEST_CASE( "vector tile datasource", "should filter features outside extent" ) {
CHECK(4096 == f.geometry(1));
CHECK(4096 == f.geometry(2));
// now actually start the meat of the test
- mapnik::vector::tile_datasource ds(layer,x,y,z,tile_size);
+ mapnik::vector::tile_datasource ds(layer,_x,_y,_z,tile_size);
mapnik::featureset_ptr fs;
// ensure we can query single feature
@@ -211,13 +262,159 @@ TEST_CASE( "vector tile datasource", "should filter features outside extent" ) {
CHECK(f_ptr->context()->size() == 1);
}
+// NOTE: encoding a 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 in error and its an optimization in the
+// case where you know that lines do not need to be labeled in custom ways.
+TEST_CASE( "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 tht 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
+ mapnik::vector::tile tile;
+ mapnik::vector::backend_pbf backend(tile,path_multiplier);
+ backend.start_tile_layer("layer");
+ mapnik::feature_ptr feature(mapnik::feature_factory::create(MAPNIK_MAKE_SHARED<mapnik::context_type>(),1));
+ backend.start_tile_feature(*feature);
+ MAPNIK_UNIQUE_PTR<mapnik::geometry_type> g(new mapnik::geometry_type(MAPNIK_LINESTRING));
+ 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.add_path(*g, tolerance, g->type());
+ backend.stop_tile_feature();
+ backend.stop_tile_layer();
+ // done encoding single feature/geometry
+ CHECK(1 == tile.layers_size());
+ mapnik::vector::tile_layer const& layer = tile.layers(0);
+ CHECK(1 == layer.features_size());
+ mapnik::vector::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
+}
+
+TEST_CASE( "encoding single line 1", "should maintain start/end vertex" ) {
+ // Options
+ // here we use a multiplier of 1 to avoid rounding numbers
+ // this works because we are staying in integer space for this test
+ unsigned path_multiplier = 1;
+ // here we use a tolerance of 2. Along with a multiplier of 1 this
+ // says to discard any verticies that are not at least >= 2 different
+ // in both the x and y from the previous vertex
+ unsigned tolerance = 2;
+ // now create the testing data
+ mapnik::vector::tile tile;
+ mapnik::vector::backend_pbf backend(tile,path_multiplier);
+ backend.start_tile_layer("layer");
+ mapnik::feature_ptr feature(mapnik::feature_factory::create(MAPNIK_MAKE_SHARED<mapnik::context_type>(),1));
+ backend.start_tile_feature(*feature);
+ MAPNIK_UNIQUE_PTR<mapnik::geometry_type> g(new mapnik::geometry_type(MAPNIK_LINESTRING));
+ 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->line_to(1000,1000); // repeated line_to, so only takes 2 geoms: x,y | total 8
+ g->line_to(1001,1001); // should skip given tolerance of 2 | total 8
+ g->line_to(1001,1001); // should not skip given it is the endpoint, added 2 geoms | total 10
+ backend.add_path(*g, tolerance, g->type());
+ backend.stop_tile_feature();
+ backend.stop_tile_layer();
+ // done encoding single feature/geometry
+ CHECK(1 == tile.layers_size());
+ mapnik::vector::tile_layer const& layer = tile.layers(0);
+ CHECK(1 == layer.features_size());
+ mapnik::vector::tile_feature const& f = layer.features(0);
+ // sequence of 10 geometries given tolerance of 2
+ CHECK(8 == f.geometry_size());
+ // first geometry is 9, which packs both the command and how many verticies are encoded with that same command
+ // It is 9 because it is a move_to (which is an enum of 1) and there is one command (length == 1)
+ unsigned move_value = f.geometry(0);
+ // (1 << 3) | (1 & ((1 << 3) -1)) == 9
+ // (length << 3) | (MOVE_TO & ((1 << 3) -1))
+ CHECK(9 == move_value);
+ unsigned move_cmd = move_value & ((1 << 3) - 1);
+ CHECK(1 == move_cmd);
+ unsigned move_length = move_value >> 3;
+ CHECK(1 == move_length);
+ // 2nd and 3rd are the x,y of the one move_to command
+ CHECK(0 == f.geometry(1));
+ CHECK(0 == f.geometry(2));
+ // 4th is the line_to (which is an enum of 2) and a number indicating how many line_to commands are repeated
+ // in this case there should be 2 because two were skipped
+ unsigned line_value = f.geometry(3);
+ // (2 << 3) | (2 & ((1 << 3) -1)) == 18
+ CHECK(18 == line_value);
+ unsigned line_cmd = line_value & ((1 << 3) - 1);
+ CHECK(2 == line_cmd);
+ unsigned line_length = line_value >> 3;
+ CHECK(2 == line_length);
+ // 5th and 6th are the x,y of the first line_to command
+ // due zigzag encoding the 2,2 should be 4,4
+ // delta encoding has no impact since the previous coordinate was 0,0
+ unsigned four = (2 << 1) ^ (2 >> 31);
+ CHECK(4 == four);
+ CHECK(4 == f.geometry(4));
+ CHECK(4 == f.geometry(5));
+ // 7th and 8th are x,y of the second line_to command
+ // due to delta encoding 1001-2 becomes 999
+ // zigzag encoded 999 becomes 1998 == (999 << 1) ^ (999 >> 31)
+ CHECK(1998 == f.geometry(6));
+ CHECK(1998 == f.geometry(7));
+}
+
+// testcase for avoiding error in is_solid_extent of
+// "Unknown command type (is_solid_extent): 0"
+// not yet clear if this test is correct
+// 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;
+ mapnik::vector::tile tile;
+ mapnik::vector::backend_pbf backend(tile,path_multiplier);
+ backend.start_tile_layer("layer");
+ mapnik::feature_ptr feature(mapnik::feature_factory::create(MAPNIK_MAKE_SHARED<mapnik::context_type>(),1));
+ backend.start_tile_feature(*feature);
+ MAPNIK_UNIQUE_PTR<mapnik::geometry_type> g(new mapnik::geometry_type(MAPNIK_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?
+ backend.add_path(*g, tolerance, g->type());
+ backend.stop_tile_feature();
+ backend.stop_tile_layer();
+ std::string key("test");
+ is_solid_extent(tile,key); // should not throw!
+ CHECK(1 == tile.layers_size());
+ mapnik::vector::tile_layer const& layer = tile.layers(0);
+ CHECK(1 == layer.features_size());
+ mapnik::vector::tile_feature const& f = layer.features(0);
+ CHECK(7 == f.geometry_size());
+}
+
int main (int argc, char* const argv[])
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
// set up bbox
double minx,miny,maxx,maxy;
mapnik::vector::spherical_mercator merc(256);
- merc.xyz(x,y,z,minx,miny,maxx,maxy);
+ merc.xyz(_x,_y,_z,minx,miny,maxx,maxy);
bbox.init(minx,miny,maxx,maxy);
int result = Catch::Session().run( argc, argv );
if (!result) printf("\x1b[1;32m ✓ \x1b[0m\n");
--
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