[mapnik] 01/07: Imported Upstream version 3.0.12~rc1+ds

Bas Couwenberg sebastic at debian.org
Fri Aug 5 13:39:47 UTC 2016


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

sebastic pushed a commit to branch master
in repository mapnik.

commit 4cbbc3006e9f565e6d8fb291e81db9b770928291
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Aug 5 14:39:39 2016 +0200

    Imported Upstream version 3.0.12~rc1+ds
---
 .travis.yml                                        |  35 +-
 CHANGELOG.md                                       |  68 ++-
 INSTALL.md                                         |   2 +-
 README.md                                          |   5 +-
 SConstruct                                         |  31 +-
 appveyor.yml                                       |   2 +-
 bootstrap.sh                                       |  12 +-
 circle.yml                                         |  14 +-
 demo/c++/README.md                                 |   4 +-
 demo/c++/rundemo.cpp                               |   6 +-
 demo/data/boundaries.dbf                           | Bin 750 -> 749 bytes
 demo/data/boundaries.prj                           |   1 -
 demo/data/boundaries.sbx                           | Bin 116 -> 0 bytes
 demo/data/boundaries_l.dbf                         | Bin 972 -> 971 bytes
 demo/data/boundaries_l.prj                         |   1 -
 demo/data/boundaries_l.sbx                         | Bin 132 -> 0 bytes
 demo/data/ontdrainage.dbf                          | Bin 264968 -> 264967 bytes
 demo/data/ontdrainage.prj                          |   1 -
 demo/data/ontdrainage.sbx                          | Bin 644 -> 0 bytes
 demo/data/popplaces.dbf                            | Bin 93538 -> 93537 bytes
 demo/data/popplaces.prj                            |   1 -
 demo/data/popplaces.sbx                            | Bin 268 -> 0 bytes
 demo/data/qcdrainage.dbf                           | Bin 219468 -> 219467 bytes
 demo/data/qcdrainage.prj                           |   1 -
 demo/data/qcdrainage.sbx                           | Bin 644 -> 0 bytes
 demo/data/roads.dbf                                | Bin 1127356 -> 1127355 bytes
 demo/data/roads.prj                                |   1 -
 demo/data/roads.sbx                                | Bin 2060 -> 0 bytes
 deps/agg/include/agg_rasterizer_scanline_aa.h      | 161 ++++---
 deps/mapnik/build.py                               |   2 +-
 include/mapnik/box2d.hpp                           |   5 +-
 src/box2d.cpp => include/mapnik/box2d_impl.hpp     |   7 +-
 include/mapnik/config.hpp                          |  13 +-
 include/mapnik/debug.hpp                           |   6 +-
 include/mapnik/expression_grammar_impl.hpp         |   1 +
 include/mapnik/feature.hpp                         |   2 +-
 include/mapnik/featureset.hpp                      |  15 +
 include/mapnik/function_call.hpp                   |  11 +
 include/mapnik/geometry.hpp                        |  70 +--
 include/mapnik/geometry_envelope.hpp               |   4 +-
 include/mapnik/geometry_envelope_impl.hpp          |  23 +-
 include/mapnik/json/attribute_value_visitor.hpp    |  68 +++
 .../json/extract_bounding_box_grammar_impl.hpp     |   9 +-
 include/mapnik/json/feature_grammar.hpp            |  32 +-
 include/mapnik/json/feature_grammar_impl.hpp       |  26 +-
 include/mapnik/json/generic_json.hpp               |  67 ++-
 include/mapnik/json/geometry_grammar_impl.hpp      |  15 +-
 include/mapnik/json/positions_grammar.hpp          |  23 -
 include/mapnik/json/positions_grammar_impl.hpp     |  24 ++
 include/mapnik/json/stringifier.hpp                | 101 +++++
 include/mapnik/json/topojson_grammar.hpp           | 152 ++++++-
 include/mapnik/json/topojson_grammar_impl.hpp      | 184 +++-----
 include/mapnik/json/topojson_utils.hpp             | 195 +++++----
 include/mapnik/json/topology.hpp                   |   9 +-
 include/mapnik/palette.hpp                         |  21 +-
 include/mapnik/params.hpp                          |   4 +
 include/mapnik/plugin.hpp                          |   3 +-
 include/mapnik/png_io.hpp                          |  75 ++--
 .../renderer_common/process_point_symbolizer.hpp   |   2 +-
 include/mapnik/symbolizer_base.hpp                 |   4 +-
 include/mapnik/text/placement_finder.hpp           |   1 -
 include/mapnik/text/scrptrun.hpp                   |   2 +-
 include/mapnik/text/symbolizer_helpers.hpp         |   6 +-
 include/mapnik/util/char_array_buffer.hpp          |  87 ++++
 include/mapnik/util/is_clockwise.hpp               |  11 +-
 include/mapnik/util/variant.hpp                    |  10 +-
 include/mapnik/value_hash.hpp                      |  16 +-
 include/mapnik/version.hpp                         |   2 +-
 include/mapnik/vertex_adapters.hpp                 |  16 +-
 include/mapnik/warning_ignore.hpp                  |   1 +
 include/mapnik/wkt/wkt_generator_grammar.hpp       |   2 +-
 include/mapnik/xml_attribute_cast.hpp              |  15 +-
 localize.sh                                        |  10 +-
 mason_latest.sh                                    |   8 +-
 plugins/input/csv/csv_datasource.cpp               |   2 +-
 plugins/input/gdal/gdal_datasource.cpp             |  11 +-
 plugins/input/gdal/gdal_featureset.cpp             | 100 +++--
 plugins/input/gdal/gdal_featureset.hpp             |   5 -
 plugins/input/geojson/geojson_datasource.cpp       |   4 +-
 plugins/input/ogr/ogr_datasource.cpp               |  20 +-
 plugins/input/pgraster/pgraster_datasource.cpp     |  19 +-
 plugins/input/pgraster/pgraster_wkb_reader.cpp     |   2 +-
 plugins/input/postgis/postgis_datasource.cpp       |   8 +-
 plugins/input/raster/raster_datasource.cpp         |   2 +-
 plugins/input/sqlite/sqlite_datasource.cpp         |   4 +-
 plugins/input/topojson/topojson_datasource.cpp     |  25 +-
 scripts/travis-common.sh                           |   5 +-
 src/agg/process_line_pattern_symbolizer.cpp        |   2 +-
 src/agg/process_polygon_pattern_symbolizer.cpp     |   2 +-
 src/agg/process_text_symbolizer.cpp                |   2 +-
 src/box2d.cpp                                      | 475 +-------------------
 src/cairo/process_line_pattern_symbolizer.cpp      |   2 +-
 src/cairo/process_polygon_pattern_symbolizer.cpp   |   2 +-
 src/datasource_cache.cpp                           |   4 +-
 src/font_engine_freetype.cpp                       |   4 +-
 src/geometry_envelope.cpp                          |  21 +-
 src/grid/process_text_symbolizer.cpp               |   2 +-
 src/image_compositing.cpp                          |  34 +-
 src/jpeg_reader.cpp                                |  30 +-
 src/load_map.cpp                                   |  18 +-
 src/map.cpp                                        |   2 +-
 src/marker_helpers.cpp                             |   9 +
 src/palette.cpp                                    |  10 -
 src/plugin.cpp                                     |  14 +-
 src/png_reader.cpp                                 |  32 +-
 src/proj_transform.cpp                             |  91 ++--
 src/renderer_common/render_markers_symbolizer.cpp  |   4 +-
 src/text/formatting/layout.cpp                     |   6 +-
 src/text/placement_finder.cpp                      |  68 +--
 src/text/symbolizer_helpers.cpp                    |  38 +-
 src/text/text_line.cpp                             |   2 +-
 src/text/text_properties.cpp                       |  12 +-
 src/tiff_reader.cpp                                |  33 +-
 src/value.cpp                                      |   4 +-
 src/vertex_adapters.cpp                            |   8 +-
 src/webp_reader.cpp                                |   3 -
 src/xml_tree.cpp                                   |   6 +-
 test/run                                           |  14 +-
 test/standalone/font_registration_test.cpp         |  18 +-
 test/unit/core/expressions_test.cpp                |   3 +
 test/unit/datasource/ds_test_util.hpp              |  15 +-
 test/unit/datasource/geojson.cpp                   |  68 +++
 test/unit/datasource/postgis.cpp                   | 477 +++++++++++----------
 test/unit/datasource/topojson.cpp                  |  41 +-
 test/unit/geometry/geometry_envelope_test.cpp      |  86 ++--
 test/unit/geometry/geometry_equal.hpp              |  11 +-
 test/unit/geometry/geometry_test_helper.cpp        |  15 +
 test/unit/geometry/is_clockwise.cpp                |  27 ++
 test/unit/imaging/image_io_test.cpp                |  51 ++-
 test/unit/imaging/tiff_io.cpp                      |  19 +-
 test/unit/serialization/wkb_test.cpp               |   4 +
 test/visual/renderer.hpp                           | 135 ++++--
 test/visual/run.cpp                                |  39 +-
 utils/mapnik-index/mapnik-index.cpp                |  13 +-
 utils/mapnik-index/process_csv_file.cpp            |   9 +-
 utils/mapnik-render/mapnik-render.cpp              |   4 +-
 utils/pgsql2sqlite/pgsql2sqlite.hpp                |  10 +-
 137 files changed, 2137 insertions(+), 1775 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 13cf1f2..5de9299 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,4 @@
-language: c
+language: cpp
 
 git:
   depth: 10
@@ -25,26 +25,34 @@ matrix:
     - os: linux
       sudo: false
       compiler: ": clang"
-      env: JOBS=8 MASON_PUBLISH=true CXX="ccache clang++-3.5 -Qunused-arguments" CC="clang-3.5" TRIGGER=true
+      env: JOBS=8 MASON_PUBLISH=true _CXX="ccache clang++-3.8 -Qunused-arguments" _CC="clang-3.8" TRIGGER=true
       addons:
         apt:
-          sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ]
-          packages: [ 'clang-3.5' ]
+          sources: [ 'ubuntu-toolchain-r-test']
+          packages: [ 'libstdc++6', 'libstdc++-5-dev']
     - os: linux
       sudo: false
       compiler: ": clang-coverage"
-      env: JOBS=8 COVERAGE=true LLVM_VERSION="3.5" CXX="ccache clang++-3.5 -Qunused-arguments" CC="clang-3.5"
+      env: JOBS=8 COVERAGE=true _CXX="ccache clang++-3.8 -Qunused-arguments" _CC="clang-3.8"
       addons:
         apt:
-          sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ]
-          packages: [ 'clang-3.5', 'llvm-3.5-dev' ]
+          sources: [ 'ubuntu-toolchain-r-test']
+          packages: [ 'libstdc++6','libstdc++-5-dev' ]
     - os: osx
-      compiler: clang
+      compiler: ": clang-osx"
       # https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions
       osx_image: xcode7.3 # upgrades clang from 6 -> 7
-      env: JOBS=4 MASON_PUBLISH=true
+      env: JOBS=4 MASON_PUBLISH=true _CXX="ccache clang++ -Qunused-arguments"
 
 before_install:
+ # workaround travis rvm bug
+ # http://superuser.com/questions/1044130/why-am-i-having-how-can-i-fix-this-error-shell-session-update-command-not-f
+ - |
+   if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
+     rvm get head || true
+   fi
+ - if [[ ${_CXX:-false} != false ]]; then export CXX=${_CXX}; fi
+ - if [[ ${_CC:-false} != false ]]; then export CC=${_CC}; fi
  - source scripts/travis-common.sh
  - export PYTHONUSERBASE=$(pwd)/mason_packages/.link
  - export PATH=${PREFIX}/bin:$(pwd)/mason_packages/.link/bin:${PYTHONUSERBASE}/bin:${PATH}
@@ -56,7 +64,6 @@ before_install:
  - git_submodule_update --init --depth=10
 
 install:
- - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then export CCACHE_READONLY=1; fi
  - on 'linux' export PYTHONPATH=${PYTHONUSERBASE}/lib/python2.7/site-packages
  - on 'osx' export PYTHONPATH=${PYTHONUSERBASE}/lib/python/site-packages
  - on 'osx' export DATA_PATH=$(brew --prefix)/var/postgres
@@ -71,6 +78,13 @@ install:
 
 before_script:
  - source bootstrap.sh
+ - |
+   if [[ $(uname -s) == 'Linux' ]]; then
+     mason install clang 3.8.0
+     export PATH=$(mason prefix clang 3.8.0)/bin:${PATH}
+     which clang++
+     export LLVM_COV="$(mason prefix clang 3.8.0)/bin/llvm-cov"
+   fi
  - ccache --version
  - ccache -p || true
  - ccache --show-stats || true
@@ -79,6 +93,7 @@ before_script:
 script:
  - export SCONSFLAGS='--debug=time'
  - configure BENCHMARK=${BENCH}
+ - cat config.log
  - make
  - make test
  - enabled ${COVERAGE} coverage
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 762cee9..8c3c00b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,9 +6,40 @@ Developers: Please commit along with changes.
 
 For a complete change history, see the git log.
 
+## 3.0.12
+
+Released: xx-xx-xx
+
+(Packaged from xxxxxx)
+
+#### Summary
+
+- Ensured gdal.input is registered once (refs #3093 #3339 #3340)
+- Fixed `mapnik::util::is_clockwise` implementation to use coordinates relative to the origin and avoid numeric precision issues
+- `mapnik-index` is updated to fail on first error in input (csv)
+- Added `guard` to `get_object_severity` method (ref #3322)
+- Improved `hash` calculation for `mapnik::value` (ref #3406)
+- AGG - made cover `unsigned` to avoid left shift of negative values (ref #3406)
+- Fixed using `scale_factor` in `evaluate_transform(..)`
+- Fixed line spacing logic by applying `scale factor`
+- ~~Fixed `stringify_object/stringify_array` implementations by disabling white space skipping (ref #3419)~~
+- Added geojson unit test for property types/values
+- JSON - added support for object and array type in `json_value` and update `stringifier`
+- GDAL.input - fallback to using `overviews` if present (8e8482803bb435726534c3b686a56037b7d3e8ad)
+- TopoJSON.input - improved and simplified grammer/parser implementation (https://github.com/mapnik/mapnik/pull/3429)
+- GDAL.input - Added support for non-alpha mask band
+- TopoJSON.input - fixed order of ellements limitation (ref #3434)
+- Fixed stroke-width size not included in markers ellipse bounding box (ref #3445)
+- Implemented `char_array_buffer` and removed `boost::iostreams` dependency (2e8c0d36c2237f2815d8004c1b96bad909056eb9)
+- JSON.input - `extract_bounding_box_grammar` - make features optional (ref #3463)
+- Ensure input plugins return `empty_featureset` rather than `nullptr` (feature_ptr())
+- Added support for quantising small (less than 3 pixel) images (ref #3466)
+- Added support for natural logarithm function in expressions (ref #3475)
+- Improved logic determining if certain compiler features are available e.g `inheriting constructors` (MSVC)
+
 ## 3.0.11
 
-Released:
+Released: April 1, 2016
 
 (Packaged from 8d9dc27)
 
@@ -16,7 +47,10 @@ Released:
 
  - Raster scaling: fixed crash and clipping negative pixel values of floating point rasters (https://github.com/mapnik/mapnik/pull/3349)
  - Restored support for unquoted strings in expressions (https://github.com/mapnik/mapnik/pull/3390)
- 
+ - [TWKB](https://github.com/TWKB/) support via https://github.com/mapnik/mapnik/pull/3356 (#3355)
+ - Visual test runner can render SVG, PDF and Postscript with Cairo renderer (https://github.com/mapnik/mapnik/pull/3418)
+ - Scale factor is now applied also to `text-line-spacing` and transforms (https://github.com/mapnik/mapnik/pull/3416)
+
 ## 3.0.10
 
 Released: February 25, 2016
@@ -541,8 +575,8 @@ Summary: The 2.2.0 release is primarily a performance and stability release. The
 
 - Enabled default input plugin directory and fonts path to be set inherited from environment settings in
   python bindings to make it easier to run tests locally (#1594). New environment settings are:
-	- MAPNIK_INPUT_PLUGINS_DIRECTORY
-	- MAPNIK_FONT_DIRECTORY
+    - MAPNIK_INPUT_PLUGINS_DIRECTORY
+    - MAPNIK_FONT_DIRECTORY
 
 - Added support for controlling rendering behavior of markers on multi-geometries `marker-multi-policy` (#1555,#1573)
 
@@ -934,7 +968,7 @@ Released January, 19 2010
 
 - Gdal Plugin: Added support for Gdal overviews, enabling fast loading of > 1GB rasters (#54)
 
-	* Use the gdaladdo utility to add overviews to existing GDAL datasets
+    * Use the gdaladdo utility to add overviews to existing GDAL datasets
 
 - PostGIS: Added an optional `geometry_table` parameter. The `geometry_table` used by Mapnik to look up
   metadata in the geometry_columns and calculate extents (when the `geometry_field` and `srid` parameters
@@ -959,23 +993,23 @@ Released January, 19 2010
   complex queries that may aggregate geometries to be kept fast by allowing proper placement of the bbox
   query to be used by indexes. (#415)
 
-	* Pass the bbox token inside a subquery like: !bbox!
+    * Pass the bbox token inside a subquery like: !bbox!
 
-	* Valid Usages include:
+    * Valid Usages include:
 
-		<Parameter name="table">
-		  (Select ST_Union(geom) as geom from table where ST_Intersects(geometry,!bbox!)) as map
-		</Parameter>
+        <Parameter name="table">
+          (Select ST_Union(geom) as geom from table where ST_Intersects(geometry,!bbox!)) as map
+        </Parameter>
 
-		<Parameter name="table">
-		  (Select * from table where geom && !bbox!) as map
-		</Parameter>
+        <Parameter name="table">
+          (Select * from table where geom && !bbox!) as map
+        </Parameter>
 
 - PostGIS Plugin: Added `scale_denominator` substitution ability in sql query string (#415/#465)
 
-	* Pass the scale_denominator token inside a subquery like: !scale_denominator!
+    * Pass the scale_denominator token inside a subquery like: !scale_denominator!
 
-	* e.g. (Select * from table where field_value > !scale_denominator!) as map
+    * e.g. (Select * from table where field_value > !scale_denominator!) as map
 
 - PostGIS Plugin: Added support for quoted table names (r1454) (#393)
 
@@ -1007,14 +1041,14 @@ Released January, 19 2010
 - TextSymbolizer: Large set of new attributes: `text_transform`, `line_spacing`, `character_spacing`,
   `wrap_character`, `wrap_before`, `horizontal_alignment`, `justify_alignment`, and `opacity`.
 
-	* More details at changesets: r1254 and r1341
+    * More details at changesets: r1254 and r1341
 
 - SheildSymbolizer: Added special new attributes: `unlock_image`, `VERTEX` placement, `no_text` and many
   attributes previously only supported in the TextSymbolizer: `allow_overlap`, `vertical_alignment`,
   `horizontal_alignment`, `justify_alignment`, `wrap_width`, `wrap_character`, `wrap_before`, `text_transform`,
   `line_spacing`, `character_spacing`, and `opacity`.
 
-	* More details at changeset r1341
+    * More details at changeset r1341
 
 - XML: Added support for using CDATA with libxml2 parser (r1364)
 
diff --git a/INSTALL.md b/INSTALL.md
index d27004d..8992144 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -98,7 +98,7 @@ Additional optional dependencies:
  * PostgreSQL (for PostGIS plugin support)
     - libpq - PostreSQL libraries
     - pg_config - PostgreSQL installation capabilities
- * libgdal - GDAL/OGR input (For gdal and ogr plugin support)
+ * libgdal - GDAL/OGR input (For gdal and ogr plugin support) (>= GDAL 2.0.2 for thread safety - https://github.com/mapnik/mapnik/issues/3339)
  * libsqlite3 - SQLite input (needs RTree support builtin) (sqlite plugin support)
 
 Instructions for installing many of these dependencies on
diff --git a/README.md b/README.md
index b5ebf6a..e99df98 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ _/      _/    _/_/_/  _/_/_/    _/    _/  _/  _/    _/
 ```
 
 [![Build Status Linux](https://api.travis-ci.org/mapnik/mapnik.svg?branch=master)](http://travis-ci.org/mapnik/mapnik)
+[![CircleCI](https://circleci.com/gh/mapnik/mapnik.svg?style=svg)](https://circleci.com/gh/mapnik/mapnik)
 [![Build Status Windows](https://ci.appveyor.com/api/projects/status/hc9l7okdjtucfqqn?branch=master&svg=true)](https://ci.appveyor.com/project/Mapbox/mapnik)
 [![Coverage Status](https://coveralls.io/repos/mapnik/mapnik/badge.svg?branch=master&service=github)](https://coveralls.io/github/mapnik/mapnik?branch=master)
 
@@ -20,7 +21,7 @@ For further information see [http://mapnik.org](http://mapnik.org) and also our
 
 # Installation
 
-See [INSTALL.md](https://github.com/mapnik/mapnik/blob/master/INSTALL.md) for installation instructions and the [Install](https://github.com/mapnik/mapnik/wiki/Mapnik-Installation) page on the wiki for guides.
+See [INSTALL.md](INSTALL.md) for installation instructions and the [Install](https://github.com/mapnik/mapnik/wiki/Mapnik-Installation) page on the wiki for guides.
 
 # Code of Conduct
 
@@ -28,4 +29,4 @@ Please note that this project is released with a [Contributor Code of Conduct](h
 
 # License
 
-Mapnik software is free and is released under the LGPL ([GNU Lesser General Public License](http://www.gnu.org/licenses/lgpl.html_)). Please see [COPYING](https://github.com/mapnik/mapnik/blob/master/COPYING) for more information.
+Mapnik software is free and is released under the LGPL ([GNU Lesser General Public License](http://www.gnu.org/licenses/lgpl.html)). Please see [COPYING](https://github.com/mapnik/mapnik/blob/master/COPYING) for more information.
diff --git a/SConstruct b/SConstruct
index dacc950..0523e18 100644
--- a/SConstruct
+++ b/SConstruct
@@ -704,11 +704,7 @@ def FindBoost(context, prefixes, thread_flag):
     BOOST_INCLUDE_DIR = None
     BOOST_APPEND = None
     env['BOOST_APPEND'] = str()
-
-    if env['THREADING'] == 'multi':
-        search_lib = 'libboost_thread'
-    else:
-        search_lib = 'libboost_filesystem'
+    search_lib = 'libboost_filesystem'
 
     # note: must call normpath to strip trailing slash otherwise dirname
     # does not remove 'lib' and 'include'
@@ -1359,7 +1355,7 @@ if not preconfigured:
 
     # test for C++11 support, which is required
     if not env['HOST'] and not conf.supports_cxx11():
-        color_print(1,"C++ compiler does not support C++11 standard (-std=c++11), which is required. Please upgrade your compiler to at least g++ 4.7 (ideally 4.8)")
+        color_print(1,"C++ compiler does not support C++11 standard (-std=c++11), which is required. Please upgrade your compiler")
         Exit(1)
 
     if not env['HOST']:
@@ -1411,15 +1407,6 @@ if not preconfigured:
             ['program_options', 'boost/program_options.hpp', False]
         ]
 
-        if env['THREADING'] == 'multi':
-            BOOST_LIBSHEADERS.append(['thread', 'boost/thread/mutex.hpp', True])
-            # on solaris the configure checks for boost_thread
-            # require the -pthreads flag to be able to check for
-            # threading support, so we add as a global library instead
-            # of attaching to cxxflags after configure
-            if env['PLATFORM'] == 'SunOS':
-                env.Append(CXXFLAGS = '-pthreads')
-
         # if requested, sort LIBPATH and CPPPATH before running CheckLibWithHeader tests
         if env['PRIORITIZE_LINKING']:
             conf.prioritize_paths(silent=True)
@@ -1451,12 +1438,13 @@ if not preconfigured:
         # just turn it off like this, but seems the only available work-
         # around. See https://svn.boost.org/trac/boost/ticket/6779 for more
         # details.
-        boost_version = [int(x) for x in env.get('BOOST_LIB_VERSION_FROM_HEADER').split('_')]
-        if not conf.CheckBoostScopedEnum():
-            if boost_version < [1, 51]:
-                env.Append(CXXFLAGS = '-DBOOST_NO_SCOPED_ENUMS')
-            elif boost_version < [1, 57]:
-                env.Append(CXXFLAGS = '-DBOOST_NO_CXX11_SCOPED_ENUMS')
+        if not env['HOST']:
+            boost_version = [int(x) for x in env.get('BOOST_LIB_VERSION_FROM_HEADER').split('_')]
+            if not conf.CheckBoostScopedEnum():
+                if boost_version < [1, 51]:
+                    env.Append(CXXFLAGS = '-DBOOST_NO_SCOPED_ENUMS')
+                elif boost_version < [1, 57]:
+                    env.Append(CXXFLAGS = '-DBOOST_NO_CXX11_SCOPED_ENUMS')
 
     if not env['HOST'] and env['ICU_LIB_NAME'] not in env['MISSING_DEPS']:
         # http://lists.boost.org/Archives/boost/2009/03/150076.php
@@ -1609,6 +1597,7 @@ if not preconfigured:
     # prepend to make sure we link locally
     env.Prepend(CPPPATH = '#deps/agg/include')
     env.Prepend(LIBPATH = '#deps/agg')
+    env.Prepend(CPPPATH = '#deps/mapbox/variant/include')
     # prepend deps dir for auxillary headers
     env.Prepend(CPPPATH = '#deps')
 
diff --git a/appveyor.yml b/appveyor.yml
index f236021..34ddc3b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,6 @@
 environment:
   msvs_toolset: 14
-  BOOST_VERSION: 59
+  BOOST_VERSION: 60
   FASTBUILD: 1
   matrix:
     - platform: x64
diff --git a/bootstrap.sh b/bootstrap.sh
index 3a64709..fd877a6 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -10,7 +10,7 @@ todo
 - shrink icu data
 '
 
-MASON_VERSION="694d08c"
+MASON_VERSION="b709931"
 
 function setup_mason() {
     if [[ ! -d ./.mason ]]; then
@@ -20,7 +20,6 @@ function setup_mason() {
         echo "Updating to latest mason"
         (cd ./.mason && git fetch && git checkout ${MASON_VERSION})
     fi
-    export MASON_DIR=$(pwd)/.mason
     export PATH=$(pwd)/.mason:$PATH
     export CXX=${CXX:-clang++}
     export CC=${CC:-clang}
@@ -32,7 +31,7 @@ function install() {
         mason install $1 $2
         mason link $1 $2
         if [[ ${3:-false} != false ]]; then
-            LA_FILE=$(${MASON_DIR:-~/.mason}/mason prefix $1 $2)/lib/$3.la
+            LA_FILE=$(mason prefix $1 $2)/lib/$3.la
             if [[ -f ${LA_FILE} ]]; then
                perl -i -p -e 's:\Q$ENV{HOME}/build/mapbox/mason\E:$ENV{PWD}:g' ${LA_FILE}
             else
@@ -63,8 +62,11 @@ function install_mason_deps() {
     wait
     install webp 0.4.2 libwebp &
     install gdal 1.11.2 libgdal &
-    install boost 1.59.0 &
-    install boost_liball 1.59.0 &
+    install boost 1.61.0 &
+    install boost_libsystem 1.61.0 &
+    install boost_libfilesystem 1.61.0 &
+    install boost_libprogram_options 1.61.0 &
+    install boost_libregex 1.61.0 &
     install freetype 2.6 libfreetype &
     install harfbuzz 0.9.41 libharfbuzz &
     wait
diff --git a/circle.yml b/circle.yml
index e7c12d7..cf3e31c 100644
--- a/circle.yml
+++ b/circle.yml
@@ -7,7 +7,7 @@ machine:
     JOBS: 8
     CCACHE_TEMPDIR: /tmp/.ccache-temp
     CCACHE_COMPRESS: 1
-    LLVM_VERSION: 3.7
+    LLVM_VERSION: 3.8
   pre:
     - echo "here"
   post:
@@ -25,20 +25,16 @@ dependencies:
   pre:
     # https://discuss.circleci.com/t/add-ability-to-cache-apt-get-programs/598/3
     - sudo rm -rf /var/cache/apt/archives && sudo ln -s ~/.apt-cache /var/cache/apt/archives && mkdir -p ~/.apt-cache/partial
-    - sudo wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
-    - sudo add-apt-repository -y "deb http://llvm.org/apt/precise/ llvm-toolchain-precise-${LLVM_VERSION} main"
     - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
-    - sudo apt-get update -y
   override:
-    - sudo apt-get install clang-${LLVM_VERSION} -y
-  post:
-    - which clang-${LLVM_VERSION}
-    - which clang++-${LLVM_VERSION}
+    - sudo apt-get update -y
 
 database:
   pre:
     - ./bootstrap.sh
-    - ./configure CC="clang-${LLVM_VERSION}" CXX="$(pwd)/mason_packages/.link/bin/ccache clang++-${LLVM_VERSION} -Qunused-arguments"
+    - ./.mason/mason install clang ${LLVM_VERSION}.0
+    - ./.mason/mason link clang ${LLVM_VERSION}.0
+    - ./configure CC="$(pwd)/mason_packages/.link/bin/clang-${LLVM_VERSION}" CXX="$(pwd)/mason_packages/.link/bin/ccache $(pwd)/mason_packages/.link/bin/clang++-${LLVM_VERSION} -Qunused-arguments"
     - make
   override:
     - psql -c 'create database template_postgis;'
diff --git a/demo/c++/README.md b/demo/c++/README.md
index daa4198..548508e 100644
--- a/demo/c++/README.md
+++ b/demo/c++/README.md
@@ -52,11 +52,11 @@ If you do not have svn installed you can grab gyp from:
 
 Simply type:
 
-   make
+    make
 
 Then to run do:
 
-   ./rundemo `mapnik-config --prefix`
+    ./rundemo `mapnik-config --prefix`
 
 On OS X you can also create an xcode project:
 
diff --git a/demo/c++/rundemo.cpp b/demo/c++/rundemo.cpp
index c07bd5c..c20c288 100644
--- a/demo/c++/rundemo.cpp
+++ b/demo/c++/rundemo.cpp
@@ -55,7 +55,7 @@ int main ( int, char** )
     try {
         std::cout << " running demo ... \n";
         datasource_cache::instance().register_datasources("plugins/input/");
-        freetype_engine::register_font("fonts/dejavu-fonts-ttf-2.34/ttf/DejaVuSans.ttf");
+        freetype_engine::register_font("fonts/dejavu-fonts-ttf-2.35/ttf/DejaVuSans.ttf");
 
         Map m(800,600);
         m.set_background(parse_color("white"));
@@ -230,7 +230,7 @@ int main ( int, char** )
             parameters p;
             p["type"]="shape";
             p["file"]="demo/data/boundaries";
-            p["encoding"]="latin1";
+            p["encoding"]="utf8";
 
             layer lyr("Provinces");
             lyr.set_datasource(datasource_cache::instance().create(p));
@@ -295,7 +295,7 @@ int main ( int, char** )
             parameters p;
             p["type"]="shape";
             p["file"]="demo/data/popplaces";
-            p["encoding"] = "latin1";
+            p["encoding"] = "utf8";
             layer lyr("Populated Places");
             lyr.set_srs(srs_lcc);
             lyr.set_datasource(datasource_cache::instance().create(p));
diff --git a/demo/data/boundaries.dbf b/demo/data/boundaries.dbf
index aeed235..04d2ed6 100644
Binary files a/demo/data/boundaries.dbf and b/demo/data/boundaries.dbf differ
diff --git a/demo/data/boundaries.prj b/demo/data/boundaries.prj
deleted file mode 100644
index 21aa9c3..0000000
--- a/demo/data/boundaries.prj
+++ /dev/null
@@ -1 +0,0 @@
-PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/demo/data/boundaries.sbx b/demo/data/boundaries.sbx
deleted file mode 100644
index c66e6e3..0000000
Binary files a/demo/data/boundaries.sbx and /dev/null differ
diff --git a/demo/data/boundaries_l.dbf b/demo/data/boundaries_l.dbf
index e6bc20c..44d4ca8 100644
Binary files a/demo/data/boundaries_l.dbf and b/demo/data/boundaries_l.dbf differ
diff --git a/demo/data/boundaries_l.prj b/demo/data/boundaries_l.prj
deleted file mode 100644
index 21aa9c3..0000000
--- a/demo/data/boundaries_l.prj
+++ /dev/null
@@ -1 +0,0 @@
-PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/demo/data/boundaries_l.sbx b/demo/data/boundaries_l.sbx
deleted file mode 100644
index e609d80..0000000
Binary files a/demo/data/boundaries_l.sbx and /dev/null differ
diff --git a/demo/data/ontdrainage.dbf b/demo/data/ontdrainage.dbf
index 33d8004..8192650 100644
Binary files a/demo/data/ontdrainage.dbf and b/demo/data/ontdrainage.dbf differ
diff --git a/demo/data/ontdrainage.prj b/demo/data/ontdrainage.prj
deleted file mode 100644
index 21aa9c3..0000000
--- a/demo/data/ontdrainage.prj
+++ /dev/null
@@ -1 +0,0 @@
-PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/demo/data/ontdrainage.sbx b/demo/data/ontdrainage.sbx
deleted file mode 100644
index 3739c61..0000000
Binary files a/demo/data/ontdrainage.sbx and /dev/null differ
diff --git a/demo/data/popplaces.dbf b/demo/data/popplaces.dbf
index 408968e..d385b6f 100644
Binary files a/demo/data/popplaces.dbf and b/demo/data/popplaces.dbf differ
diff --git a/demo/data/popplaces.prj b/demo/data/popplaces.prj
deleted file mode 100644
index 21aa9c3..0000000
--- a/demo/data/popplaces.prj
+++ /dev/null
@@ -1 +0,0 @@
-PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/demo/data/popplaces.sbx b/demo/data/popplaces.sbx
deleted file mode 100644
index c028881..0000000
Binary files a/demo/data/popplaces.sbx and /dev/null differ
diff --git a/demo/data/qcdrainage.dbf b/demo/data/qcdrainage.dbf
index 95ce20f..04fcf85 100644
Binary files a/demo/data/qcdrainage.dbf and b/demo/data/qcdrainage.dbf differ
diff --git a/demo/data/qcdrainage.prj b/demo/data/qcdrainage.prj
deleted file mode 100644
index 21aa9c3..0000000
--- a/demo/data/qcdrainage.prj
+++ /dev/null
@@ -1 +0,0 @@
-PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/demo/data/qcdrainage.sbx b/demo/data/qcdrainage.sbx
deleted file mode 100644
index c73534e..0000000
Binary files a/demo/data/qcdrainage.sbx and /dev/null differ
diff --git a/demo/data/roads.dbf b/demo/data/roads.dbf
index 73c1db9..709f881 100644
Binary files a/demo/data/roads.dbf and b/demo/data/roads.dbf differ
diff --git a/demo/data/roads.prj b/demo/data/roads.prj
deleted file mode 100644
index 21aa9c3..0000000
--- a/demo/data/roads.prj
+++ /dev/null
@@ -1 +0,0 @@
-PROJCS["Atlas of Canada Lambert Conformal Conic",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-95.0],PARAMETER["Standard_Parallel_1",49.0],PARAMETER["Standard_Parallel_2",77.0],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/demo/data/roads.sbx b/demo/data/roads.sbx
deleted file mode 100644
index b804cb3..0000000
Binary files a/demo/data/roads.sbx and /dev/null differ
diff --git a/deps/agg/include/agg_rasterizer_scanline_aa.h b/deps/agg/include/agg_rasterizer_scanline_aa.h
index 77bc41b..b2d4e3e 100644
--- a/deps/agg/include/agg_rasterizer_scanline_aa.h
+++ b/deps/agg/include/agg_rasterizer_scanline_aa.h
@@ -2,15 +2,15 @@
 // Anti-Grain Geometry - Version 2.4
 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
 //
-// Permission to copy, use, modify, sell and distribute this software 
-// is granted provided this copyright notice appears in all copies. 
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all copies.
 // This software is provided "as is" without express or implied
 // warranty, and with no claim as to its suitability for any purpose.
 //
 //----------------------------------------------------------------------------
 //
-// The author gratefully acknowleges the support of David Turner, 
-// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType 
+// The author gratefully acknowleges the support of David Turner,
+// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
 // libray - in producing this work. See http://www.freetype.org for details.
 //
 //----------------------------------------------------------------------------
@@ -19,12 +19,12 @@
 //          http://www.antigrain.com
 //----------------------------------------------------------------------------
 //
-// Adaptation for 32-bit screen coordinates has been sponsored by 
+// Adaptation for 32-bit screen coordinates has been sponsored by
 // Liberty Technology Systems, Inc., visit http://lib-sys.com
 //
 // Liberty Technology Systems, Inc. is the provider of
 // PostScript and PDF technology for software developers.
-// 
+//
 //----------------------------------------------------------------------------
 #ifndef AGG_RASTERIZER_SCANLINE_AA_INCLUDED
 #define AGG_RASTERIZER_SCANLINE_AA_INCLUDED
@@ -39,8 +39,8 @@ namespace agg
 
 
     //-----------------------------------------------------------------cell_aa
-    // A pixel cell. There're no constructors defined and it was done 
-    // intentionally in order to avoid extra overhead when allocating an 
+    // A pixel cell. There're no constructors defined and it was done
+    // intentionally in order to avoid extra overhead when allocating an
     // array of cells.
     struct cell_aa
     {
@@ -67,10 +67,10 @@ namespace agg
 
 
     //==================================================rasterizer_scanline_aa
-    // Polygon rasterizer that is used to render filled polygons with 
-    // high-quality Anti-Aliasing. Internally, by default, the class uses 
-    // integer coordinates in format 24.8, i.e. 24 bits for integer part 
-    // and 8 bits for fractional - see poly_subpixel_shift. This class can be 
+    // Polygon rasterizer that is used to render filled polygons with
+    // high-quality Anti-Aliasing. Internally, by default, the class uses
+    // integer coordinates in format 24.8, i.e. 24 bits for integer part
+    // and 8 bits for fractional - see poly_subpixel_shift. This class can be
     // used in the following  way:
     //
     // 1. filling_rule(filling_rule_e ft) - optional.
@@ -79,20 +79,20 @@ namespace agg
     //
     // 3. reset()
     //
-    // 4. move_to(x, y) / line_to(x, y) - make the polygon. One can create 
+    // 4. move_to(x, y) / line_to(x, y) - make the polygon. One can create
     //    more than one contour, but each contour must consist of at least 3
     //    vertices, i.e. move_to(x1, y1); line_to(x2, y2); line_to(x3, y3);
     //    is the absolute minimum of vertices that define a triangle.
     //    The algorithm does not check either the number of vertices nor
-    //    coincidence of their coordinates, but in the worst case it just 
+    //    coincidence of their coordinates, but in the worst case it just
     //    won't draw anything.
-    //    The orger of the vertices (clockwise or counterclockwise) 
+    //    The orger of the vertices (clockwise or counterclockwise)
     //    is important when using the non-zero filling rule (fill_non_zero).
     //    In this case the vertex order of all the contours must be the same
     //    if you want your intersecting polygons to be without "holes".
-    //    You actually can use different vertices order. If the contours do not 
-    //    intersect each other the order is not important anyway. If they do, 
-    //    contours with the same vertex order will be rendered without "holes" 
+    //    You actually can use different vertices order. If the contours do not
+    //    intersect each other the order is not important anyway. If they do,
+    //    contours with the same vertex order will be rendered without "holes"
     //    while the intersecting contours with different orders will have "holes".
     //
     // filling_rule() and gamma() can be called anytime before "sweeping".
@@ -122,7 +122,7 @@ namespace agg
         };
 
         //--------------------------------------------------------------------
-        rasterizer_scanline_aa() : 
+        rasterizer_scanline_aa() :
             m_outline(),
             m_clipper(),
             m_filling_rule(fill_non_zero),
@@ -136,8 +136,8 @@ namespace agg
         }
 
         //--------------------------------------------------------------------
-        template<class GammaF> 
-        rasterizer_scanline_aa(const GammaF& gamma_function) : 
+        template<class GammaF>
+        rasterizer_scanline_aa(const GammaF& gamma_function) :
             m_outline(),
             m_clipper(m_outline),
             m_filling_rule(fill_non_zero),
@@ -150,7 +150,7 @@ namespace agg
         }
 
         //--------------------------------------------------------------------
-        void reset(); 
+        void reset();
         void reset_clipping();
         void clip_box(double x1, double y1, double x2, double y2);
         void filling_rule(filling_rule_e filling_rule);
@@ -158,7 +158,7 @@ namespace agg
 
         //--------------------------------------------------------------------
         template<class GammaF> void gamma(const GammaF& gamma_function)
-        { 
+        {
             int i;
             for(i = 0; i < aa_scale; i++)
             {
@@ -167,9 +167,9 @@ namespace agg
         }
 
         //--------------------------------------------------------------------
-        unsigned apply_gamma(unsigned cover) const 
-        { 
-            return m_gamma[cover]; 
+        unsigned apply_gamma(unsigned cover) const
+        {
+            return m_gamma[cover];
         }
 
         //--------------------------------------------------------------------
@@ -198,7 +198,7 @@ namespace agg
                 add_vertex(x, y, cmd);
             }
         }
-        
+
         //--------------------------------------------------------------------
         int min_x() const { return m_outline.min_x(); }
         int min_y() const { return m_outline.min_y(); }
@@ -237,7 +237,7 @@ namespace agg
                 sl.reset_spans();
                 unsigned num_cells = m_outline.scanline_num_cells(m_scan_y);
                 const cell_aa* const* cells = m_outline.scanline_cells(m_scan_y);
-                int cover = 0;
+                unsigned cover = 0;
 
                 while(num_cells)
                 {
@@ -276,7 +276,7 @@ namespace agg
                         }
                     }
                 }
-        
+
                 if(sl.num_spans()) break;
                 ++m_scan_y;
             }
@@ -294,7 +294,7 @@ namespace agg
         //--------------------------------------------------------------------
         // Disable copying
         rasterizer_scanline_aa(const rasterizer_scanline_aa<Clip>&);
-        const rasterizer_scanline_aa<Clip>& 
+        const rasterizer_scanline_aa<Clip>&
         operator = (const rasterizer_scanline_aa<Clip>&);
 
     private:
@@ -321,32 +321,32 @@ namespace agg
 
 
     //------------------------------------------------------------------------
-    template<class Clip> 
-    void rasterizer_scanline_aa<Clip>::reset() 
-    { 
-        m_outline.reset(); 
+    template<class Clip>
+    void rasterizer_scanline_aa<Clip>::reset()
+    {
+        m_outline.reset();
         m_status = status_initial;
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
-    void rasterizer_scanline_aa<Clip>::filling_rule(filling_rule_e filling_rule) 
-    { 
-        m_filling_rule = filling_rule; 
+    template<class Clip>
+    void rasterizer_scanline_aa<Clip>::filling_rule(filling_rule_e filling_rule)
+    {
+        m_filling_rule = filling_rule;
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
-    void rasterizer_scanline_aa<Clip>::clip_box(double x1, double y1, 
+    template<class Clip>
+    void rasterizer_scanline_aa<Clip>::clip_box(double x1, double y1,
                                                 double x2, double y2)
     {
         reset();
-        m_clipper.clip_box(conv_type::upscale(x1), conv_type::upscale(y1), 
+        m_clipper.clip_box(conv_type::upscale(x1), conv_type::upscale(y1),
                            conv_type::upscale(x2), conv_type::upscale(y2));
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     void rasterizer_scanline_aa<Clip>::reset_clipping()
     {
         reset();
@@ -354,7 +354,7 @@ namespace agg
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     void rasterizer_scanline_aa<Clip>::close_polygon()
     {
         if(m_status == status_line_to)
@@ -365,56 +365,56 @@ namespace agg
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     void rasterizer_scanline_aa<Clip>::move_to(int x, int y)
     {
         if(m_outline.sorted()) reset();
         if(m_auto_close) close_polygon();
-        m_clipper.move_to(m_start_x = conv_type::downscale(x), 
+        m_clipper.move_to(m_start_x = conv_type::downscale(x),
                           m_start_y = conv_type::downscale(y));
         m_status = status_move_to;
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     void rasterizer_scanline_aa<Clip>::line_to(int x, int y)
     {
-        m_clipper.line_to(m_outline, 
-                          conv_type::downscale(x), 
+        m_clipper.line_to(m_outline,
+                          conv_type::downscale(x),
                           conv_type::downscale(y));
         m_status = status_line_to;
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
-    void rasterizer_scanline_aa<Clip>::move_to_d(double x, double y) 
-    { 
+    template<class Clip>
+    void rasterizer_scanline_aa<Clip>::move_to_d(double x, double y)
+    {
         if(m_outline.sorted()) reset();
         if(m_auto_close) close_polygon();
-        m_clipper.move_to(m_start_x = conv_type::upscale(x), 
-                          m_start_y = conv_type::upscale(y)); 
+        m_clipper.move_to(m_start_x = conv_type::upscale(x),
+                          m_start_y = conv_type::upscale(y));
         m_status = status_move_to;
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
-    void rasterizer_scanline_aa<Clip>::line_to_d(double x, double y) 
-    { 
-        m_clipper.line_to(m_outline, 
-                          conv_type::upscale(x), 
-                          conv_type::upscale(y)); 
+    template<class Clip>
+    void rasterizer_scanline_aa<Clip>::line_to_d(double x, double y)
+    {
+        m_clipper.line_to(m_outline,
+                          conv_type::upscale(x),
+                          conv_type::upscale(y));
         m_status = status_line_to;
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     void rasterizer_scanline_aa<Clip>::add_vertex(double x, double y, unsigned cmd)
     {
-        if(is_move_to(cmd)) 
+        if(is_move_to(cmd))
         {
             move_to_d(x, y);
         }
-        else 
+        else
         if(is_vertex(cmd))
         {
             line_to_d(x, y);
@@ -427,32 +427,32 @@ namespace agg
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     void rasterizer_scanline_aa<Clip>::edge(int x1, int y1, int x2, int y2)
     {
         if(m_outline.sorted()) reset();
         m_clipper.move_to(conv_type::downscale(x1), conv_type::downscale(y1));
-        m_clipper.line_to(m_outline, 
-                          conv_type::downscale(x2), 
+        m_clipper.line_to(m_outline,
+                          conv_type::downscale(x2),
                           conv_type::downscale(y2));
         m_status = status_move_to;
     }
-    
+
     //------------------------------------------------------------------------
-    template<class Clip> 
-    void rasterizer_scanline_aa<Clip>::edge_d(double x1, double y1, 
+    template<class Clip>
+    void rasterizer_scanline_aa<Clip>::edge_d(double x1, double y1,
                                               double x2, double y2)
     {
         if(m_outline.sorted()) reset();
-        m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1)); 
-        m_clipper.line_to(m_outline, 
-                          conv_type::upscale(x2), 
-                          conv_type::upscale(y2)); 
+        m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1));
+        m_clipper.line_to(m_outline,
+                          conv_type::upscale(x2),
+                          conv_type::upscale(y2));
         m_status = status_move_to;
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     void rasterizer_scanline_aa<Clip>::sort()
     {
         if(m_auto_close) close_polygon();
@@ -460,12 +460,12 @@ namespace agg
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     AGG_INLINE bool rasterizer_scanline_aa<Clip>::rewind_scanlines()
     {
         if(m_auto_close) close_polygon();
         m_outline.sort_cells();
-        if(m_outline.total_cells() == 0) 
+        if(m_outline.total_cells() == 0)
         {
             return false;
         }
@@ -475,14 +475,14 @@ namespace agg
 
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     AGG_INLINE bool rasterizer_scanline_aa<Clip>::navigate_scanline(int y)
     {
         if(m_auto_close) close_polygon();
         m_outline.sort_cells();
-        if(m_outline.total_cells() == 0 || 
-           y < m_outline.min_y() || 
-           y > m_outline.max_y()) 
+        if(m_outline.total_cells() == 0 ||
+           y < m_outline.min_y() ||
+           y > m_outline.max_y())
         {
             return false;
         }
@@ -491,7 +491,7 @@ namespace agg
     }
 
     //------------------------------------------------------------------------
-    template<class Clip> 
+    template<class Clip>
     bool rasterizer_scanline_aa<Clip>::hit_test(int tx, int ty)
     {
         if(!navigate_scanline(ty)) return false;
@@ -507,4 +507,3 @@ namespace agg
 
 
 #endif
-
diff --git a/deps/mapnik/build.py b/deps/mapnik/build.py
index 0d6dc84..76529fb 100644
--- a/deps/mapnik/build.py
+++ b/deps/mapnik/build.py
@@ -7,7 +7,7 @@ subdirs =  {
   './sparsehash':{'dir':'sparsehash','glob':'*'},
   './sparsehash/internal':{'dir':'sparsehash/internal','glob':'*'},
   '../agg/include':{'dir':'agg','glob':'agg*'},
-  '../mapbox':{'dir':'mapbox/variant','glob':'*/*.hpp'}
+  '../mapbox/variant/include':{'dir':'mapbox','glob':'*/*.hpp'}
 }
 
 if 'install' in COMMAND_LINE_TARGETS:
diff --git a/include/mapnik/box2d.hpp b/include/mapnik/box2d.hpp
index a790c1d..2671201 100644
--- a/include/mapnik/box2d.hpp
+++ b/include/mapnik/box2d.hpp
@@ -40,9 +40,8 @@ struct trans_affine;
 
 namespace mapnik {
 
-/*!
- * A spatial envelope (i.e. bounding box) which also defines some basic operators.
- */
+// A spatial envelope (i.e. bounding box) which also defines some basic operators.
+
 template <typename T> class MAPNIK_DECL box2d
 : boost::equality_comparable<box2d<T> ,
                              boost::addable<box2d<T>,
diff --git a/src/box2d.cpp b/include/mapnik/box2d_impl.hpp
similarity index 98%
copy from src/box2d.cpp
copy to include/mapnik/box2d_impl.hpp
index 7d54233..7f3e9d1 100644
--- a/src/box2d.cpp
+++ b/include/mapnik/box2d_impl.hpp
@@ -2,7 +2,7 @@
  *
  * This file is part of Mapnik (c++ mapping toolkit)
  *
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2016 Artem Pavlenko
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -493,9 +493,4 @@ box2d<T>& box2d<T>::operator*=(agg::trans_affine const& tr)
     expand_to_include(static_cast<T>(x3), static_cast<T>(y3));
     return *this;
 }
-
-template class box2d<int>;
-template class box2d<float>;
-template class box2d<double>;
-
 }
diff --git a/include/mapnik/config.hpp b/include/mapnik/config.hpp
index 6461a6c..a05caf9 100644
--- a/include/mapnik/config.hpp
+++ b/include/mapnik/config.hpp
@@ -53,6 +53,17 @@
 #define PROJ_ENVELOPE_POINTS 20
 
 #define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
-#define BOOST_MPL_LIMIT_VECTOR_SIZE 30
+
+#ifndef BOOST_MPL_LIMIT_VECTOR_SIZE
+ #define BOOST_MPL_LIMIT_VECTOR_SIZE 30
+#else
+ #warning "WARNING: BOOST_MPL_LIMIT_VECTOR_SIZE is already defined. Ensure config.hpp is included before any Boost headers"
+#endif
+
+#ifndef BOOST_MPL_LIMIT_LIST_SIZE
+ #define BOOST_MPL_LIMIT_LIST_SIZE 30
+#else
+ #warning "WARNING: BOOST_MPL_LIMIT_LIST_SIZE is already defined. Ensure config.hpp is included before any Boost headers"
+#endif
 
 #endif // MAPNIK_CONFIG_HPP
diff --git a/include/mapnik/debug.hpp b/include/mapnik/debug.hpp
index cf5e5b9..8429095 100644
--- a/include/mapnik/debug.hpp
+++ b/include/mapnik/debug.hpp
@@ -69,15 +69,15 @@ public:
 
     static void set_severity(severity_type severity_level)
     {
-//#ifdef MAPNIK_THREADSAFE
-//        std::lock_guard<std::mutex> lock(severity_mutex_);
-//#endif
         severity_level_ = severity_level;
     }
 
     // per object security levels
     static severity_type get_object_severity(std::string const& object_name)
     {
+#ifdef MAPNIK_THREADSAFE
+        std::lock_guard<std::mutex> lock(severity_mutex_);
+#endif
         severity_map::iterator it = object_severity_level_.find(object_name);
         if (object_name.empty() || it == object_severity_level_.end())
         {
diff --git a/include/mapnik/expression_grammar_impl.hpp b/include/mapnik/expression_grammar_impl.hpp
index 40be6b9..ad28f01 100644
--- a/include/mapnik/expression_grammar_impl.hpp
+++ b/include/mapnik/expression_grammar_impl.hpp
@@ -102,6 +102,7 @@ unary_function_types::unary_function_types()
         ("tan",  tan_impl())
         ("atan", atan_impl())
         ("exp",  exp_impl())
+        ("log",  log_impl())
         ("abs",  abs_impl())
         ("length",length_impl())
         ;
diff --git a/include/mapnik/feature.hpp b/include/mapnik/feature.hpp
index f340c83..1300d32 100644
--- a/include/mapnik/feature.hpp
+++ b/include/mapnik/feature.hpp
@@ -154,7 +154,7 @@ public:
 
     inline bool has_key(context_type::key_type const& key) const
     {
-        return (ctx_->mapping_.find(key) != ctx_->mapping_.end());
+        return (ctx_->mapping_.count(key) == 1);
     }
 
     inline value_type const& get(context_type::key_type const& key) const
diff --git a/include/mapnik/featureset.hpp b/include/mapnik/featureset.hpp
index c6829d1..6886d87 100644
--- a/include/mapnik/featureset.hpp
+++ b/include/mapnik/featureset.hpp
@@ -41,8 +41,23 @@ struct MAPNIK_DECL Featureset : private util::noncopyable
     virtual ~Featureset() {}
 };
 
+
+struct MAPNIK_DECL empty_featureset final : Featureset
+{
+    feature_ptr next()
+    {
+        return feature_ptr();
+    }
+    ~empty_featureset() {}
+};
+
 using featureset_ptr = std::shared_ptr<Featureset>;
 
+inline featureset_ptr make_empty_featureset()
+{
+    return std::make_shared<empty_featureset>();
+}
+
 }
 
 #endif // MAPNIK_FEATURESET_HPP
diff --git a/include/mapnik/function_call.hpp b/include/mapnik/function_call.hpp
index efbaa5f..f50829b 100644
--- a/include/mapnik/function_call.hpp
+++ b/include/mapnik/function_call.hpp
@@ -50,6 +50,17 @@ struct exp_impl
 
 };
 
+// log
+struct log_impl
+{
+    //using type = T;
+    value_type operator() (value_type const& val) const
+    {
+        return std::log(val.to_double());
+    }
+
+};
+
 // sin
 struct sin_impl
 {
diff --git a/include/mapnik/geometry.hpp b/include/mapnik/geometry.hpp
index 7063685..aae2bf3 100644
--- a/include/mapnik/geometry.hpp
+++ b/include/mapnik/geometry.hpp
@@ -24,43 +24,41 @@
 #define MAPNIK_GEOMETRY_HPP
 
 #include <mapnik/util/variant.hpp>
-#include <mapnik/coord.hpp>
 #include <vector>
 #include <type_traits>
 #include <cstddef>
 
-
-
 namespace mapnik { namespace geometry {
 
 template <typename T>
 struct point
 {
-    using value_type = T;
+    using coord_type = T;
     point() {}
     point(T x_, T y_)
         : x(x_), y(y_)
     {}
-    // temp - remove when geometry is templated on value_type
-    point(mapnik::coord<double, 2> const& c)
-        : x(c.x), y(c.y) {}
 
-    friend inline bool operator== (point<T> const& a, point<T> const& b)
-    {
-        return a.x == b.x && a.y == b.y;
-    }
-    friend inline bool operator!= (point<T> const& a, point <T> const& b)
-    {
-        return a.x != b.x  || a.y != b.y;
-    }
-    value_type x;
-    value_type y;
+    coord_type x;
+    coord_type y;
 };
 
+template <typename T>
+bool operator==(point<T> const& lhs, point<T> const& rhs)
+{
+    return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+template <typename T>
+bool operator!=(point<T> const& lhs, point<T> const& rhs)
+{
+    return !(lhs == rhs);
+}
 
 template <typename T>
 struct line_string : std::vector<point<T> >
 {
+    using coord_type = T;
     line_string() = default;
     explicit line_string(std::size_t size)
         : std::vector<point<T> >(size) {}
@@ -71,6 +69,7 @@ struct line_string : std::vector<point<T> >
 template <typename T>
 struct linear_ring : line_string<T>
 {
+    using coord_type = T;
     linear_ring() = default;
     explicit linear_ring(std::size_t size)
         : line_string<T>(size) {}
@@ -86,8 +85,9 @@ using rings_container = std::vector<linear_ring<T>>;
 template <typename T, template <typename> class InteriorRings = rings_container>
 struct polygon
 {
-    linear_ring<T> exterior_ring;
+    using coord_type = T;
     using rings_container = InteriorRings<T>;
+    linear_ring<T> exterior_ring;
     rings_container interior_rings;
 
     inline void set_exterior_ring(linear_ring<T> && ring)
@@ -109,13 +109,22 @@ struct polygon
 };
 
 template <typename T>
-struct multi_point : line_string<T> {};
+struct multi_point : line_string<T>
+{
+    using coord_type = T;
+};
 
 template <typename T>
-struct multi_line_string : std::vector<line_string<T>> {};
+struct multi_line_string : std::vector<line_string<T>>
+{
+    using coord_type = T;
+};
 
 template <typename T>
-struct multi_polygon : std::vector<polygon<T>> {};
+struct multi_polygon : std::vector<polygon<T>>
+{
+    using coord_type = T;
+};
 
 template <typename T>
 struct geometry_collection;
@@ -131,23 +140,32 @@ using geometry_base = mapnik::util::variant<geometry_empty,
                                             multi_point<T>,
                                             multi_line_string<T>,
                                             multi_polygon<T>,
-                                            mapnik::util::recursive_wrapper<geometry_collection<T> > >;
+                                            geometry_collection<T> >;
 template <typename T>
 struct geometry : geometry_base<T>
 {
-    using value_type = T;
+    using coord_type = T;
+
+#if __cpp_inheriting_constructors >= 200802
+
+    using geometry_base<T>::geometry_base;
 
-    geometry()
-        : geometry_base<T>() {} // empty
+#else
+
+    geometry() = default;
 
     template <typename G>
     geometry(G && geom)
         : geometry_base<T>(std::forward<G>(geom)) {}
 
+#endif
 };
 
 template <typename T>
-struct geometry_collection : std::vector<geometry<T>> {};
+struct geometry_collection : std::vector<geometry<T>>
+{
+    using coord_type = T;
+};
 
 }}
 
diff --git a/include/mapnik/geometry_envelope.hpp b/include/mapnik/geometry_envelope.hpp
index 17b8fcb..981b978 100644
--- a/include/mapnik/geometry_envelope.hpp
+++ b/include/mapnik/geometry_envelope.hpp
@@ -26,11 +26,11 @@
 #include <mapnik/config.hpp>
 #include <mapnik/box2d.hpp>
 
-namespace mapnik {     
+namespace mapnik {
 namespace geometry {
 
 template <typename T>
-MAPNIK_DECL mapnik::box2d<double> envelope(T const& geom);
+MAPNIK_DECL auto envelope(T const& geom) -> box2d<typename T::coord_type>;
 
 } // end ns geometry
 } // end ns mapnik
diff --git a/include/mapnik/geometry_envelope_impl.hpp b/include/mapnik/geometry_envelope_impl.hpp
index 5c39bc3..2ebddb9 100644
--- a/include/mapnik/geometry_envelope_impl.hpp
+++ b/include/mapnik/geometry_envelope_impl.hpp
@@ -28,23 +28,24 @@ namespace mapnik { namespace geometry {
 
 namespace detail {
 
+template <typename T>
 struct geometry_envelope
 {
-    using bbox_type = box2d<double>;
+    using coord_type = T;
+    using bbox_type = box2d<coord_type>;
     bbox_type & bbox;
 
     geometry_envelope(bbox_type & bbox_)
         : bbox(bbox_) {}
 
-    template <typename T>
-    void operator() (T const& geom) const
+    template <typename U>
+    void operator() (U const& geom) const
     {
         return mapnik::util::apply_visitor(*this, geom);
     }
 
     void operator() (mapnik::geometry::geometry_empty const&) const {}
 
-    template <typename T>
     void operator() (mapnik::geometry::point<T> const& pt) const
     {
         if (!bbox.valid())
@@ -54,7 +55,6 @@ struct geometry_envelope
         bbox.expand_to_include(pt.x, pt.y);
     }
 
-    template <typename T>
     void operator() (mapnik::geometry::line_string<T> const& line) const
     {
         bool first = true;
@@ -72,13 +72,11 @@ struct geometry_envelope
         }
     }
 
-    template <typename T>
     void operator() (mapnik::geometry::linear_ring<T> const& ring) const
     {
         (*this)(static_cast<mapnik::geometry::line_string<T> const&>(ring));
     }
 
-    template <typename T>
     void operator() (mapnik::geometry::polygon<T> const& poly) const
     {
         bool first = true;
@@ -96,7 +94,6 @@ struct geometry_envelope
         }
     }
 
-    template <typename T>
     void operator() (mapnik::geometry::multi_point<T> const& multi_point) const
     {
         bool first = true;
@@ -114,7 +111,6 @@ struct geometry_envelope
         }
     }
 
-    template <typename T>
     void operator() (mapnik::geometry::multi_line_string<T> const& multi_line) const
     {
         for (auto const& line : multi_line)
@@ -123,7 +119,6 @@ struct geometry_envelope
         }
     }
 
-    template <typename T>
     void operator() (mapnik::geometry::multi_polygon<T> const& multi_poly) const
     {
         for (auto const& poly : multi_poly)
@@ -132,7 +127,6 @@ struct geometry_envelope
         }
     }
 
-    template <typename T>
     void operator() (mapnik::geometry::geometry_collection<T> const& collection) const
     {
         for (auto const& geom : collection)
@@ -145,10 +139,11 @@ struct geometry_envelope
 } // end ns detail
 
 template <typename T>
-mapnik::box2d<double> envelope(T const& geom)
+auto envelope(T const& geom) -> box2d<typename T::coord_type>
 {
-    box2d<double> bbox;
-    detail::geometry_envelope op(bbox);
+    using coord_type = typename T::coord_type;
+    box2d<coord_type> bbox;
+    detail::geometry_envelope<coord_type> op(bbox);
     op(geom);
     return bbox;
 }
diff --git a/include/mapnik/json/attribute_value_visitor.hpp b/include/mapnik/json/attribute_value_visitor.hpp
new file mode 100644
index 0000000..206530c
--- /dev/null
+++ b/include/mapnik/json/attribute_value_visitor.hpp
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#ifndef MAPNIK_JSON_ATTRIBUTE_VALUE_VISITOR_HPP
+#define MAPNIK_JSON_ATTRIBUTE_VALUE_VISITOR_HPP
+
+// mapnik
+#include <mapnik/value.hpp>
+#include <mapnik/unicode.hpp>
+#include <mapnik/json/stringifier.hpp>
+#include <mapnik/json/value_converters.hpp>
+
+namespace mapnik { namespace json {
+
+struct attribute_value_visitor
+{
+public:
+    attribute_value_visitor(mapnik::transcoder const& tr)
+        : tr_(tr) {}
+
+    mapnik::value operator()(std::string const& val) const
+    {
+        return mapnik::value(tr_.transcode(val.c_str()));
+    }
+
+    mapnik::value operator()(std::vector<mapnik::json::json_value> const& array) const
+    {
+        std::string str = stringifier()(array);
+        return mapnik::value(tr_.transcode(str.c_str()));
+    }
+
+    mapnik::value operator()(std::vector<std::pair<std::string, mapnik::json::json_value> > const& object) const
+    {
+        std::string str = stringifier()(object);
+        return mapnik::value(tr_.transcode(str.c_str()));
+    }
+
+    template <typename T>
+    mapnik::value operator()(T const& val) const
+    {
+        return mapnik::value(val);
+    }
+
+    mapnik::transcoder const& tr_;
+};
+
+}}
+
+#endif //MAPNIK_JSON_ATTRIBUTE_VALUE_VISITOR_HPP
diff --git a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp
index 8a7fc33..25ddbcd 100644
--- a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp
+++ b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp
@@ -110,7 +110,7 @@ extract_bounding_box_grammar<Iterator, Boxes, ErrorHandler>::extract_bounding_bo
                                                >> *((json.key_value - lit("\"features\"")) >> lit(','))
                                                >> lit("\"features\"")
                                                >> lit(':'))
-                                          >> lit('[') >> (feature(_r1,_a) % lit(',')) >> lit(']')
+                                          >> lit('[') >> -(feature(_r1,_a) % lit(',')) >> lit(']')
         ;
 
     feature = raw[lit('{')[_a = 1]
@@ -148,13 +148,10 @@ extract_bounding_box_grammar<Iterator, Boxes, ErrorHandler>::extract_bounding_bo
     json.value = json.object | json.array | json.string_ | json.number
         ;
 
-    json.pairs = json.key_value % lit(',')
+    json.key_value = json.string_ >> lit(':') >> json.value
         ;
 
-    json.key_value = (json.string_ >> lit(':') >> json.value)
-        ;
-
-    json.object = lit('{') >> *json.pairs >> lit('}')
+    json.object = lit('{') >>  json.key_value % lit(',') >> lit('}')
         ;
 
     json.array = lit('[')
diff --git a/include/mapnik/json/feature_grammar.hpp b/include/mapnik/json/feature_grammar.hpp
index ae59e3b..c329eb7 100644
--- a/include/mapnik/json/feature_grammar.hpp
+++ b/include/mapnik/json/feature_grammar.hpp
@@ -24,14 +24,10 @@
 #define MAPNIK_FEATURE_GRAMMAR_HPP
 
 // mapnik
-#include <mapnik/json/geometry_grammar.hpp>
 #include <mapnik/value.hpp>
 #include <mapnik/feature.hpp>
-#include <mapnik/unicode.hpp>
-#include <mapnik/value.hpp>
-#include <mapnik/json/generic_json.hpp>
-#include <mapnik/json/value_converters.hpp>
-
+#include <mapnik/json/geometry_grammar.hpp>
+#include <mapnik/json/attribute_value_visitor.hpp>
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
 #include <boost/spirit/include/qi.hpp>
@@ -45,27 +41,6 @@ namespace qi = boost::spirit::qi;
 namespace phoenix = boost::phoenix;
 namespace fusion = boost::fusion;
 
-class attribute_value_visitor
-
-{
-public:
-    attribute_value_visitor(mapnik::transcoder const& tr)
-        : tr_(tr) {}
-
-    mapnik::value operator()(std::string const& val) const
-    {
-        return mapnik::value(tr_.transcode(val.c_str()));
-    }
-
-    template <typename T>
-    mapnik::value operator()(T const& val) const
-    {
-        return mapnik::value(val);
-    }
-
-    mapnik::transcoder const& tr_;
-};
-
 struct put_property
 {
     using result_type = void;
@@ -101,9 +76,6 @@ struct feature_grammar : qi::grammar<Iterator, void(FeatureType&), space_type>
     qi::rule<Iterator, space_type> feature_type;
     qi::rule<Iterator,void(FeatureType &),space_type> properties;
     qi::rule<Iterator,qi::locals<std::string>, void(FeatureType &),space_type> attributes;
-    qi::rule<Iterator, json_value(), space_type> attribute_value;
-    qi::rule<Iterator, qi::locals<std::int32_t>, std::string(), space_type> stringify_object;
-    qi::rule<Iterator, qi::locals<std::int32_t>, std::string(), space_type> stringify_array;
     // functions
     phoenix::function<put_property> put_property_;
     phoenix::function<set_geometry_impl> set_geometry;
diff --git a/include/mapnik/json/feature_grammar_impl.hpp b/include/mapnik/json/feature_grammar_impl.hpp
index 61a23f9..0fc4205 100644
--- a/include/mapnik/json/feature_grammar_impl.hpp
+++ b/include/mapnik/json/feature_grammar_impl.hpp
@@ -50,23 +50,22 @@ feature_grammar<Iterator,FeatureType,ErrorHandler>::feature_grammar(mapnik::tran
     using phoenix::construct;
 
     // generic json types
-    json_.value =  json_.object | json_.array | json_.string_ | json_.number
+    json_.value = json_.object | json_.array | json_.string_ | json_.number
         ;
 
-    json_.pairs = json_.key_value % lit(',')
-        ;
-
-    json_.key_value = (json_.string_ > lit(':') > json_.value)
+    json_.key_value = json_.string_ > lit(':') > json_.value
         ;
 
     json_.object = lit('{')
-        > *json_.pairs
+        > -(json_.key_value % lit(','))
         > lit('}')
         ;
+
     json_.array = lit('[')
-        > json_.value > *(lit(',') > json_.value)
+        > -(json_.value % lit(','))
         > lit(']')
         ;
+
     json_.number = json_.strict_double[_val = json_.double_converter(_1)]
         | json_.int__[_val = json_.integer_converter(_1)]
         | lit("true") [_val = true]
@@ -95,24 +94,13 @@ feature_grammar<Iterator,FeatureType,ErrorHandler>::feature_grammar(mapnik::tran
         > lit(':') > ((lit('{') > -attributes(_r1) > lit('}')) | lit("null"))
         ;
 
-    attributes = (json_.string_ [_a = _1] > lit(':') > attribute_value [put_property_(_r1,_a,_1)]) % lit(',')
-        ;
-
-    attribute_value %= json_.number | json_.string_ | stringify_object | stringify_array
-        ;
-
-    stringify_object %= char_('{')[_a = 1 ] > *(eps(_a > 0) > (char_('{')[_a +=1] | char_('}')[_a -=1] | char_))
-        ;
-
-    stringify_array %= char_('[')[_a = 1 ] > *(eps(_a > 0) > (char_('[')[_a +=1] | char_(']')[_a -=1] | char_))
+    attributes = (json_.string_ [_a = _1] > lit(':') > json_.value [put_property_(_r1,_a,_1)]) % lit(',')
         ;
 
     feature.name("Feature");
     feature_type.name("type");
     properties.name("properties");
     attributes.name("Attributes");
-    attribute_value.name("Attribute Value");
-
     on_error<fail>(feature, error_handler(_1, _2, _3, _4));
 
 }
diff --git a/include/mapnik/json/generic_json.hpp b/include/mapnik/json/generic_json.hpp
index 3313410..7ea1bc0 100644
--- a/include/mapnik/json/generic_json.hpp
+++ b/include/mapnik/json/generic_json.hpp
@@ -31,15 +31,47 @@
 #include <mapnik/warning_ignore.hpp>
 #include <boost/spirit/include/qi.hpp>
 #include <boost/spirit/include/phoenix.hpp>
+#include <boost/fusion/include/std_pair.hpp>
 #pragma GCC diagnostic pop
 
+#include <vector>
+
 namespace mapnik { namespace json {
 
 namespace qi = boost::spirit::qi;
 namespace standard = boost::spirit::standard;
 namespace phoenix = boost::phoenix;
 using space_type = standard::space_type;
-using json_value = mapnik::util::variant<value_null,value_bool, value_integer, value_double, std::string>;
+
+struct json_value;
+
+using json_array = std::vector<json_value>;
+using json_object_element = std::pair<std::string, json_value>;
+using json_object = std::vector<json_object_element>;
+using json_value_base = mapnik::util::variant<value_null,
+                                              value_bool,
+                                              value_integer,
+                                              value_double,
+                                              std::string,
+                                              mapnik::util::recursive_wrapper<json_array>,
+                                              mapnik::util::recursive_wrapper<json_object> >;
+struct json_value : json_value_base
+{
+#if __cpp_inheriting_constructors >= 200802
+
+    using json_value_base::json_value_base;
+
+#else
+
+    json_value() = default;
+
+    template <typename T>
+    json_value(T && val)
+        : json_value_base(std::forward<T>(val)) {}
+
+#endif
+};
+
 using uchar = std::uint32_t; // a unicode code point
 
 // unicode string grammar via boost/libs/spirit/example/qi/json/json/parser/grammar.hpp
@@ -121,10 +153,14 @@ unicode_string<Iterator>::unicode_string()
 
     escape =
         ('x' > hex)                     [push_utf8(_r1, _1)]
-        |   ('u' > hex4)                    [push_utf8(_r1, _1)]
-        |   ('U' > hex8)                    [push_utf8(_r1, _1)]
-        |   char_("0abtnvfre\"/\\N_LP \t")  [push_esc(_r1, _1)]
-        |   eol                             // continue to next line
+        |
+        ('u' > hex4)                    [push_utf8(_r1, _1)]
+        |
+        ('U' > hex8)                    [push_utf8(_r1, _1)]
+        |
+        char_("0abtnvfre\"/\\N_LP \t")  [push_esc(_r1, _1)]
+        |
+        eol                             // continue to next line
         ;
 
     char_esc =
@@ -132,7 +168,7 @@ unicode_string<Iterator>::unicode_string()
         ;
 
     double_quoted =
-              '"'
+        '"'
         > *(char_esc(_val) | (~char_('"'))    [_val += _1])
         > '"'
         ;
@@ -141,18 +177,17 @@ unicode_string<Iterator>::unicode_string()
 template <typename Iterator>
 struct generic_json
 {
-    qi::rule<Iterator,space_type> value;
-    qi::int_parser<mapnik::value_integer,10,1,-1> int__;
+    qi::rule<Iterator, json_value(), space_type> value;
+    qi::int_parser<mapnik::value_integer, 10, 1, -1> int__;
     unicode_string<Iterator> string_;
-    qi::rule<Iterator,space_type> key_value;
-    qi::rule<Iterator,json_value(),space_type> number;
-    qi::rule<Iterator,space_type> object;
-    qi::rule<Iterator,space_type> array;
-    qi::rule<Iterator,space_type> pairs;
-    qi::real_parser<double, qi::strict_real_policies<double> > strict_double;
+    qi::rule<Iterator, json_object_element(), space_type> key_value;
+    qi::rule<Iterator, json_value(), space_type> number;
+    qi::rule<Iterator, json_object(), space_type> object;
+    qi::rule<Iterator, json_array(), space_type> array;
+    qi::real_parser<double, qi::strict_real_policies<double>> strict_double;
     // conversions
-    boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_integer> > integer_converter;
-    boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_double> > double_converter;
+    boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_integer>> integer_converter;
+    boost::phoenix::function<mapnik::detail::value_converter<mapnik::value_double>> double_converter;
 };
 
 }}
diff --git a/include/mapnik/json/geometry_grammar_impl.hpp b/include/mapnik/json/geometry_grammar_impl.hpp
index 3c5693f..285be27 100644
--- a/include/mapnik/json/geometry_grammar_impl.hpp
+++ b/include/mapnik/json/geometry_grammar_impl.hpp
@@ -47,7 +47,6 @@ geometry_grammar<Iterator, ErrorHandler>::geometry_grammar()
     qi::_a_type _a;
     qi::_b_type _b;
     qi::eps_type eps;
-    qi::omit_type omit;
     using qi::fail;
     using qi::on_error;
     using phoenix::push_back;
@@ -58,26 +57,26 @@ geometry_grammar<Iterator, ErrorHandler>::geometry_grammar()
     json_.value =  json_.object | json_.array | json_.string_ | json_.number
         ;
 
-    json_.pairs = json_.key_value % lit(',')
-        ;
-
-    json_.key_value = (json_.string_ > lit(':') > json_.value)
+    json_.key_value = json_.string_ > lit(':') > json_.value
         ;
 
     json_.object = lit('{')
-        > *json_.pairs
+        > -(json_.key_value % lit(','))
         > lit('}')
         ;
+
     json_.array = lit('[')
-        > json_.value > *(lit(',') > json_.value)
+        > -(json_.value % lit(','))
         > lit(']')
         ;
+
     json_.number = json_.strict_double
         | json_.int__
         | lit("true")
         | lit ("false")
         | lit("null")
         ;
+
     geometry = lit('{')[_a = 0]
         > (((lit("\"type\"") > lit(':') > geometry_type_dispatch[_a = _1])
             |
@@ -85,7 +84,7 @@ geometry_grammar<Iterator, ErrorHandler>::geometry_grammar()
             |
             (lit("\"geometries\"") > lit(':') > lit('[') > geometry_collection[_val = _1] > lit(']'))
             |
-            omit[json_.key_value]) % lit(',')) [create_geometry(_val,_a,_b)]
+            json_.key_value) % lit(',')) [create_geometry(_val,_a,_b)]
         > lit('}')
         ;
 
diff --git a/include/mapnik/json/positions_grammar.hpp b/include/mapnik/json/positions_grammar.hpp
index bc81046..75a6b21 100644
--- a/include/mapnik/json/positions_grammar.hpp
+++ b/include/mapnik/json/positions_grammar.hpp
@@ -32,7 +32,6 @@
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
 #include <boost/spirit/include/qi.hpp>
-#include <boost/spirit/include/phoenix_function.hpp>
 #pragma GCC diagnostic pop
 
 
@@ -40,26 +39,6 @@ namespace mapnik { namespace json {
 
 namespace qi = boost::spirit::qi;
 
-struct set_position_impl
-{
-    using result_type = void;
-    template <typename T0,typename T1>
-    result_type operator() (T0 & coords, T1 const& pos) const
-    {
-        if (pos) coords = *pos;
-    }
-};
-
-struct push_position_impl
-{
-    using result_type = void;
-    template <typename T0, typename T1>
-    result_type operator() (T0 & coords, T1 const& pos) const
-    {
-        if (pos) coords.emplace_back(*pos);
-    }
-};
-
 template <typename Iterator, typename ErrorHandler = error_handler<Iterator> >
 struct positions_grammar :
         qi::grammar<Iterator,coordinates(),space_type>
@@ -70,8 +49,6 @@ struct positions_grammar :
     qi::rule<Iterator, positions(), space_type> ring;
     qi::rule<Iterator, std::vector<positions>(), space_type> rings;
     qi::rule<Iterator, std::vector<std::vector<positions> >(), space_type> rings_array;
-    boost::phoenix::function<set_position_impl> set_position;
-    boost::phoenix::function<push_position_impl> push_position;
 };
 
 }}
diff --git a/include/mapnik/json/positions_grammar_impl.hpp b/include/mapnik/json/positions_grammar_impl.hpp
index 90df172..65f2a93 100644
--- a/include/mapnik/json/positions_grammar_impl.hpp
+++ b/include/mapnik/json/positions_grammar_impl.hpp
@@ -28,12 +28,33 @@
 #include <boost/spirit/include/phoenix_object.hpp>
 #include <boost/spirit/include/phoenix_stl.hpp>
 #include <boost/spirit/include/phoenix_operator.hpp>
+#include <boost/spirit/include/phoenix_function.hpp>
 // stl
 #include <iostream>
 #include <string>
 
 namespace mapnik { namespace json {
 
+struct set_position_impl
+{
+    using result_type = void;
+    template <typename T0,typename T1>
+    result_type operator() (T0 & coords, T1 const& pos) const
+    {
+        if (pos) coords = *pos;
+    }
+};
+
+struct push_position_impl
+{
+    using result_type = void;
+    template <typename T0, typename T1>
+    result_type operator() (T0 & coords, T1 const& pos) const
+    {
+        if (pos) coords.emplace_back(*pos);
+    }
+};
+
 template <typename Iterator, typename ErrorHandler>
 positions_grammar<Iterator, ErrorHandler>::positions_grammar(ErrorHandler & error_handler)
     : positions_grammar::base_type(coords,"coordinates")
@@ -49,6 +70,9 @@ positions_grammar<Iterator, ErrorHandler>::positions_grammar(ErrorHandler & erro
     using qi::fail;
     using qi::on_error;
 
+    boost::phoenix::function<set_position_impl> set_position;
+    boost::phoenix::function<push_position_impl> push_position;
+
     coords = rings_array[_val = _1] | rings [_val = _1] | ring[_val = _1] |  pos[set_position(_val,_1)]
         ;
     pos = lit('[') > -(double_ > lit(',') > double_) > omit[*(lit(',') > double_)] > lit(']')
diff --git a/include/mapnik/json/stringifier.hpp b/include/mapnik/json/stringifier.hpp
new file mode 100644
index 0000000..93906f6
--- /dev/null
+++ b/include/mapnik/json/stringifier.hpp
@@ -0,0 +1,101 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#ifndef MAPNIK_JSON_STRINGIFIER_HPP
+#define MAPNIK_JSON_STRINGIFIER_HPP
+
+// mapnik
+#include <mapnik/json/generic_json.hpp>
+#include <mapnik/util/conversions.hpp>
+#include <mapnik/util/variant.hpp>
+// stl
+#include <string>
+
+
+namespace mapnik { namespace json {
+
+struct stringifier
+{
+    std::string operator()(std::string const& val) const
+    {
+        return "\"" + val + "\"";
+    }
+
+    std::string operator()(value_null) const
+    {
+        return "null";
+    }
+
+    std::string operator()(value_bool val) const
+    {
+        return val ? "true" : "false";
+    }
+
+    std::string operator()(value_integer val) const
+    {
+        std::string str;
+        util::to_string(str, val);
+        return str;
+    }
+
+    std::string operator()(value_double val) const
+    {
+        std::string str;
+        util::to_string(str, val);
+        return str;
+    }
+
+    std::string operator()(std::vector<mapnik::json::json_value> const& array) const
+    {
+        std::string str = "[";
+        bool first = true;
+        for (auto const& val : array)
+        {
+            if (first) first = false;
+            else str += ",";
+            str += mapnik::util::apply_visitor(*this, val);
+        }
+        str += "]";
+        return str;
+    }
+
+    std::string operator()(std::vector<std::pair<std::string, mapnik::json::json_value>> const& object) const
+    {
+        std::string str = "{";
+        bool first = true;
+        for (auto const& kv : object)
+        {
+            if (first) first = false;
+            else str += ",";
+            str += kv.first;
+            str += ":";
+            str += mapnik::util::apply_visitor(*this, kv.second);
+        }
+        str += "}";
+        return str;
+    }
+};
+
+}}
+
+
+#endif // MAPNIK_JSON_STRINGIFIER_HPP
diff --git a/include/mapnik/json/topojson_grammar.hpp b/include/mapnik/json/topojson_grammar.hpp
index eec2bb4..c21be95 100644
--- a/include/mapnik/json/topojson_grammar.hpp
+++ b/include/mapnik/json/topojson_grammar.hpp
@@ -42,6 +42,138 @@ namespace qi = boost::spirit::qi;
 namespace fusion = boost::fusion;
 using space_type = mapnik::json::space_type;
 
+struct create_point
+{
+    using result_type = mapnik::topojson::point;
+    template <typename T0, typename T1>
+    result_type operator()(T0 & coord, T1 & props) const
+    {
+        mapnik::topojson::point pt;
+        if (coord.template is<mapnik::topojson::coordinate>())
+        {
+            auto const& coord_ = coord.template get<mapnik::topojson::coordinate>();
+            pt.coord = coord_;
+            pt.props = props;
+        }
+        return pt;
+    }
+};
+
+struct create_multi_point
+{
+    using result_type = mapnik::topojson::multi_point;
+    template <typename T0, typename T1>
+    result_type operator()(T0 & coords, T1 & props) const
+    {
+        mapnik::topojson::multi_point mpt;
+        if (coords.template is<std::vector<mapnik::topojson::coordinate>>())
+        {
+            auto const& points = coords.template get<std::vector<mapnik::topojson::coordinate>>();
+            mpt. points = points;
+            mpt.props = props;
+        }
+        return mpt;
+    }
+};
+
+struct create_line_string
+{
+    using result_type = mapnik::topojson::linestring;
+    template <typename T0, typename T1>
+    result_type operator()(T0 & arcs, T1 & props) const
+    {
+        mapnik::topojson::linestring line;
+        if (arcs.template is<std::vector<index_type>>())
+        {
+            auto const& arcs_ = arcs.template get<std::vector<index_type>>();
+            line.rings = arcs_;
+            line.props = props;
+        }
+        return line;
+    }
+};
+
+struct create_multi_line_string
+{
+    using result_type = mapnik::topojson::multi_linestring;
+    template <typename T0, typename T1>
+    result_type operator()(T0 & arcs, T1 & props) const
+    {
+        mapnik::topojson::multi_linestring mline;
+        if (arcs.template is<std::vector<std::vector<index_type>>>())
+        {
+            auto const& arcs_ = arcs.template get<std::vector<std::vector<index_type>>>();
+            mline.lines = arcs_;
+            mline.props = props;
+        }
+        return mline;
+    }
+};
+
+struct create_polygon
+{
+    using result_type = mapnik::topojson::polygon;
+    template <typename T0, typename T1>
+    result_type operator()(T0 & arcs, T1 & props) const
+    {
+        mapnik::topojson::polygon poly;
+        if (arcs.template is<std::vector<std::vector<index_type>>>())
+        {
+            auto const& arcs_ = arcs.template get<std::vector<std::vector<index_type>>>();
+            poly.rings = arcs_;
+            poly.props = props;
+        }
+        return poly;
+    }
+};
+
+struct create_multi_polygon
+{
+    using result_type = mapnik::topojson::multi_polygon;
+    template <typename T0, typename T1>
+    result_type operator()(T0 & arcs, T1 & props) const
+    {
+        mapnik::topojson::multi_polygon mpoly;
+        if (arcs.template is<std::vector<std::vector<std::vector<index_type>>>>())
+        {
+            auto const& arcs_ = arcs.template get<std::vector<std::vector<std::vector<index_type>>>>();
+            mpoly.polygons = arcs_;
+            mpoly.props = props;
+        }
+        return mpoly;
+    }
+};
+
+
+struct create_geometry_impl
+{
+    using result_type = mapnik::topojson::geometry;
+    template <typename T0, typename T1, typename T2, typename T3>
+    result_type operator()(T0 geom_type, T1 & coord, T2 & arcs, T3 & props) const
+    {
+        switch (geom_type)
+        {
+        case 1: //Point
+            return create_point()(coord, props);
+        case 2: //LineString
+            return create_line_string()(arcs, props);
+        case 3: //Polygon
+            return create_polygon()(arcs, props);
+        case 4: //MultiPoint
+            return create_multi_point()(coord, props);
+        case 5: //MultiLineString
+            return create_multi_line_string()(arcs, props);
+        case 6: //MultiPolygon
+            return create_multi_polygon()(arcs, props);
+        default:
+            break;
+        }
+        return mapnik::topojson::geometry(); //empty
+    }
+};
+
+using coordinates_type = util::variant<coordinate,std::vector<coordinate>>;
+using arcs_type = util::variant<std::vector<index_type>, std::vector<std::vector<index_type>>, std::vector<std::vector<std::vector<index_type>>>>;
 template <typename Iterator, typename ErrorHandler = json::error_handler<Iterator> >
 struct topojson_grammar : qi::grammar<Iterator, space_type, topology()>
 
@@ -55,24 +187,18 @@ private:
     qi::rule<Iterator, space_type, std::vector<mapnik::topojson::geometry>()> objects;
     qi::rule<Iterator, space_type, std::vector<mapnik::topojson::arc>()> arcs;
     qi::rule<Iterator, space_type, mapnik::topojson::arc()> arc;
-    qi::rule<Iterator, space_type, mapnik::topojson::coordinate()> coordinate;
+    qi::rule<Iterator, space_type, mapnik::topojson::coordinate()> coordinate_;
+    qi::rule<Iterator, space_type, coordinates_type()> coordinates;
     qi::rule<Iterator, space_type, mapnik::topojson::transform()> transform;
     qi::rule<Iterator, space_type, mapnik::topojson::bounding_box()> bbox;
-    qi::rule<Iterator, space_type, mapnik::topojson::geometry() > geometry;
-    qi::rule<Iterator, space_type, mapnik::topojson::point()> point;
-    qi::rule<Iterator, space_type, mapnik::topojson::multi_point()> multi_point;
-    qi::rule<Iterator, space_type, mapnik::topojson::linestring()> linestring;
-    qi::rule<Iterator, space_type, mapnik::topojson::multi_linestring()> multi_linestring;
-    qi::rule<Iterator, space_type, mapnik::topojson::polygon()> polygon;
-    qi::rule<Iterator, space_type, mapnik::topojson::multi_polygon()> multi_polygon;
+    qi::rule<Iterator, qi::locals<int, coordinates_type, arcs_type, properties>, mapnik::topojson::geometry(), space_type> geometry;
     qi::rule<Iterator, space_type, void(std::vector<mapnik::topojson::geometry>&)> geometry_collection;
     qi::rule<Iterator, space_type, std::vector<index_type>()> ring;
+    qi::rule<Iterator, space_type, std::vector<std::vector<index_type>>()> rings;
+    qi::rule<Iterator, space_type, arcs_type()> rings_array;
     // properties
-    qi::rule<Iterator, space_type, mapnik::topojson::properties()> properties;
-    qi::rule<Iterator, space_type, mapnik::topojson::properties()> attributes;
-    qi::rule<Iterator, space_type, mapnik::json::json_value()> attribute_value;
-    // id
-    qi::rule<Iterator,space_type> id;
+    qi::rule<Iterator, space_type, mapnik::topojson::properties()> properties_;
+    qi::symbols<char, int> geometry_type_dispatch;
 };
 
 }}
diff --git a/include/mapnik/json/topojson_grammar_impl.hpp b/include/mapnik/json/topojson_grammar_impl.hpp
index ca86079..f868cef 100644
--- a/include/mapnik/json/topojson_grammar_impl.hpp
+++ b/include/mapnik/json/topojson_grammar_impl.hpp
@@ -58,42 +58,6 @@ BOOST_FUSION_ADAPT_STRUCT(
     )
 
 BOOST_FUSION_ADAPT_STRUCT(
-    mapnik::topojson::point,
-    (mapnik::topojson::coordinate, coord)
-    (boost::optional<mapnik::topojson::properties>, props)
-    )
-
-BOOST_FUSION_ADAPT_STRUCT(
-    mapnik::topojson::multi_point,
-    (std::vector<mapnik::topojson::coordinate>, points)
-    (boost::optional<mapnik::topojson::properties>, props)
-    )
-
-BOOST_FUSION_ADAPT_STRUCT(
-    mapnik::topojson::linestring,
-    (mapnik::topojson::index_type, ring)
-    (boost::optional<mapnik::topojson::properties>, props)
-    )
-
-BOOST_FUSION_ADAPT_STRUCT(
-    mapnik::topojson::multi_linestring,
-    (std::vector<mapnik::topojson::index_type>, rings)
-    (boost::optional<mapnik::topojson::properties>, props)
-    )
-
-BOOST_FUSION_ADAPT_STRUCT(
-    mapnik::topojson::polygon,
-    (std::vector<std::vector<mapnik::topojson::index_type> >, rings)
-    (boost::optional<mapnik::topojson::properties>, props)
-    )
-
-BOOST_FUSION_ADAPT_STRUCT(
-    mapnik::topojson::multi_polygon,
-    (std::vector<std::vector<std::vector<mapnik::topojson::index_type> > >, polygons)
-    (boost::optional<mapnik::topojson::properties>, props)
-    )
-
-BOOST_FUSION_ADAPT_STRUCT(
     mapnik::topojson::topology,
     (std::vector<mapnik::topojson::geometry>, geometries)
     (std::vector<mapnik::topojson::arc>, arcs)
@@ -101,6 +65,8 @@ BOOST_FUSION_ADAPT_STRUCT(
     (boost::optional<mapnik::topojson::bounding_box>, bbox)
    )
 
+
+
 namespace mapnik { namespace topojson {
 
 namespace qi = boost::spirit::qi;
@@ -121,30 +87,43 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
     qi::_3_type _3;
     qi::_4_type _4;
     qi::_r1_type _r1;
+    qi::_a_type _a;
+    qi::_b_type _b;
+    qi::_c_type _c;
+    qi::_d_type _d;
     using qi::fail;
     using qi::on_error;
     using phoenix::push_back;
     using phoenix::construct;
 
+    geometry_type_dispatch.add
+        ("\"Point\"",1)
+        ("\"LineString\"",2)
+        ("\"Polygon\"",3)
+        ("\"MultiPoint\"",4)
+        ("\"MultiLineString\"",5)
+        ("\"MultiPolygon\"",6)
+        ("\"GeometryCollection\"",7)
+        ;
+
     // error handler
     boost::phoenix::function<ErrorHandler> const error_handler;
-
+    boost::phoenix::function<create_geometry_impl> const create_geometry;
     // generic JSON types
     json.value = json.object | json.array | json.string_ | json.number
         ;
 
-    json.pairs = json.key_value % lit(',')
+    json.key_value = json.string_ > lit(':') > json.value
         ;
 
-    json.key_value = (json.string_ >> lit(':') >> json.value)
-        ;
-
-    json.object = lit('{') >> *json.pairs >> lit('}')
+    json.object = lit('{')
+        > -(json.key_value % lit(','))
+        > lit('}')
         ;
 
     json.array = lit('[')
-        >> json.value >> *(lit(',') >> json.value)
-        >> lit(']')
+        > -(json.value % lit(','))
+        > lit(']')
         ;
 
     json.number = json.strict_double[_val = json.double_converter(_1)]
@@ -181,101 +160,58 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
         >> lit('{')
         >> -((omit[json.string_]
               >> lit(':')
-              >>  (geometry_collection(_val) | geometry)) % lit(','))
+              >>  (geometry_collection(_val) | geometry[push_back(_val, _1)]) % lit(',')))
         >> lit('}')
         ;
 
-    geometry =
-        point |
-        linestring |
-        polygon |
-        multi_point |
-        multi_linestring |
-        multi_polygon |
-        omit[json.object]
+    geometry = lit('{')[_a = 0]
+        > ((lit("\"type\"") > lit(':') > geometry_type_dispatch[_a = _1])
+           |
+           (lit("\"coordinates\"") > lit(':') > coordinates[_b = _1])
+           |
+           (lit("\"arcs\"") > lit(':') > rings_array[_c = _1])
+           |
+           properties_[_d = _1]
+           |
+           json.key_value) % lit(',')
+        > lit('}')[_val = create_geometry(_a, _b, _c, _d)]
         ;
 
-    geometry_collection =  lit('{')
-               >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"")
-               >> -(lit(',') >> omit[bbox])
-               >> lit(',') >> lit("\"geometries\"") >> lit(':') >> lit('[') >> -(geometry[push_back(_r1, _1)] % lit(','))
-               >> lit(']')
-               >> lit('}')
-        ;
-    point = lit('{')
-        >> lit("\"type\"") >> lit(':') >> lit("\"Point\"")
-        >> -(lit(',') >> omit[bbox])
-        >> ((lit(',') >> lit("\"coordinates\"") >> lit(':') >> coordinate)
-            ^ (lit(',') >> properties) /*^ (lit(',') >> omit[id])*/)
-        >> lit('}')
-        ;
-
-    multi_point = lit('{')
-        >> lit("\"type\"") >> lit(':') >> lit("\"MultiPoint\"")
-        >> -(lit(',') >> omit[bbox])
-        >> ((lit(',') >> lit("\"coordinates\"") >> lit(':')
-             >> lit('[') >> -(coordinate % lit(',')) >> lit(']'))
-            ^ (lit(',') >> properties) ^ (lit(',') >> omit[id]))
-        >> lit('}')
-        ;
-
-    linestring = lit('{')
-        >> lit("\"type\"") >> lit(':') >> lit("\"LineString\"")
-        >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> int_ >> lit(']'))
-            ^ (lit(',') >> properties) ^ (lit(',') >> omit[id]))
-        >> lit('}')
-        ;
-
-    multi_linestring = lit('{')
-        >> lit("\"type\"") >> lit(':') >> lit("\"MultiLineString\"")
-        >> -(lit(',') >> omit[bbox])
-        >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[')
-             >> -((lit('[') >> int_ >> lit(']')) % lit(',')) >> lit(']'))
-            ^ (lit(',') >> properties) ^ (lit(',') >> omit[id]))
-        >> lit('}')
-        ;
 
-    polygon = lit('{')
-        >> lit("\"type\"") >> lit(':') >> lit("\"Polygon\"")
-        >> -(lit(',') >> omit[bbox])
-        >> ((lit(',') >> lit("\"arcs\"") >> lit(':')
-             >> lit('[') >> -(ring % lit(',')) >> lit(']'))
-            ^ (lit(',') >> properties) ^ (lit(',') >> omit[id]))
+    geometry_collection =  lit('{')
+        >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"")
+        >> lit(',') >> lit("\"geometries\"") >> lit(':')
+        >> lit('[')
+        >> -(geometry[push_back(_r1, _1)] % lit(','))
+        >> lit(']')
         >> lit('}')
         ;
 
-    multi_polygon = lit('{')
-        >> lit("\"type\"") >> lit(':') >> lit("\"MultiPolygon\"")
-        >> -(lit(',') >> omit[bbox])
-        >> ((lit(',') >> lit("\"arcs\"") >> lit(':')
-             >> lit('[')
-             >> -((lit('[') >> -(ring % lit(',')) >> lit(']')) % lit(','))
-             >> lit(']')) ^ (lit(',') >> properties) ^ (lit(',') >> omit[id]))
-        >> lit('}')
+    ring = lit('[') >> -(int_ % lit(',')) >> lit(']')
         ;
-
-    id = lit("\"id\"") >> lit(':') >> omit[json.value]
+    rings = lit('[') >> -(ring % lit(',')) >> lit(']')
         ;
-
-    ring = lit('[') >> -(int_ % lit(',')) >> lit(']')
+    rings_array = lit('[') >> -(rings % lit(',')) >> lit(']')
+        |
+        rings
+        |
+        ring
         ;
 
-    properties = lit("\"properties\"")
+    properties_ = lit("\"properties\"")
         >> lit(':')
-        >> (( lit('{') >> attributes >> lit('}')) | json.object)
-        ;
-
-    attributes = (json.string_ >> lit(':') >> attribute_value) % lit(',')
+        >> lit('{') >> (json.string_ >> lit(':') >> json.value) % lit(',') >> lit('}')
         ;
 
-    attribute_value %= json.number | json.string_  ;
-
     arcs = lit("\"arcs\"") >> lit(':')
                            >> lit('[') >> -( arc % lit(',')) >> lit(']') ;
 
-    arc = lit('[') >> -(coordinate % lit(',')) >> lit(']') ;
+    arc = lit('[') >> -(coordinate_ % lit(',')) >> lit(']') ;
+
+    coordinate_ = lit('[') > double_ > lit(',') > double_ > lit(']');
 
-    coordinate = lit('[') >> double_ >> lit(',') >> double_ >> lit(']');
+    coordinates = (lit('[') >> coordinate_ % lit(',') > lit(']'))
+        | coordinate_;
 
     topology.name("topology");
     transform.name("transform");
@@ -283,13 +219,9 @@ topojson_grammar<Iterator, ErrorHandler>::topojson_grammar()
     arc.name("arc");
     arcs.name("arcs");
     json.value.name("value");
-    coordinate.name("coordinate");
-
-    point.name("point");
-    multi_point.name("multi_point");
-    linestring.name("linestring");
-    polygon.name("polygon");
-    multi_polygon.name("multi_polygon");
+    coordinate_.name("coordinate");
+    geometry.name("geometry");
+    properties_.name("properties");
     geometry_collection.name("geometry_collection");
     // error handler
     on_error<fail>(topology, error_handler(_1, _2, _3, _4));
diff --git a/include/mapnik/json/topojson_utils.hpp b/include/mapnik/json/topojson_utils.hpp
index c4de306..40a2c91 100644
--- a/include/mapnik/json/topojson_utils.hpp
+++ b/include/mapnik/json/topojson_utils.hpp
@@ -2,7 +2,7 @@
  *
  * This file is part of Mapnik (c++ mapping toolkit)
  *
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2016 Artem Pavlenko
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -27,6 +27,7 @@
 #include <mapnik/box2d.hpp>
 #include <mapnik/unicode.hpp>
 #include <mapnik/json/topology.hpp>
+#include <mapnik/json/attribute_value_visitor.hpp>
 #include <mapnik/feature_factory.hpp>
 #include <mapnik/geometry_adapters.hpp>
 #include <mapnik/geometry_correct.hpp>
@@ -39,6 +40,11 @@ struct bounding_box_visitor
         : topo_(topo),
           num_arcs_(topo_.arcs.size()) {}
 
+    box2d<double> operator() (mapnik::topojson::empty const&) const
+    {
+        return box2d<double>();
+    }
+
     box2d<double> operator() (mapnik::topojson::point const& pt) const
     {
         double x = pt.coord.x;
@@ -81,50 +87,15 @@ struct bounding_box_visitor
     box2d<double> operator() (mapnik::topojson::linestring const& line) const
     {
         box2d<double> bbox;
+        bool first = true;
         if (num_arcs_ > 0)
         {
-            index_type index = line.ring;
-            index_type arc_index = index < 0 ? std::abs(index) - 1 : index;
-            if (arc_index >= 0 && arc_index < static_cast<int>(num_arcs_))
-            {
-                bool first = true;
-                double px = 0, py = 0;
-                auto const& arcs = topo_.arcs[arc_index];
-                for (auto pt : arcs.coordinates)
-                {
-                    double x = pt.x;
-                    double y = pt.y;
-                    if (topo_.tr)
-                    {
-                        x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
-                        y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
-                    }
-                    if (first)
-                    {
-                        first = false;
-                        bbox.init(x, y, x, y);
-                    }
-                    else
-                    {
-                        bbox.expand_to_include(x, y);
-                    }
-                }
-            }
-        }
-        return bbox;
-    }
-
-    box2d<double> operator() (mapnik::topojson::multi_linestring const& multi_line) const
-    {
-        box2d<double> bbox;
-        if (num_arcs_ > 0)
-        {
-            bool first = true;
-            for (auto index : multi_line.rings)
+            for (auto index : line.rings)
             {
                 index_type arc_index = index < 0 ? std::abs(index) - 1 : index;
                 if (arc_index >= 0 && arc_index < static_cast<int>(num_arcs_))
                 {
+
                     double px = 0, py = 0;
                     auto const& arcs = topo_.arcs[arc_index];
                     for (auto pt : arcs.coordinates)
@@ -152,6 +123,47 @@ struct bounding_box_visitor
         return bbox;
     }
 
+    box2d<double> operator() (mapnik::topojson::multi_linestring const& multi_line) const
+    {
+        box2d<double> bbox;
+        if (num_arcs_ > 0)
+        {
+            bool first = true;
+            for (auto const& line : multi_line.lines)
+            {
+                for (auto index : line)
+                {
+                    index_type arc_index = index < 0 ? std::abs(index) - 1 : index;
+                    if (arc_index >= 0 && arc_index < static_cast<int>(num_arcs_))
+                    {
+                        double px = 0, py = 0;
+                        auto const& arcs = topo_.arcs[arc_index];
+                        for (auto pt : arcs.coordinates)
+                        {
+                            double x = pt.x;
+                            double y = pt.y;
+                            if (topo_.tr)
+                            {
+                                x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
+                                y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
+                            }
+                            if (first)
+                            {
+                                first = false;
+                                bbox.init(x, y, x, y);
+                            }
+                            else
+                            {
+                                bbox.expand_to_include(x, y);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return bbox;
+    }
+
     box2d<double> operator() (mapnik::topojson::polygon const& poly) const
     {
         box2d<double> bbox;
@@ -246,26 +258,6 @@ private:
 };
 
 namespace {
-struct attribute_value_visitor
-
-{
-public:
-    attribute_value_visitor(mapnik::transcoder const& tr)
-        : tr_(tr) {}
-
-    mapnik::value operator()(std::string const& val) const
-    {
-        return mapnik::value(tr_.transcode(val.c_str()));
-    }
-
-    template <typename T>
-    mapnik::value operator()(T const& val) const
-    {
-        return mapnik::value(val);
-    }
-
-    mapnik::transcoder const& tr_;
-};
 
 template <typename T>
 void assign_properties(mapnik::feature_impl & feature, T const& geom, mapnik::transcoder const& tr)
@@ -274,7 +266,7 @@ void assign_properties(mapnik::feature_impl & feature, T const& geom, mapnik::tr
     {
         for (auto const& p : *geom.props)
         {
-            feature.put_new(std::get<0>(p), mapnik::util::apply_visitor(attribute_value_visitor(tr),std::get<1>(p)));
+            feature.put_new(std::get<0>(p), mapnik::util::apply_visitor(mapnik::json::attribute_value_visitor(tr),std::get<1>(p)));
         }
     }
 }
@@ -333,29 +325,31 @@ struct feature_generator
         mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
         if (num_arcs_ > 0)
         {
-            index_type index = line.ring;
-            index_type arc_index = index < 0 ? std::abs(index) - 1 : index;
-            if (arc_index >= 0 && arc_index < static_cast<int>(num_arcs_))
-            {
-                auto const& arcs = topo_.arcs[arc_index];
-                double px = 0, py = 0;
-                mapnik::geometry::line_string<double> line_string;
-                line_string.reserve(arcs.coordinates.size());
+            mapnik::geometry::line_string<double> line_string;
 
-                for (auto pt : arcs.coordinates)
+            for (auto index : line.rings)
+            {
+                index_type arc_index = index < 0 ? std::abs(index) - 1 : index;
+                if (arc_index >= 0 && arc_index < static_cast<int>(num_arcs_))
                 {
-                    double x = pt.x;
-                    double y = pt.y;
-                    if (topo_.tr)
+                    auto const& arcs = topo_.arcs[arc_index];
+                    double px = 0, py = 0;
+                    line_string.reserve(line_string.size() + arcs.coordinates.size());
+                    for (auto pt : arcs.coordinates)
                     {
-                        x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
-                        y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
+                        double x = pt.x;
+                        double y = pt.y;
+                        if (topo_.tr)
+                        {
+                            x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
+                            y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
+                        }
+                        line_string.add_coord(x,y);
                     }
-                    line_string.add_coord(x,y);
                 }
-                feature->set_geometry(std::move(line_string));
-                assign_properties(*feature, line, tr_);
             }
+            feature->set_geometry(std::move(line_string));
+            assign_properties(*feature, line, tr_);
         }
         return feature;
     }
@@ -367,30 +361,34 @@ struct feature_generator
         {
             mapnik::geometry::multi_line_string<double> multi_line_string;
             bool hit = false;
-            multi_line_string.reserve(multi_line.rings.size());
-            for (auto const& index : multi_line.rings)
+            for (auto const& line : multi_line.lines)
             {
-                index_type arc_index = index < 0 ? std::abs(index) - 1 : index;
-                if (arc_index >= 0 && arc_index < static_cast<int>(num_arcs_))
+                multi_line_string.reserve(multi_line_string.size() + line.size());
+                mapnik::geometry::line_string<double> line_string;
+                for (auto index : line)
                 {
-                    hit = true;
-                    double px = 0, py = 0;
-                    mapnik::geometry::line_string<double> line_string;
-                    auto const& arcs = topo_.arcs[arc_index];
-                    line_string.reserve(arcs.coordinates.size());
-                    for (auto pt : arcs.coordinates)
+                    index_type arc_index = index < 0 ? std::abs(index) - 1 : index;
+                    if (arc_index >= 0 && arc_index < static_cast<int>(num_arcs_))
                     {
-                        double x = pt.x;
-                        double y = pt.y;
-                        if (topo_.tr)
+                        hit = true;
+                        double px = 0, py = 0;
+                        auto const& arcs = topo_.arcs[arc_index];
+                        line_string.reserve(line_string.size() + arcs.coordinates.size());
+                        for (auto pt : arcs.coordinates)
                         {
-                            x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
-                            y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
+                            double x = pt.x;
+                            double y = pt.y;
+                            if (topo_.tr)
+                            {
+                                x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
+                                y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
+                            }
+                            line_string.add_coord(x, y);
                         }
-                        line_string.add_coord(x, y);
+
                     }
-                    multi_line_string.push_back(std::move(line_string));
                 }
+                multi_line_string.push_back(std::move(line_string));
             }
             if (hit)
             {
@@ -439,7 +437,7 @@ struct feature_generator
                             }
                             processed_coords.emplace_back(coordinate{x,y});
                         }
-
+                        linear_ring.reserve(linear_ring.size() + processed_coords.size());
                         if (reverse)
                         {
                             for (auto const& c : processed_coords | boost::adaptors::reversed)
@@ -513,14 +511,15 @@ struct feature_generator
 
                                 if (topo_.tr)
                                 {
-                                    x =  (px += x) * (*topo_.tr).scale_x + (*topo_.tr).translate_x;
-                                    y =  (py += y) * (*topo_.tr).scale_y + (*topo_.tr).translate_y;
+                                    transform const& tr = *topo_.tr;
+                                    x =  (px += x) * tr.scale_x + tr.translate_x;
+                                    y =  (py += y) * tr.scale_y + tr.translate_y;
                                 }
                                 processed_coords.emplace_back(coordinate{x,y});
                             }
 
                             using namespace boost::adaptors;
-
+                            linear_ring.reserve(linear_ring.size() + processed_coords.size());
                             if (reverse)
                             {
                                 for (auto const& c : (processed_coords | reversed))
diff --git a/include/mapnik/json/topology.hpp b/include/mapnik/json/topology.hpp
index 05f35b4..048c855 100644
--- a/include/mapnik/json/topology.hpp
+++ b/include/mapnik/json/topology.hpp
@@ -62,13 +62,13 @@ struct multi_point
 
 struct linestring
 {
-    index_type ring ;
+    std::vector<index_type> rings ;
     boost::optional<properties> props;
 };
 
 struct multi_linestring
 {
-    std::vector<index_type> rings;
+    std::vector<std::vector<index_type> > lines;
     boost::optional<properties> props;
 };
 
@@ -84,7 +84,10 @@ struct multi_polygon
     boost::optional<properties> props;
 };
 
-using geometry =  util::variant<point,
+struct empty {};
+
+using geometry =  util::variant<empty,
+                                point,
                                 linestring,
                                 polygon,
                                 multi_point,
diff --git a/include/mapnik/palette.hpp b/include/mapnik/palette.hpp
index 5f96272..1276d13 100644
--- a/include/mapnik/palette.hpp
+++ b/include/mapnik/palette.hpp
@@ -43,6 +43,7 @@ using rgba_hash_table = std::unordered_map<unsigned int, unsigned char>;
 
 // stl
 #include <vector>
+#include <tuple>
 
 #define U2RED(x) ((x)&0xff)
 #define U2GREEN(x) (((x)>>8)&0xff)
@@ -53,7 +54,8 @@ namespace mapnik {
 
 struct rgba;
 
-struct MAPNIK_DECL rgb {
+struct MAPNIK_DECL rgb
+{
     std::uint8_t r;
     std::uint8_t g;
     std::uint8_t b;
@@ -92,7 +94,7 @@ struct MAPNIK_DECL rgba
           b(U2BLUE(c)),
           a(U2ALPHA(c)) {}
 
-    inline bool operator==(const rgba& y) const
+    inline bool operator==(rgba const& y) const
     {
         return r == y.r && g == y.g && b == y.b && a == y.a;
     }
@@ -103,18 +105,27 @@ struct MAPNIK_DECL rgba
         bool operator() (const rgba& x, const rgba& y) const;
     };
 
+    inline bool operator<(rgba const& y) const
+    {
+        return std::tie(r, g, b, a) < std::tie(y.r, y.g, y.b, y.a);
+    }
+
 };
 
 
-class MAPNIK_DECL rgba_palette : private util::noncopyable {
+class MAPNIK_DECL rgba_palette : private util::noncopyable
+{
 public:
     enum palette_type { PALETTE_RGBA = 0, PALETTE_RGB = 1, PALETTE_ACT = 2 };
 
     explicit rgba_palette(std::string const& pal, palette_type type = PALETTE_RGBA);
     rgba_palette();
 
-    const std::vector<rgb>& palette() const;
-    const std::vector<unsigned>& alphaTable() const;
+    inline std::vector<rgb> const& palette() const { return rgb_pal_;}
+    inline std::vector<unsigned> const& alpha_table() const { return alpha_pal_;}
+
+    inline std::vector<rgb>& palette() { return rgb_pal_;}
+    inline std::vector<unsigned>& alpha_table() { return alpha_pal_;}
 
     unsigned char quantize(unsigned c) const;
 
diff --git a/include/mapnik/params.hpp b/include/mapnik/params.hpp
index a38fa54..535cacb 100644
--- a/include/mapnik/params.hpp
+++ b/include/mapnik/params.hpp
@@ -51,6 +51,10 @@ struct value_holder : value_holder_base
     value_holder()
         : value_holder_base() {}
 
+    // C-string -> std::string
+    value_holder(char const* str)
+        : value_holder(std::string(str)) {}
+
     // perfect forwarding
     template <typename T>
     value_holder(T && obj)
diff --git a/include/mapnik/plugin.hpp b/include/mapnik/plugin.hpp
index a3428de..68e7de6 100644
--- a/include/mapnik/plugin.hpp
+++ b/include/mapnik/plugin.hpp
@@ -38,7 +38,8 @@ using mapnik_lib_t = struct _mapnik_lib_t;
 class PluginInfo : util::noncopyable
 {
 public:
-    using name_func = const char* (*) ();
+    using callable_returning_string = const char* (*) ();
+    using callable_returning_void = void (*) ();
     PluginInfo (std::string const& filename,
                 std::string const& library_name);
     ~PluginInfo();
diff --git a/include/mapnik/png_io.hpp b/include/mapnik/png_io.hpp
index 051ea6b..9f8b38d 100644
--- a/include/mapnik/png_io.hpp
+++ b/include/mapnik/png_io.hpp
@@ -39,7 +39,7 @@ extern "C"
 {
 #include <png.h>
 }
-
+#include <set>
 #pragma GCC diagnostic pop
 
 #define MAX_OCTREE_LEVELS 4
@@ -515,19 +515,19 @@ void save_as_png8_oct(T1 & file,
     }
 
     //transparency values per palette index
-    std::vector<unsigned> alphaTable;
-    //alphaTable.resize(palette.size());//allow semitransparency also in almost opaque range
+    std::vector<unsigned> alpha_table;
+    //alpha_table.resize(palette.size());//allow semitransparency also in almost opaque range
     if (opts.trans_mode != 0)
     {
-        alphaTable.resize(palette.size() - cols[TRANSPARENCY_LEVELS-1]);
+        alpha_table.resize(palette.size() - cols[TRANSPARENCY_LEVELS-1]);
     }
 
     if (palette.size() > 16 )
     {
         // >16 && <=256 colors -> write 8-bit color depth
         image_gray8 reduced_image(width,height);
-        reduce_8(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable);
-        save_as_png(file,palette,reduced_image,width,height,8,alphaTable,opts);
+        reduce_8(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alpha_table);
+        save_as_png(file,palette,reduced_image,width,height,8,alpha_table,opts);
     }
     else if (palette.size() == 1)
     {
@@ -535,13 +535,13 @@ void save_as_png8_oct(T1 & file,
         unsigned image_width  = ((width + 15) >> 3) & ~1U; // 1-bit image, round up to 16-bit boundary
         unsigned image_height = height;
         image_gray8 reduced_image(image_width,image_height);
-        reduce_1(image,reduced_image,trees, limits, alphaTable);
+        reduce_1(image,reduced_image,trees, limits, alpha_table);
         if (meanAlpha<255 && cols[0]==0)
         {
-            alphaTable.resize(1);
-            alphaTable[0] = meanAlpha;
+            alpha_table.resize(1);
+            alpha_table[0] = meanAlpha;
         }
-        save_as_png(file,palette,reduced_image,width,height,1,alphaTable,opts);
+        save_as_png(file,palette,reduced_image,width,height,1,alpha_table,opts);
     }
     else
     {
@@ -549,8 +549,8 @@ void save_as_png8_oct(T1 & file,
         unsigned image_width  = ((width + 7) >> 1) & ~3U; // 4-bit image, round up to 32-bit boundary
         unsigned image_height = height;
         image_gray8 reduced_image(image_width,image_height);
-        reduce_4(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable);
-        save_as_png(file,palette,reduced_image,width,height,4,alphaTable,opts);
+        reduce_4(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alpha_table);
+        save_as_png(file,palette,reduced_image,width,height,4,alpha_table,opts);
     }
 }
 
@@ -560,7 +560,7 @@ void save_as_png8(T1 & file,
                   T2 const& image,
                   T3 const & tree,
                   std::vector<mapnik::rgb> const& palette,
-                  std::vector<unsigned> const& alphaTable,
+                  std::vector<unsigned> const& alpha_table,
                   png_options const& opts)
 {
     unsigned width = image.width();
@@ -579,7 +579,7 @@ void save_as_png8(T1 & file,
                 row_out[x] = tree.quantize(row[x]);
             }
         }
-        save_as_png(file, palette, reduced_image, width, height, 8, alphaTable, opts);
+        save_as_png(file, palette, reduced_image, width, height, 8, alpha_table, opts);
     }
     else if (palette.size() == 1)
     {
@@ -588,7 +588,7 @@ void save_as_png8(T1 & file,
         unsigned image_height = height;
         image_gray8 reduced_image(image_width, image_height);
         reduced_image.set(0);
-        save_as_png(file, palette, reduced_image, width, height, 1, alphaTable, opts);
+        save_as_png(file, palette, reduced_image, width, height, 1, alpha_table, opts);
     }
     else
     {
@@ -612,7 +612,7 @@ void save_as_png8(T1 & file,
                 row_out[x>>1] |= index;
             }
         }
-        save_as_png(file, palette, reduced_image, width, height, 4, alphaTable, opts);
+        save_as_png(file, palette, reduced_image, width, height, 4, alpha_table, opts);
     }
 }
 
@@ -623,6 +623,7 @@ void save_as_png8_hex(T1 & file,
 {
     unsigned width = image.width();
     unsigned height = image.height();
+
     if (width + height > 3) // at least 3 pixels (hextree implementation requirement)
     {
         // structure for color quantization
@@ -647,20 +648,44 @@ void save_as_png8_hex(T1 & file,
         }
 
         //transparency values per palette index
-        std::vector<mapnik::rgba> pal;
-        tree.create_palette(pal);
+        std::vector<mapnik::rgba> rgba_palette;
+        tree.create_palette(rgba_palette);
+        auto size = rgba_palette.size();
         std::vector<mapnik::rgb> palette;
-        std::vector<unsigned> alphaTable;
-        for (unsigned i=0; i<pal.size(); ++i)
+        std::vector<unsigned> alpha_table;
+        palette.reserve(size);
+        alpha_table.reserve(size);
+        for (auto const& c : rgba_palette)
         {
-            palette.push_back(rgb(pal[i].r, pal[i].g, pal[i].b));
-            alphaTable.push_back(pal[i].a);
+            palette.emplace_back(c.r, c.g, c.b);
+            alpha_table.push_back(c.a);
         }
-        save_as_png8<T1, T2, hextree<mapnik::rgba> >(file, image, tree, palette, alphaTable, opts);
+        save_as_png8<T1, T2, hextree<mapnik::rgba> >(file, image, tree, palette, alpha_table, opts);
     }
     else
     {
-        throw std::runtime_error("Can't quantize images with less than 3 pixels");
+
+        std::set<mapnik::rgba> colors;
+        for (unsigned y = 0; y < height; ++y)
+        {
+            typename T2::pixel_type const * row = image.get_row(y);
+
+            for (unsigned x = 0; x < width; ++x)
+            {
+                unsigned val = row[x];
+                colors.emplace(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val));
+            }
+        }
+        std::string str;
+        for (auto c : colors)
+        {
+            str.push_back(c.r);
+            str.push_back(c.g);
+            str.push_back(c.b);
+            str.push_back(c.a);
+        }
+        rgba_palette pal(str, rgba_palette::PALETTE_RGBA);
+        save_as_png8<T1, T2, rgba_palette>(file, image, pal, pal.palette(), pal.alpha_table(), opts);
     }
 }
 
@@ -670,7 +695,7 @@ void save_as_png8_pal(T1 & file,
                       rgba_palette const& pal,
                       png_options const& opts)
 {
-    save_as_png8<T1, T2, rgba_palette>(file, image, pal, pal.palette(), pal.alphaTable(), opts);
+    save_as_png8<T1, T2, rgba_palette>(file, image, pal, pal.palette(), pal.alpha_table(), opts);
 }
 
 }
diff --git a/include/mapnik/renderer_common/process_point_symbolizer.hpp b/include/mapnik/renderer_common/process_point_symbolizer.hpp
index 12ffba5..ef0863f 100644
--- a/include/mapnik/renderer_common/process_point_symbolizer.hpp
+++ b/include/mapnik/renderer_common/process_point_symbolizer.hpp
@@ -61,7 +61,7 @@ void render_point_symbolizer(point_symbolizer const &sym,
 
         agg::trans_affine tr;
         auto image_transform = get_optional<transform_type>(sym, keys::image_transform);
-        if (image_transform) evaluate_transform(tr, feature, common.vars_, *image_transform);
+        if (image_transform) evaluate_transform(tr, feature, common.vars_, *image_transform, common.scale_factor_);
 
         agg::trans_affine_translation recenter(-center.x, -center.y);
         agg::trans_affine recenter_tr = recenter * tr;
diff --git a/include/mapnik/symbolizer_base.hpp b/include/mapnik/symbolizer_base.hpp
index 8ac4462..f07cf3d 100644
--- a/include/mapnik/symbolizer_base.hpp
+++ b/include/mapnik/symbolizer_base.hpp
@@ -58,7 +58,7 @@ MAPNIK_DECL void evaluate_transform(agg::trans_affine& tr,
                                     feature_impl const& feature,
                                     attributes const& vars,
                                     transform_type const& trans_expr,
-                                    double scale_factor=1.0);
+                                    double scale_factor);
 
 struct enumeration_wrapper
 {
@@ -101,7 +101,7 @@ struct strict_value : value_base_type
     strict_value() = default;
 
     strict_value(const char* val)
-        : value_base_type(val) {}
+        : value_base_type(std::string(val)) {}
 
     template <typename T>
     strict_value(T const& obj)
diff --git a/include/mapnik/text/placement_finder.hpp b/include/mapnik/text/placement_finder.hpp
index f35a5d5..3b02d89 100644
--- a/include/mapnik/text/placement_finder.hpp
+++ b/include/mapnik/text/placement_finder.hpp
@@ -78,7 +78,6 @@ private:
     // Maps upright==auto, left-only and right-only to left,right to simplify processing.
     // angle = angle of at start of line (to estimate best option for upright==auto)
     text_upright_e simplify_upright(text_upright_e upright, double angle) const;
-    box2d<double> get_bbox(text_layout const& layout, glyph_info const& glyph, pixel_position const& pos, rotation const& rot);
     feature_impl const& feature_;
     attributes const& attr_;
     DetectorType & detector_;
diff --git a/include/mapnik/text/scrptrun.hpp b/include/mapnik/text/scrptrun.hpp
index 7c8063b..2161c51 100644
--- a/include/mapnik/text/scrptrun.hpp
+++ b/include/mapnik/text/scrptrun.hpp
@@ -105,7 +105,7 @@ private:
 
 inline ScriptRun::ScriptRun()
 {
-    reset(NULL, 0, 0);
+    reset(nullptr, 0, 0);
 }
 
 inline ScriptRun::ScriptRun(const UChar chars[], int32_t length)
diff --git a/include/mapnik/text/symbolizer_helpers.hpp b/include/mapnik/text/symbolizer_helpers.hpp
index 6d6fbdc..8fd3c15 100644
--- a/include/mapnik/text/symbolizer_helpers.hpp
+++ b/include/mapnik/text/symbolizer_helpers.hpp
@@ -158,6 +158,10 @@ protected:
     void init_marker() const;
 };
 
-} //namespace
+namespace geometry {
+MAPNIK_DECL mapnik::box2d<double> envelope(mapnik::base_symbolizer_helper::geometry_cref const& geom);
+}
+
+} //namespace mapnik
 
 #endif // SYMBOLIZER_HELPERS_HPP
diff --git a/include/mapnik/util/char_array_buffer.hpp b/include/mapnik/util/char_array_buffer.hpp
new file mode 100644
index 0000000..e5b834b
--- /dev/null
+++ b/include/mapnik/util/char_array_buffer.hpp
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#ifndef MAPNIK_UTIL_CHAR_ARRAY_BUFFER_HPP
+#define MAPNIK_UTIL_CHAR_ARRAY_BUFFER_HPP
+
+#include <streambuf>
+
+namespace mapnik { namespace util {
+
+// ref https://artofcode.wordpress.com/2010/12/12/deriving-from-stdstreambuf/
+
+class char_array_buffer : public std::streambuf
+{
+public:
+    char_array_buffer(char const* data, std::size_t size)
+        : begin_(data), end_(data + size), current_(data) {}
+
+private:
+    int_type underflow()
+    {
+        if (current_ == end_)
+        {
+            return traits_type::eof();
+        }
+        return traits_type::to_int_type(*current_);
+    }
+
+    int_type uflow()
+    {
+        if (current_ == end_)
+        {
+            return traits_type::eof();
+        }
+        return traits_type::to_int_type(*current_++);
+    }
+
+    int_type pbackfail(int_type ch)
+    {
+        if (current_ == begin_ || (ch != traits_type::eof() && ch != current_[-1]))
+        {
+            return traits_type::eof();
+        }
+        return traits_type::to_int_type(*--current_);
+    }
+
+    std::streamsize showmanyc()
+    {
+        return end_ - current_;
+    }
+
+    pos_type seekoff(off_type off, std::ios_base::seekdir dir,
+                     std::ios_base::openmode which = std::ios_base::in | std::ios_base::out )
+    {
+        if (dir == std::ios_base::beg) current_ = std::min(begin_ + off, end_);
+        else if (dir == std::ios_base::cur) current_ = std::min(current_ + off, end_);
+        else current_ = std::max(end_ - off, begin_); // dir == std::ios_base::end
+        return pos_type(off_type(current_ - begin_));
+    }
+    char const * const begin_;
+    char const * const end_;
+    char const * current_;
+};
+
+}}
+
+
+#endif // MAPNIK_UTIL_CHAR_ARRAY_BUFFER_HPP
diff --git a/include/mapnik/util/is_clockwise.hpp b/include/mapnik/util/is_clockwise.hpp
index 2483727..93951df 100644
--- a/include/mapnik/util/is_clockwise.hpp
+++ b/include/mapnik/util/is_clockwise.hpp
@@ -23,6 +23,8 @@
 #ifndef MAPNIK_UTIL_IS_CLOCKWISE_HPP
 #define MAPNIK_UTIL_IS_CLOCKWISE_HPP
 
+#include <cassert>
+
 namespace mapnik { namespace util {
 
 template <typename T>
@@ -30,11 +32,18 @@ bool is_clockwise(T const& ring)
 {
     double area = 0.0;
     std::size_t num_points = ring.size();
+    assert(num_points > 2);
+    double orig_x = ring[0].x;
+    double orig_y = ring[0].y;
     for (std::size_t i = 0; i < num_points; ++i)
     {
         auto const& p0 = ring[i];
         auto const& p1 = ring[(i + 1) % num_points];
-        area += p0.x * p1.y - p0.y * p1.x;
+        double x0 = p0.x - orig_x;
+        double y0 = p0.y - orig_y;
+        double x1 = p1.x - orig_x;
+        double y1 = p1.y - orig_y;
+        area += x0 * y1 - x1 * y0;
     }
     return (area < 0.0) ? true : false;
 }
diff --git a/include/mapnik/util/variant.hpp b/include/mapnik/util/variant.hpp
index 05dddec..87cfb70 100644
--- a/include/mapnik/util/variant.hpp
+++ b/include/mapnik/util/variant.hpp
@@ -24,7 +24,7 @@
 #define MAPNIK_UTIL_VARIANT_HPP
 
 #include <mapnik/config.hpp>
-#include <mapbox/variant/variant.hpp>
+#include <mapbox/variant.hpp>
 
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
@@ -51,13 +51,13 @@ public:
 // unary visitor interface
 // const
 template <typename F, typename V>
-auto VARIANT_INLINE static apply_visitor(F && f, V const& v) -> decltype(V::visit(v, f))
+auto VARIANT_INLINE static apply_visitor(F && f, V const& v) -> decltype(V::visit(v, std::forward<F>(f)))
 {
     return V::visit(v, std::forward<F>(f));
 }
 // non-const
 template <typename F, typename V>
-auto VARIANT_INLINE static apply_visitor(F && f, V & v) -> decltype(V::visit(v, f))
+auto VARIANT_INLINE static apply_visitor(F && f, V & v) -> decltype(V::visit(v, std::forward<F>(f)))
 {
     return V::visit(v, std::forward<F>(f));
 }
@@ -65,14 +65,14 @@ auto VARIANT_INLINE static apply_visitor(F && f, V & v) -> decltype(V::visit(v,
 // binary visitor interface
 // const
 template <typename F, typename V>
-auto VARIANT_INLINE static apply_visitor(F && f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, f))
+auto VARIANT_INLINE static apply_visitor(F && f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
 {
     return V::binary_visit(v0, v1, std::forward<F>(f));
 }
 
 // non-const
 template <typename F, typename V>
-auto VARIANT_INLINE static apply_visitor(F && f, V & v0, V & v1) -> decltype(V::binary_visit(v0, v1, f))
+auto VARIANT_INLINE static apply_visitor(F && f, V & v0, V & v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
 {
     return V::binary_visit(v0, v1, std::forward<F>(f));
 }
diff --git a/include/mapnik/value_hash.hpp b/include/mapnik/value_hash.hpp
index b050e37..80e10d8 100644
--- a/include/mapnik/value_hash.hpp
+++ b/include/mapnik/value_hash.hpp
@@ -26,22 +26,20 @@
 // mapnik
 #include <mapnik/util/variant.hpp>
 #include <mapnik/value_types.hpp>
-
 // stl
 #include <functional>
 
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
 #include <unicode/unistr.h>
+
 #pragma GCC diagnostic pop
 
 namespace mapnik { namespace detail {
 
-template <class T>
-inline void hash_combine(std::size_t & seed, T const& v)
+inline void hash_combine(std::size_t & seed, std::size_t val)
 {
-    std::hash<T> hasher;
-    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+    seed ^= val + 0x9e3779b9 + (seed << 6) + (seed >> 2);
 }
 
 struct value_hasher
@@ -56,6 +54,11 @@ struct value_hasher
         return static_cast<std::size_t>(val.hashCode());
     }
 
+    std::size_t operator()(value_integer val) const
+    {
+        return static_cast<std::size_t>(val);
+    }
+
     template <class T>
     std::size_t operator()(T const& val) const
     {
@@ -69,7 +72,8 @@ struct value_hasher
 template <typename T>
 std::size_t mapnik_hash_value(T const& val)
 {
-    std::size_t seed = util::apply_visitor(detail::value_hasher(), val);
+    std::size_t seed = 0;
+    detail::hash_combine(seed, util::apply_visitor(detail::value_hasher(), val));
     detail::hash_combine(seed, val.which());
     return seed;
 }
diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp
index e1a367b..deba428 100644
--- a/include/mapnik/version.hpp
+++ b/include/mapnik/version.hpp
@@ -27,7 +27,7 @@
 
 #define MAPNIK_MAJOR_VERSION 3
 #define MAPNIK_MINOR_VERSION 0
-#define MAPNIK_PATCH_VERSION 11
+#define MAPNIK_PATCH_VERSION 12
 
 #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
 
diff --git a/include/mapnik/vertex_adapters.hpp b/include/mapnik/vertex_adapters.hpp
index 3a41bee..0c77d75 100644
--- a/include/mapnik/vertex_adapters.hpp
+++ b/include/mapnik/vertex_adapters.hpp
@@ -32,10 +32,10 @@ namespace mapnik { namespace geometry {
 template <typename T>
 struct point_vertex_adapter
 {
-    using value_type = typename point<T>::value_type;
+    using coord_type = typename point<T>::coord_type;
 
     point_vertex_adapter(point<T> const& pt);
-    unsigned vertex(value_type * x, value_type * y) const;
+    unsigned vertex(coord_type * x, coord_type * y) const;
     void rewind(unsigned) const;
     geometry_types type () const;
     point<T> const& pt_;
@@ -45,9 +45,9 @@ struct point_vertex_adapter
 template <typename T>
 struct line_string_vertex_adapter
 {
-    using value_type = typename point<T>::value_type;
+    using coord_type = typename point<T>::coord_type;
     line_string_vertex_adapter(line_string<T> const& line);
-    unsigned vertex(value_type * x, value_type * y) const;
+    unsigned vertex(coord_type * x, coord_type * y) const;
     void rewind(unsigned) const;
     geometry_types type () const;
     line_string<T> const& line_;
@@ -58,10 +58,10 @@ struct line_string_vertex_adapter
 template <typename T>
 struct polygon_vertex_adapter
 {
-    using value_type = typename point<T>::value_type;
+    using coord_type = typename point<T>::coord_type;
     polygon_vertex_adapter(polygon<T> const& poly);
     void rewind(unsigned) const;
-    unsigned vertex(value_type * x, value_type * y) const;
+    unsigned vertex(coord_type * x, coord_type * y) const;
     geometry_types type () const;
 private:
     polygon<T> const& poly_;
@@ -75,10 +75,10 @@ private:
 template <typename T>
 struct ring_vertex_adapter
 {
-    using value_type = typename point<T>::value_type;
+    using coord_type = typename point<T>::coord_type;
     ring_vertex_adapter(linear_ring<T> const& ring);
     void rewind(unsigned) const;
-    unsigned vertex(value_type * x, value_type * y) const;
+    unsigned vertex(coord_type * x, coord_type * y) const;
     geometry_types type () const;
 private:
     linear_ring<T> const& ring_;
diff --git a/include/mapnik/warning_ignore.hpp b/include/mapnik/warning_ignore.hpp
index 62b7899..95249b8 100644
--- a/include/mapnik/warning_ignore.hpp
+++ b/include/mapnik/warning_ignore.hpp
@@ -23,6 +23,7 @@
 
 #pragma GCC diagnostic ignored "-Wunknown-pragmas" // clang+gcc
 #pragma GCC diagnostic ignored "-Wpragmas" // gcc
+#pragma GCC diagnostic ignored "-W#pragma-messages"
 #pragma GCC diagnostic ignored "-Wunsequenced"
 #pragma GCC diagnostic ignored "-Wunused-function"
 #pragma GCC diagnostic ignored "-Wunused-parameter"
diff --git a/include/mapnik/wkt/wkt_generator_grammar.hpp b/include/mapnik/wkt/wkt_generator_grammar.hpp
index 892e551..f31fcc0 100644
--- a/include/mapnik/wkt/wkt_generator_grammar.hpp
+++ b/include/mapnik/wkt/wkt_generator_grammar.hpp
@@ -104,7 +104,7 @@ template <typename OutputIterator, typename Geometry>
 struct wkt_generator_grammar :
     karma::grammar<OutputIterator, Geometry const& ()>
 {
-    using coord_type = typename Geometry::value_type;
+    using coord_type = typename Geometry::coord_type;
     wkt_generator_grammar();
     // rules
     karma::rule<OutputIterator, Geometry const&()> geometry;
diff --git a/include/mapnik/xml_attribute_cast.hpp b/include/mapnik/xml_attribute_cast.hpp
index fb3b7eb..cafa7e7 100644
--- a/include/mapnik/xml_attribute_cast.hpp
+++ b/include/mapnik/xml_attribute_cast.hpp
@@ -55,7 +55,7 @@ struct do_xml_attribute_cast
 {
     static inline boost::optional<T> xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& /*source*/)
     {
-        std::string err_msg("No conversion from std::string to");
+        std::string err_msg("No conversion from std::string to ");
         err_msg += std::string(typeid(T).name());
         throw std::runtime_error(err_msg);
     }
@@ -74,6 +74,19 @@ struct do_xml_attribute_cast<mapnik::boolean_type>
     }
 };
 
+// specialization for mapnik::value_bool
+template <>
+struct do_xml_attribute_cast<mapnik::value_bool>
+{
+    static inline boost::optional<mapnik::value_bool> xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source)
+    {
+        bool result;
+        if (mapnik::util::string2bool(source, result))
+            return boost::optional<mapnik::value_bool>(result);
+        return boost::optional<mapnik::value_bool>();
+    }
+};
+
 // specialization for int
 template <>
 struct do_xml_attribute_cast<int>
diff --git a/localize.sh b/localize.sh
index 4b69c2f..3d034f5 100755
--- a/localize.sh
+++ b/localize.sh
@@ -1,10 +1,12 @@
 #!/bin/bash
+
 # TODO - use rpath to avoid needing this to run tests locally
+
 export CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-if [ $(uname -s) = 'Darwin' ]; then
-    export DYLD_LIBRARY_PATH="${CURRENT_DIR}/src/":${DYLD_LIBRARY_PATH}
-else
-    export LD_LIBRARY_PATH="${CURRENT_DIR}/src/":${LD_LIBRARY_PATH}
+if [[ $(uname -s) == 'Darwin' ]]; then
+    export DYLD_LIBRARY_PATH="${CURRENT_DIR}/src/":${DYLD_LIBRARY_PATH:-""}
+elif [[ $(uname -s) == 'Linux' ]]; then
+    export LD_LIBRARY_PATH="${CURRENT_DIR}/src/":${LD_LIBRARY_PATH:-""}
 fi
 
 export PATH=$(pwd)/utils/mapnik-render/:${PATH}
diff --git a/mason_latest.sh b/mason_latest.sh
index 7b3e02a..d5db696 100755
--- a/mason_latest.sh
+++ b/mason_latest.sh
@@ -1,12 +1,14 @@
 #!/usr/bin/env bash
 
-set -eu
-
 MASON_NAME=mapnik
 MASON_VERSION=latest
 MASON_LIB_FILE=lib/libmapnik-wkt.a
 
-. ${MASON_DIR:-~/.mason}/mason.sh
+# warning: may break when https://github.com/mapbox/mason/issues/141 lands
+# hence we are pinned for now to older mason in bootstrap.sh
+. ./.mason/mason.sh
+
+set -eu
 
 function mason_load_source {
     export MASON_BUILD_PATH=$(pwd)
diff --git a/plugins/input/csv/csv_datasource.cpp b/plugins/input/csv/csv_datasource.cpp
index 6c1ff10..4a246b4 100644
--- a/plugins/input/csv/csv_datasource.cpp
+++ b/plugins/input/csv/csv_datasource.cpp
@@ -431,7 +431,7 @@ mapnik::featureset_ptr csv_datasource::features(mapnik::query const& q) const
             return std::make_shared<csv_index_featureset>(filename_, filter, locator_, separator_, quote_, headers_, ctx_);
         }
     }
-    return mapnik::featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 mapnik::featureset_ptr csv_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
diff --git a/plugins/input/gdal/gdal_datasource.cpp b/plugins/input/gdal/gdal_datasource.cpp
index 07cec47..2454ed6 100644
--- a/plugins/input/gdal/gdal_datasource.cpp
+++ b/plugins/input/gdal/gdal_datasource.cpp
@@ -44,11 +44,14 @@ using mapnik::featureset_ptr;
 using mapnik::layer_descriptor;
 using mapnik::datasource_exception;
 
+static std::once_flag once_flag;
 
-static bool GDALAllRegister_once_()
+extern "C" MAPNIK_EXP void on_plugin_load()
 {
-    static bool const quiet_unused = (GDALAllRegister(), true);
-    return quiet_unused;
+    // initialize gdal formats
+    std::call_once(once_flag,[](){
+        GDALAllRegister();
+    });
 }
 
 gdal_datasource::gdal_datasource(parameters const& params)
@@ -60,8 +63,6 @@ gdal_datasource::gdal_datasource(parameters const& params)
 {
     MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource: Initializing...";
 
-    GDALAllRegister_once_();
-
 #ifdef MAPNIK_STATS
     mapnik::progress_timer __stats__(std::clog, "gdal_datasource::init");
 #endif
diff --git a/plugins/input/gdal/gdal_featureset.cpp b/plugins/input/gdal/gdal_featureset.cpp
index fe1fe64..1377322 100644
--- a/plugins/input/gdal/gdal_featureset.cpp
+++ b/plugins/input/gdal/gdal_featureset.cpp
@@ -44,6 +44,41 @@ using mapnik::view_transform;
 using mapnik::datasource_exception;
 using mapnik::feature_factory;
 
+#ifdef MAPNIK_LOG
+namespace {
+
+void get_overview_meta(GDALRasterBand* band)
+{
+    int band_overviews = band->GetOverviewCount();
+    if (band_overviews > 0)
+    {
+        MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: " << band_overviews << " overviews found!";
+
+        for (int b = 0; b < band_overviews; b++)
+        {
+            GDALRasterBand * overview = band->GetOverview(b);
+            MAPNIK_LOG_DEBUG(gdal) << "Overview= " << b
+              << " Width=" << overview->GetXSize()
+              << " Height=" << overview->GetYSize();
+        }
+    }
+    else
+    {
+        MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: No overviews found!";
+    }
+
+    int bsx,bsy;
+    double scale;
+    band->GetBlockSize(&bsx, &bsy);
+    scale = band->GetScale();
+
+    MAPNIK_LOG_DEBUG(gdal) << "Block=" << bsx << "x" << bsy
+        << " Scale=" << scale
+        << " Type=" << GDALGetDataTypeName(band->GetRasterDataType())
+        << "Color=" << GDALGetColorInterpretationName(band->GetColorInterpretation());
+}
+} // anonymous ns
+#endif
 gdal_featureset::gdal_featureset(GDALDataset& dataset,
                                  int band,
                                  gdal_query q,
@@ -403,15 +438,15 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
                     if( red->GetBand() == 1 && green->GetBand() == 2 && blue->GetBand() == 3 )
                     {
                         int nBandsToRead = 3;
-                        if( alpha != NULL && alpha->GetBand() == 4 && !raster_has_nodata )
+                        if( alpha != nullptr && alpha->GetBand() == 4 && !raster_has_nodata )
                         {
                             nBandsToRead = 4;
-                            alpha = NULL; // to avoid reading it again afterwards
+                            alpha = nullptr; // to avoid reading it again afterwards
                         }
                         raster_io_error = dataset_.RasterIO(GF_Read, x_off, y_off, width, height,
                                                             image.bytes(),
                                                             image.width(), image.height(), GDT_Byte,
-                                                            nBandsToRead, NULL,
+                                                            nBandsToRead, nullptr,
                                                             4, 4 * image.width(), 1);
                         if (raster_io_error == CE_Failure) {
                             throw datasource_exception(CPLGetLastErrorMsg());
@@ -551,6 +586,32 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
                         MAPNIK_LOG_WARN(gdal) << "warning: nodata value (" << raster_nodata << ") used to set transparency instead of alpha band";
                     }
                 }
+                else if( dataset_.GetRasterCount() > 0 && dataset_.GetRasterBand(1) )
+                {
+                    // Check if we have a non-alpha mask band (for example a TIFF internal mask)
+                    int flags = dataset_.GetRasterBand(1)->GetMaskFlags();
+                    GDALRasterBand* mask = 0;
+                    if (flags == GMF_PER_DATASET)
+                    {
+                        mask = dataset_.GetRasterBand(1)->GetMaskBand();
+                    }
+                    if (mask)
+                    {
+                        MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: found and processing mask band...";
+                        if (!raster_has_nodata)
+                        {
+                            raster_io_error = mask->RasterIO(GF_Read, x_off, y_off, width, height, image.bytes() + 3,
+                                                              image.width(), image.height(), GDT_Byte, 4, 4 * image.width());
+                            if (raster_io_error == CE_Failure) {
+                                throw datasource_exception(CPLGetLastErrorMsg());
+                            }
+                        }
+                        else
+                        {
+                            MAPNIK_LOG_WARN(gdal) << "warning: nodata value (" << raster_nodata << ") used to set transparency instead of mask band";
+                        }
+                    }
+                }
                 mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(intersect, image, filter_factor);
                 // set nodata value to be used in raster colorizer
                 if (nodata_value_) raster->set_nodata(*nodata_value_);
@@ -620,36 +681,3 @@ feature_ptr gdal_featureset::get_feature_at_point(mapnik::coord2d const& pt)
     }
     return feature_ptr();
 }
-
-#ifdef MAPNIK_LOG
-void gdal_featureset::get_overview_meta(GDALRasterBand* band)
-{
-    int band_overviews = band->GetOverviewCount();
-    if (band_overviews > 0)
-    {
-        MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: " << band_overviews << " overviews found!";
-
-        for (int b = 0; b < band_overviews; b++)
-        {
-            GDALRasterBand * overview = band->GetOverview(b);
-            MAPNIK_LOG_DEBUG(gdal) << "Overview= " << b
-              << " Width=" << overview->GetXSize()
-              << " Height=" << overview->GetYSize();
-        }
-    }
-    else
-    {
-        MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: No overviews found!";
-    }
-
-    int bsx,bsy;
-    double scale;
-    band->GetBlockSize(&bsx, &bsy);
-    scale = band->GetScale();
-
-    MAPNIK_LOG_DEBUG(gdal) << "Block=" << bsx << "x" << bsy
-        << " Scale=" << scale
-        << " Type=" << GDALGetDataTypeName(band->GetRasterDataType())
-        << "Color=" << GDALGetColorInterpretationName(band->GetColorInterpretation());
-}
-#endif
diff --git a/plugins/input/gdal/gdal_featureset.hpp b/plugins/input/gdal/gdal_featureset.hpp
index 639028b..f2f66f7 100644
--- a/plugins/input/gdal/gdal_featureset.hpp
+++ b/plugins/input/gdal/gdal_featureset.hpp
@@ -73,11 +73,6 @@ public:
 private:
     mapnik::feature_ptr get_feature(mapnik::query const& q);
     mapnik::feature_ptr get_feature_at_point(mapnik::coord2d const& p);
-
-#ifdef MAPNIK_LOG
-    void get_overview_meta(GDALRasterBand * band);
-#endif
-
     GDALDataset & dataset_;
     mapnik::context_ptr ctx_;
     int band_;
diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp
index e9456f8..d2ebc2d 100644
--- a/plugins/input/geojson/geojson_datasource.cpp
+++ b/plugins/input/geojson/geojson_datasource.cpp
@@ -587,8 +587,8 @@ mapnik::featureset_ptr geojson_datasource::features(mapnik::query const& q) cons
         }
 
     }
-    // otherwise return an empty featureset pointer
-    return mapnik::featureset_ptr();
+    // otherwise return an empty featureset
+    return mapnik::make_empty_featureset();
 }
 
 mapnik::featureset_ptr geojson_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
diff --git a/plugins/input/ogr/ogr_datasource.cpp b/plugins/input/ogr/ogr_datasource.cpp
index 41c0ebb..2943962 100644
--- a/plugins/input/ogr/ogr_datasource.cpp
+++ b/plugins/input/ogr/ogr_datasource.cpp
@@ -59,6 +59,16 @@ using mapnik::datasource_exception;
 using mapnik::filter_in_box;
 using mapnik::filter_at_point;
 
+static std::once_flag once_flag;
+
+extern "C" MAPNIK_EXP void on_plugin_load()
+{
+    // initialize ogr formats
+    // NOTE: in GDAL >= 2.0 this is the same as GDALAllRegister()
+    std::call_once(once_flag,[](){
+        OGRRegisterAll();
+    });
+}
 
 ogr_datasource::ogr_datasource(parameters const& params)
     : datasource(params),
@@ -87,10 +97,6 @@ void ogr_datasource::init(mapnik::parameters const& params)
     mapnik::progress_timer __stats__(std::clog, "ogr_datasource::init");
 #endif
 
-    // initialize ogr formats
-    // NOTE: in GDAL >= 2.0 this is the same as GDALAllRegister()
-    OGRRegisterAll();
-
     boost::optional<std::string> file = params.get<std::string>("file");
     boost::optional<std::string> string = params.get<std::string>("string");
     if (!string) string  = params.get<std::string>("inline");
@@ -371,7 +377,7 @@ void ogr_datasource::init(mapnik::parameters const& params)
     }
     mapnik::parameters & extra_params = desc_.get_extra_parameters();
     OGRSpatialReference * srs_ref = layer->GetSpatialRef();
-    char * srs_output = NULL;
+    char * srs_output = nullptr;
     if (srs_ref && srs_ref->exportToProj4( &srs_output ) == OGRERR_NONE ) {
         extra_params["proj4"] = mapnik::util::trim_copy(srs_output);
     }
@@ -554,7 +560,7 @@ featureset_ptr ogr_datasource::features(query const& q) const
         }
     }
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 featureset_ptr ogr_datasource::features_at_point(coord2d const& pt, double tol) const
@@ -597,5 +603,5 @@ featureset_ptr ogr_datasource::features_at_point(coord2d const& pt, double tol)
         }
     }
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
diff --git a/plugins/input/pgraster/pgraster_datasource.cpp b/plugins/input/pgraster/pgraster_datasource.cpp
index 451c74e..ced1810 100644
--- a/plugins/input/pgraster/pgraster_datasource.cpp
+++ b/plugins/input/pgraster/pgraster_datasource.cpp
@@ -257,7 +257,7 @@ pgraster_datasource::pgraster_datasource(parameters const& params)
                         if (srid_ == 0)
                         {
                             const char* srid_c = rs->getValue("srid");
-                            if (srid_c != NULL)
+                            if (srid_c != nullptr)
                             {
                                 int result = 0;
                                 const char * end = srid_c + std::strlen(srid_c);
@@ -274,7 +274,7 @@ pgraster_datasource::pgraster_datasource(parameters const& params)
                     }
                     else
                     {
-            MAPNIK_LOG_DEBUG(pgraster) << "pgraster_datasource: no response from metadata query " << s.str();
+                        MAPNIK_LOG_DEBUG(pgraster) << "pgraster_datasource: no response from metadata query " << s.str();
                     }
                     rs->close();
                 }
@@ -297,7 +297,7 @@ pgraster_datasource::pgraster_datasource(parameters const& params)
                     if (rs->next())
                     {
                         const char* srid_c = rs->getValue("srid");
-                        if (srid_c != NULL)
+                        if (srid_c != nullptr)
                         {
                             int result = 0;
                             const char * end = srid_c + std::strlen(srid_c);
@@ -878,9 +878,10 @@ featureset_ptr pgraster_datasource::features_with_context(query const& q,process
         std::string table_with_bbox;
         std::string col = geometryColumn_;
 
-        if ( use_overviews_ ) {
-          std::string sch = schema_;
-          std::string tab = mapnik::sql_utils::unquote_double(raster_table_);
+        if ( use_overviews_ && !overviews_.empty()) {
+          std::string sch = overviews_[0].schema;
+          std::string tab = overviews_[0].table;
+          col = overviews_[0].column;
           const double scale = std::min(px_gw, px_gh);
           std::vector<pgraster_overview>::const_reverse_iterator i;
           for (i=overviews_.rbegin(); i!=overviews_.rend(); ++i) {
@@ -997,7 +998,7 @@ featureset_ptr pgraster_datasource::features_with_context(query const& q,process
 
     }
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 
@@ -1010,7 +1011,7 @@ featureset_ptr pgraster_datasource::features_at_point(coord2d const& pt, double
     if (pool)
     {
         shared_ptr<Connection> conn = pool->borrowObject();
-        if (!conn) return featureset_ptr();
+        if (!conn) return mapnik::make_empty_featureset();
 
         if (conn->isOK())
         {
@@ -1081,7 +1082,7 @@ featureset_ptr pgraster_datasource::features_at_point(coord2d const& pt, double
         }
     }
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 box2d<double> pgraster_datasource::envelope() const
diff --git a/plugins/input/pgraster/pgraster_wkb_reader.cpp b/plugins/input/pgraster/pgraster_wkb_reader.cpp
index b97d0cd..4a9fd1c 100644
--- a/plugins/input/pgraster/pgraster_wkb_reader.cpp
+++ b/plugins/input/pgraster/pgraster_wkb_reader.cpp
@@ -66,7 +66,7 @@ read_uint16(const uint8_t** from, uint8_t littleEndian) {
 /*
 int16_t
 read_int16(const uint8_t** from, uint8_t littleEndian) {
-    assert(NULL != from);
+    assert(nullptr != from);
 
     return read_uint16(from, littleEndian);
 }
diff --git a/plugins/input/postgis/postgis_datasource.cpp b/plugins/input/postgis/postgis_datasource.cpp
index 909cd58..2653e57 100644
--- a/plugins/input/postgis/postgis_datasource.cpp
+++ b/plugins/input/postgis/postgis_datasource.cpp
@@ -479,7 +479,7 @@ postgis_datasource::postgis_datasource(parameters const& params)
         // Finally, add unique metadata to layer descriptor
         mapnik::parameters & extra_params = desc_.get_extra_parameters();
         // explicitly make copies of values due to https://github.com/mapnik/mapnik/issues/2651
-        extra_params["srid"] = srid_;
+        extra_params["srid"] = mapnik::value_integer(srid_);
         if (!key_field_.empty())
         {
             extra_params["key_field"] = key_field_;
@@ -942,7 +942,7 @@ featureset_ptr postgis_datasource::features_with_context(query const& q,processo
 
     }
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 
@@ -955,7 +955,7 @@ featureset_ptr postgis_datasource::features_at_point(coord2d const& pt, double t
     if (pool)
     {
         shared_ptr<Connection> conn = pool->borrowObject();
-        if (!conn) return featureset_ptr();
+        if (!conn) return mapnik::make_empty_featureset();
 
         if (conn->isOK())
         {
@@ -1030,7 +1030,7 @@ featureset_ptr postgis_datasource::features_at_point(coord2d const& pt, double t
         }
     }
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 box2d<double> postgis_datasource::envelope() const
diff --git a/plugins/input/raster/raster_datasource.cpp b/plugins/input/raster/raster_datasource.cpp
index 4d8b5f9..38a6aa9 100644
--- a/plugins/input/raster/raster_datasource.cpp
+++ b/plugins/input/raster/raster_datasource.cpp
@@ -224,5 +224,5 @@ featureset_ptr raster_datasource::features_at_point(coord2d const&, double tol)
 {
     MAPNIK_LOG_WARN(raster) << "raster_datasource: feature_at_point not supported";
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
diff --git a/plugins/input/sqlite/sqlite_datasource.cpp b/plugins/input/sqlite/sqlite_datasource.cpp
index 939d9d3..37e4411 100644
--- a/plugins/input/sqlite/sqlite_datasource.cpp
+++ b/plugins/input/sqlite/sqlite_datasource.cpp
@@ -551,7 +551,7 @@ featureset_ptr sqlite_datasource::features(query const& q) const
                                                      using_subquery_);
     }
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt, double tol) const
@@ -631,5 +631,5 @@ featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt, double to
                                                      using_subquery_);
     }
 
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
diff --git a/plugins/input/topojson/topojson_datasource.cpp b/plugins/input/topojson/topojson_datasource.cpp
index 4c4e6b5..e6cab26 100644
--- a/plugins/input/topojson/topojson_datasource.cpp
+++ b/plugins/input/topojson/topojson_datasource.cpp
@@ -66,18 +66,9 @@ struct attr_value_converter
     {
         return mapnik::Boolean;
     }
-
-    mapnik::eAttributeType operator() (std::string const& /*val*/) const
-    {
-        return mapnik::String;
-    }
-
-    mapnik::eAttributeType operator() (mapnik::value_unicode_string const& /*val*/) const
-    {
-        return mapnik::String;
-    }
-
-    mapnik::eAttributeType operator() (mapnik::value_null const& /*val*/) const
+    // string, object, array
+    template <typename T>
+    mapnik::eAttributeType operator() (T const& /*val*/) const
     {
         return mapnik::String;
     }
@@ -109,6 +100,11 @@ struct geometry_type_visitor
     {
         return static_cast<int>(mapnik::datasource_geometry_t::Polygon);
     }
+    template <typename T>
+    int operator() (T const& ) const
+    {
+        return 0;
+    }
 };
 
 struct collect_attributes_visitor
@@ -117,6 +113,9 @@ struct collect_attributes_visitor
     collect_attributes_visitor(mapnik::layer_descriptor & desc):
       desc_(desc) {}
 
+    // no-op
+    void operator() (mapnik::topojson::empty) {}
+    //
     template <typename GeomType>
     void operator() (GeomType const& g)
     {
@@ -285,7 +284,7 @@ mapnik::featureset_ptr topojson_datasource::features(mapnik::query const& q) con
         }
     }
     // otherwise return an empty featureset pointer
-    return mapnik::featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 mapnik::featureset_ptr topojson_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
diff --git a/scripts/travis-common.sh b/scripts/travis-common.sh
index 15b38dd..40d1837 100644
--- a/scripts/travis-common.sh
+++ b/scripts/travis-common.sh
@@ -62,7 +62,7 @@ commit_message_contains () {
 }
 
 commit_message_parse () {
-    if commit_message_contains '[skip tests]' || [[ $(uname -s) == 'Darwin' ]]; then
+    if commit_message_contains '[skip tests]'; then
         config_override "CPP_TESTS = False"
     fi
     if commit_message_contains '[skip utils]'; then
@@ -92,8 +92,7 @@ configure () {
 
 coverage () {
     ./mason_packages/.link/bin/cpp-coveralls \
-        --gcov /usr/bin/llvm-cov-${LLVM_VERSION} \
-        --build-root . --gcov-options '\-lp' \
+        --gcov ${LLVM_COV} \
         --exclude mason_packages \
         --exclude .sconf_temp --exclude benchmark --exclude deps \
         --exclude scons --exclude test --exclude demo --exclude docs \
diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp
index 3b947a4..dbbb7f2 100644
--- a/src/agg/process_line_pattern_symbolizer.cpp
+++ b/src/agg/process_line_pattern_symbolizer.cpp
@@ -92,7 +92,7 @@ struct agg_renderer_process_visitor_l
         value_double opacity = get<value_double, keys::opacity>(sym_, feature_, common_.vars_);
         agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
         auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
-        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
+        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
         mapnik::box2d<double> const& bbox_image = marker.get_data()->bounding_box() * image_tr;
         image_rgba8 image(bbox_image.width(), bbox_image.height());
         render_pattern<buffer_type>(*ras_ptr_, marker, image_tr, 1.0, image);
diff --git a/src/agg/process_polygon_pattern_symbolizer.cpp b/src/agg/process_polygon_pattern_symbolizer.cpp
index e654886..f2a0159 100644
--- a/src/agg/process_polygon_pattern_symbolizer.cpp
+++ b/src/agg/process_polygon_pattern_symbolizer.cpp
@@ -84,7 +84,7 @@ struct agg_renderer_process_visitor_p
     {
         agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
         auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
-        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
+        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
         mapnik::box2d<double> const& bbox_image = marker.get_data()->bounding_box() * image_tr;
         mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height());
         render_pattern<buffer_type>(*ras_ptr_, marker, image_tr, 1.0, image);
diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp
index b3794bc..685b66c 100644
--- a/src/agg/process_text_symbolizer.cpp
+++ b/src/agg/process_text_symbolizer.cpp
@@ -63,7 +63,7 @@ void agg_renderer<T0,T1>::process(text_symbolizer const& sym,
     if (halo_transform)
     {
         agg::trans_affine halo_affine_transform;
-        evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform);
+        evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform, common_.scale_factor_);
         ren.set_halo_transform(halo_affine_transform);
     }
 
diff --git a/src/box2d.cpp b/src/box2d.cpp
index 7d54233..407c054 100644
--- a/src/box2d.cpp
+++ b/src/box2d.cpp
@@ -2,7 +2,7 @@
  *
  * This file is part of Mapnik (c++ mapping toolkit)
  *
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2016 Artem Pavlenko
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -21,478 +21,9 @@
  *****************************************************************************/
 
 // mapnik
-#include <mapnik/box2d.hpp>
-#include <mapnik/safe_cast.hpp>
+#include <mapnik/box2d_impl.hpp>
 
-// stl
-#include <stdexcept>
-#include <sstream>
-#include <iomanip>
-
-#include <mapnik/config.hpp>
-
-#pragma GCC diagnostic push
-#include <mapnik/warning_ignore.hpp>
-#include <boost/fusion/include/adapt_adt.hpp>
-#include <boost/spirit/include/qi.hpp>
-#include <boost/spirit/include/support_adapt_adt_attributes.hpp>
-#pragma GCC diagnostic pop
-
-// agg
-#include "agg_trans_affine.h"
-
-BOOST_FUSION_ADAPT_TPL_ADT(
-    (T),
-    (mapnik::box2d)(T),
-    (T, T, obj.minx(), obj.set_minx(mapnik::safe_cast<T>(val)))
-    (T, T, obj.miny(), obj.set_miny(mapnik::safe_cast<T>(val)))
-    (T, T, obj.maxx(), obj.set_maxx(mapnik::safe_cast<T>(val)))
-    (T, T, obj.maxy(), obj.set_maxy(mapnik::safe_cast<T>(val))))
-
-namespace mapnik
-{
-template <typename T>
-box2d<T>::box2d()
-    :minx_( std::numeric_limits<T>::max()),
-     miny_( std::numeric_limits<T>::max()),
-     maxx_(-std::numeric_limits<T>::max()),
-     maxy_(-std::numeric_limits<T>::max()) {}
-
-template <typename T>
-box2d<T>::box2d(T minx,T miny,T maxx,T maxy)
-{
-    init(minx,miny,maxx,maxy);
-}
-
-template <typename T>
-box2d<T>::box2d(coord<T,2> const& c0, coord<T,2> const& c1)
-{
-    init(c0.x,c0.y,c1.x,c1.y);
-}
-
-template <typename T>
-box2d<T>::box2d(box2d_type const& rhs)
-    : minx_(rhs.minx_),
-      miny_(rhs.miny_),
-      maxx_(rhs.maxx_),
-      maxy_(rhs.maxy_) {}
-
-template <typename T>
-box2d<T>::box2d(box2d_type && rhs)
-    : minx_(std::move(rhs.minx_)),
-      miny_(std::move(rhs.miny_)),
-      maxx_(std::move(rhs.maxx_)),
-      maxy_(std::move(rhs.maxy_)) {}
-
-template <typename T>
-box2d<T>& box2d<T>::operator=(box2d_type other)
-{
-    swap(*this, other);
-    return *this;
-}
-
-template <typename T>
-box2d<T>::box2d(box2d_type const& rhs, agg::trans_affine const& tr)
-{
-    double x0 = rhs.minx_, y0 = rhs.miny_;
-    double x1 = rhs.maxx_, y1 = rhs.miny_;
-    double x2 = rhs.maxx_, y2 = rhs.maxy_;
-    double x3 = rhs.minx_, y3 = rhs.maxy_;
-    tr.transform(&x0, &y0);
-    tr.transform(&x1, &y1);
-    tr.transform(&x2, &y2);
-    tr.transform(&x3, &y3);
-    init(static_cast<T>(x0), static_cast<T>(y0),
-         static_cast<T>(x2), static_cast<T>(y2));
-    expand_to_include(static_cast<T>(x1), static_cast<T>(y1));
-    expand_to_include(static_cast<T>(x3), static_cast<T>(y3));
-}
-
-template <typename T>
-bool box2d<T>::operator==(box2d<T> const& other) const
-{
-    return minx_==other.minx_ &&
-        miny_==other.miny_ &&
-        maxx_==other.maxx_ &&
-        maxy_==other.maxy_;
-}
-
-template <typename T>
-T box2d<T>::minx() const
-{
-    return minx_;
-}
-
-template <typename T>
-T box2d<T>::maxx() const
-{
-    return maxx_;
-}
-
-template <typename T>
-T box2d<T>::miny() const
-{
-    return miny_;
-}
-
-template <typename T>
-T box2d<T>::maxy() const
-{
-    return maxy_;
-}
-
-template<typename T>
-void box2d<T>::set_minx(T v)
-{
-    minx_ = v;
-}
-
-template<typename T>
-void box2d<T>::set_miny(T v)
-{
-    miny_ = v;
-}
-
-template<typename T>
-void box2d<T>::set_maxx(T v)
-{
-    maxx_ = v;
-}
-
-template<typename T>
-void box2d<T>::set_maxy(T v)
-{
-    maxy_ = v;
-}
-
-template <typename T>
-T box2d<T>::width() const
-{
-    return maxx_-minx_;
-}
-
-template <typename T>
-T box2d<T>::height() const
-{
-    return maxy_-miny_;
-}
-
-template <typename T>
-void box2d<T>::width(T w)
-{
-    T cx=center().x;
-    minx_=static_cast<T>(cx-w*0.5);
-    maxx_=static_cast<T>(cx+w*0.5);
-}
-
-template <typename T>
-void box2d<T>::height(T h)
-{
-    T cy=center().y;
-    miny_=static_cast<T>(cy-h*0.5);
-    maxy_=static_cast<T>(cy+h*0.5);
-}
-
-template <typename T>
-coord<T,2> box2d<T>::center() const
-{
-    return coord<T,2>(static_cast<T>(0.5*(minx_+maxx_)),
-                      static_cast<T>(0.5*(miny_+maxy_)));
-}
-
-template <typename T>
-void box2d<T>::expand_to_include(coord<T,2> const& c)
-{
-    expand_to_include(c.x,c.y);
-}
-
-template <typename T>
-void box2d<T>::expand_to_include(T x,T y)
-{
-    if (x<minx_) minx_=x;
-    if (x>maxx_) maxx_=x;
-    if (y<miny_) miny_=y;
-    if (y>maxy_) maxy_=y;
-}
-
-template <typename T>
-void box2d<T>::expand_to_include(box2d<T> const& other)
-{
-    if (other.minx_<minx_) minx_=other.minx_;
-    if (other.maxx_>maxx_) maxx_=other.maxx_;
-    if (other.miny_<miny_) miny_=other.miny_;
-    if (other.maxy_>maxy_) maxy_=other.maxy_;
-}
-
-template <typename T>
-bool box2d<T>::contains(coord<T,2> const& c) const
-{
-    return contains(c.x,c.y);
-}
-
-template <typename T>
-bool box2d<T>::contains(T x,T y) const
-{
-    return x>=minx_ && x<=maxx_ && y>=miny_ && y<=maxy_;
-}
-
-template <typename T>
-bool box2d<T>::contains(box2d<T> const& other) const
-{
-    return other.minx_>=minx_ &&
-        other.maxx_<=maxx_ &&
-        other.miny_>=miny_ &&
-        other.maxy_<=maxy_;
-}
-
-template <typename T>
-bool box2d<T>::intersects(coord<T,2> const& c) const
-{
-    return intersects(c.x,c.y);
-}
-
-template <typename T>
-bool box2d<T>::intersects(T x,T y) const
-{
-    return !(x>maxx_ || x<minx_ || y>maxy_ || y<miny_);
-}
-
-template <typename T>
-bool box2d<T>::intersects(box2d<T> const& other) const
-{
-    return !(other.minx_>maxx_ || other.maxx_<minx_ ||
-             other.miny_>maxy_ || other.maxy_<miny_);
-}
-
-template <typename T>
-box2d<T> box2d<T>::intersect(box2d_type const& other) const
-{
-    if (intersects(other))
-    {
-        T x0=std::max(minx_,other.minx_);
-        T y0=std::max(miny_,other.miny_);
-        T x1=std::min(maxx_,other.maxx_);
-        T y1=std::min(maxy_,other.maxy_);
-        return box2d<T>(x0,y0,x1,y1);
-    }
-    else
-    {
-        return box2d<T>();
-    }
-}
-
-template <typename T>
-void box2d<T>::re_center(T cx,T cy)
-{
-    T dx=cx-center().x;
-    T dy=cy-center().y;
-    minx_+=dx;
-    miny_+=dy;
-    maxx_+=dx;
-    maxy_+=dy;
-}
-
-template <typename T>
-void box2d<T>::re_center(coord<T,2> const& c)
-{
-    re_center(c.x,c.y);
-}
-
-template <typename T>
-void box2d<T>::init(T x0, T y0, T x1, T y1)
-{
-    if (x0 < x1)
-    {
-        minx_ = x0;
-        maxx_ = x1;
-    }
-    else
-    {
-        minx_ = x1;
-        maxx_ = x0;
-    }
-    if (y0 < y1)
-    {
-        miny_ = y0;
-        maxy_ = y1;
-    }
-    else
-    {
-        miny_ = y1;
-        maxy_ = y0;
-    }
-}
-
-template <typename T>
-void box2d<T>::init(T x, T y)
-{
-    init(x, y, x, y);
-}
-
-template <typename T>
-void box2d<T>::clip(box2d_type const& other)
-{
-    minx_ = std::max(minx_, other.minx());
-    miny_ = std::max(miny_, other.miny());
-    maxx_ = std::min(maxx_, other.maxx());
-    maxy_ = std::min(maxy_, other.maxy());
-}
-
-template <typename T>
-void box2d<T>::pad(T padding)
-{
-    minx_ -= padding;
-    miny_ -= padding;
-    maxx_ += padding;
-    maxy_ += padding;
-}
-
-template <typename T>
-bool box2d<T>::from_string(std::string const& str)
-{
-    boost::spirit::qi::lit_type lit;
-    boost::spirit::qi::double_type double_;
-    boost::spirit::ascii::space_type space;
-    bool r = boost::spirit::qi::phrase_parse(str.begin(),
-                                             str.end(),
-                                             double_ >> -lit(',') >> double_ >> -lit(',') >> double_ >> -lit(',') >> double_,
-                                             space,
-                                             *this);
-    return r;
-}
-
-template <typename T>
-bool box2d<T>::valid() const
-{
-    return (minx_ <= maxx_ && miny_ <= maxy_) ;
-}
-
-template <typename T>
-void box2d<T>::move(T x, T y)
-{
-    minx_ += x;
-    maxx_ += x;
-    miny_ += y;
-    maxy_ += y;
-}
-
-template <typename T>
-std::string box2d<T>::to_string() const
-{
-    std::ostringstream s;
-    if (valid())
-    {
-        s << "box2d(" << std::fixed << std::setprecision(16)
-          << minx_ << ',' << miny_ << ','
-          << maxx_ << ',' << maxy_ << ')';
-    }
-    else
-    {
-        s << "box2d(INVALID)";
-    }
-    return s.str();
-}
-
-
-template <typename T>
-box2d<T>&  box2d<T>::operator+=(box2d<T> const& other)
-{
-    expand_to_include(other);
-    return *this;
-}
-
-template <typename T>
-box2d<T> box2d<T>::operator+ (T other) const
-{
-    return box2d<T>(minx_ - other, miny_ - other, maxx_ + other, maxy_ + other);
-}
-
-template <typename T>
-box2d<T>& box2d<T>::operator+= (T other)
-{
-    minx_ -= other;
-    miny_ -= other;
-    maxx_ += other;
-    maxy_ += other;
-    return *this;
-}
-
-
-template <typename T>
-box2d<T>& box2d<T>::operator*=(T t)
-{
-    coord<T,2> c = center();
-    T sx = static_cast<T>(0.5 * width()  * t);
-    T sy = static_cast<T>(0.5 * height() * t);
-    minx_ = c.x - sx;
-    maxx_ = c.x + sx;
-    miny_ = c.y - sy;
-    maxy_ = c.y + sy;
-    return *this;
-}
-
-template <typename T>
-box2d<T>& box2d<T>::operator/=(T t)
-{
-    coord<T,2> c = center();
-    T sx = static_cast<T>(0.5 * width() / t);
-    T sy = static_cast<T>(0.5 * height() / t);
-    minx_ = c.x - sx;
-    maxx_ = c.x + sx;
-    miny_ = c.y - sy;
-    maxy_ = c.y + sy;
-    return *this;
-}
-
-template <typename T>
-T box2d<T>::operator[] (int index) const
-{
-    switch(index)
-    {
-    case 0:
-        return minx_;
-    case 1:
-        return miny_;
-    case 2:
-        return maxx_;
-    case 3:
-        return maxy_;
-    case -4:
-        return minx_;
-    case -3:
-        return miny_;
-    case -2:
-        return maxx_;
-    case -1:
-        return maxy_;
-    default:
-        throw std::out_of_range("index out of range, max value is 3, min value is -4 ");
-    }
-}
-
-template <typename T>
-box2d<T> box2d<T>::operator*(agg::trans_affine const& tr) const
-{
-    return box2d<T>(*this, tr);
-}
-
-template <typename T>
-box2d<T>& box2d<T>::operator*=(agg::trans_affine const& tr)
-{
-    double x0 = minx_, y0 = miny_;
-    double x1 = maxx_, y1 = miny_;
-    double x2 = maxx_, y2 = maxy_;
-    double x3 = minx_, y3 = maxy_;
-    tr.transform(&x0, &y0);
-    tr.transform(&x1, &y1);
-    tr.transform(&x2, &y2);
-    tr.transform(&x3, &y3);
-    init(static_cast<T>(x0), static_cast<T>(y0),
-         static_cast<T>(x2), static_cast<T>(y2));
-    expand_to_include(static_cast<T>(x1), static_cast<T>(y1));
-    expand_to_include(static_cast<T>(x3), static_cast<T>(y3));
-    return *this;
-}
+namespace mapnik {
 
 template class box2d<int>;
 template class box2d<float>;
diff --git a/src/cairo/process_line_pattern_symbolizer.cpp b/src/cairo/process_line_pattern_symbolizer.cpp
index 7aa85ab..f53d850 100644
--- a/src/cairo/process_line_pattern_symbolizer.cpp
+++ b/src/cairo/process_line_pattern_symbolizer.cpp
@@ -62,7 +62,7 @@ struct cairo_renderer_process_visitor_l
         mapnik::rasterizer ras;
         agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
         auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
-        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
+        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
         mapnik::box2d<double> const& bbox_image = marker.get_data()->bounding_box() * image_tr;
         mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height());
         render_pattern<image_rgba8>(ras, marker, image_tr, 1.0, image);
diff --git a/src/cairo/process_polygon_pattern_symbolizer.cpp b/src/cairo/process_polygon_pattern_symbolizer.cpp
index c2b211f..4e21234 100644
--- a/src/cairo/process_polygon_pattern_symbolizer.cpp
+++ b/src/cairo/process_polygon_pattern_symbolizer.cpp
@@ -96,7 +96,7 @@ void cairo_renderer<T>::process(polygon_pattern_symbolizer const& sym,
     value_double opacity = get<value_double, keys::opacity>(sym, feature, common_.vars_);
     agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
     auto image_transform = get_optional<transform_type>(sym, keys::image_transform);
-    if (image_transform) evaluate_transform(image_tr, feature, common_.vars_, *image_transform);
+    if (image_transform) evaluate_transform(image_tr, feature, common_.vars_, *image_transform, common_.scale_factor_);
 
     cairo_save_restore guard(context_);
     context_.set_operator(comp_op);
diff --git a/src/datasource_cache.cpp b/src/datasource_cache.cpp
index c561a7f..3759a6c 100644
--- a/src/datasource_cache.cpp
+++ b/src/datasource_cache.cpp
@@ -65,7 +65,7 @@ datasource_cache::~datasource_cache()
 datasource_ptr datasource_cache::create(parameters const& params)
 {
     boost::optional<std::string> type = params.get<std::string>("type");
-    if ( ! type)
+    if (!type)
     {
         throw config_error(std::string("Could not create datasource. Required ") +
                            "parameter 'type' is missing");
@@ -117,7 +117,7 @@ datasource_ptr datasource_cache::create(parameters const& params)
 #endif
         create_ds create_datasource = reinterpret_cast<create_ds>(itr->second->get_symbol("create"));
 
-    if (! create_datasource)
+    if (!create_datasource)
     {
         throw std::runtime_error(std::string("Cannot load symbols: ") +
                                  itr->second->get_error());
diff --git a/src/font_engine_freetype.cpp b/src/font_engine_freetype.cpp
index a14e6b6..9721d70 100644
--- a/src/font_engine_freetype.cpp
+++ b/src/font_engine_freetype.cpp
@@ -105,7 +105,7 @@ bool freetype_engine::register_font_impl(std::string const& file_name,
     streamRec.size = file.size();
     streamRec.descriptor.pointer = file.get();
     streamRec.read  = ft_read_cb;
-    streamRec.close = NULL;
+    streamRec.close = nullptr;
     args.flags = FT_OPEN_STREAM;
     args.stream = &streamRec;
     int num_faces = 0;
@@ -269,7 +269,7 @@ bool freetype_engine::can_open(std::string const& face_name,
     streamRec.size = file.size();
     streamRec.descriptor.pointer = file.get();
     streamRec.read  = ft_read_cb;
-    streamRec.close = NULL;
+    streamRec.close = nullptr;
     args.flags = FT_OPEN_STREAM;
     args.stream = &streamRec;
     // -1 is used to quickly check if the font file appears valid without iterating each face
diff --git a/src/geometry_envelope.cpp b/src/geometry_envelope.cpp
index 5b3ade6..bb79f59 100644
--- a/src/geometry_envelope.cpp
+++ b/src/geometry_envelope.cpp
@@ -23,30 +23,21 @@
 #include <mapnik/geometry_envelope.hpp>
 #include <mapnik/geometry_envelope_impl.hpp>
 #include <mapnik/text/symbolizer_helpers.hpp>
-namespace mapnik {
-namespace geometry {
+
+namespace mapnik { namespace geometry {
 
 template MAPNIK_DECL mapnik::box2d<double> envelope(geometry<double> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(mapnik::base_symbolizer_helper::geometry_cref const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(geometry_empty const& geom);
+// single
 template MAPNIK_DECL mapnik::box2d<double> envelope(point<double> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(line_string<double> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(linear_ring<double> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(polygon<double> const& geom);
+template MAPNIK_DECL mapnik::box2d<double> envelope(linear_ring<double> const& geom);
+// multi
 template MAPNIK_DECL mapnik::box2d<double> envelope(multi_point<double> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(multi_line_string<double> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(multi_polygon<double> const& geom);
+// collection
 template MAPNIK_DECL mapnik::box2d<double> envelope(geometry_collection<double> const& geom);
 
-template MAPNIK_DECL mapnik::box2d<double> envelope(geometry<std::int64_t> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(point<std::int64_t> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(line_string<std::int64_t> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(linear_ring<std::int64_t> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(polygon<std::int64_t> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(multi_point<std::int64_t> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(multi_line_string<std::int64_t> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(multi_polygon<std::int64_t> const& geom);
-template MAPNIK_DECL mapnik::box2d<double> envelope(geometry_collection<std::int64_t> const& geom);
-
 } // end ns geometry
 } // end ns mapnik
diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp
index 7fe0ff3..d1b6a17 100644
--- a/src/grid/process_text_symbolizer.cpp
+++ b/src/grid/process_text_symbolizer.cpp
@@ -60,7 +60,7 @@ void grid_renderer<T>::process(text_symbolizer const& sym,
     if (halo_transform)
     {
         agg::trans_affine halo_affine_transform;
-        evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform);
+        evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform, common_.scale_factor_);
         ren.set_halo_transform(halo_affine_transform);
     }
 
diff --git a/src/image_compositing.cpp b/src/image_compositing.cpp
index a8f9bba..d6c8a33 100644
--- a/src/image_compositing.cpp
+++ b/src/image_compositing.cpp
@@ -191,7 +191,20 @@ struct composite_visitor
           dy_(dy) {}
 
     template <typename T>
-    void operator() (T & dst) const;
+    void operator() (T & dst) const
+    {
+        throw std::runtime_error("Error: Composite with " + std::string(typeid(dst).name()) + " is not supported");
+    }
+
+    void operator()(image_rgba8 & dst) const
+    {
+        composite(dst, util::get<image_rgba8>(src_), mode_, opacity_, dx_, dy_);
+    }
+
+    void operator() (image_gray32f & dst) const
+    {
+        composite(dst, util::get<image_gray32f>(src_), mode_, opacity_, dx_, dy_);
+    }
 
   private:
     image_any const& src_;
@@ -199,25 +212,8 @@ struct composite_visitor
     float opacity_;
     int dx_;
     int dy_;
-};
-
-template <typename T>
-void composite_visitor::operator() (T & dst) const
-{
-    throw std::runtime_error("Error: Composite with " + std::string(typeid(dst).name()) + " is not supported");
-}
-
-template <>
-void composite_visitor::operator()<image_rgba8> (image_rgba8 & dst) const
-{
-    composite(dst, util::get<image_rgba8>(src_), mode_, opacity_, dx_, dy_);
-}
 
-template <>
-void composite_visitor::operator()<image_gray32f> (image_gray32f & dst) const
-{
-    composite(dst, util::get<image_gray32f>(src_), mode_, opacity_, dx_, dy_);
-}
+};
 
 } // end ns
 
diff --git a/src/jpeg_reader.cpp b/src/jpeg_reader.cpp
index 84c805e..6b6e1b1 100644
--- a/src/jpeg_reader.cpp
+++ b/src/jpeg_reader.cpp
@@ -22,6 +22,7 @@
 
 // mapnik
 #include <mapnik/image_reader.hpp>
+#include <mapnik/util/char_array_buffer.hpp>
 #include <mapnik/color.hpp>
 
 // jpeg
@@ -30,16 +31,10 @@ extern "C"
 #include <jpeglib.h>
 }
 
-#pragma GCC diagnostic push
-#include <mapnik/warning_ignore.hpp>
-#include <boost/iostreams/device/file.hpp>
-#include <boost/iostreams/device/array.hpp>
-#include <boost/iostreams/stream.hpp>
-#pragma GCC diagnostic pop
-
 // std
 #include <cstdio>
 #include <memory>
+#include <fstream>
 
 namespace mapnik
 {
@@ -49,7 +44,7 @@ class jpeg_reader : public image_reader
 {
 public:
     using source_type = T;
-    using input_stream = boost::iostreams::stream<source_type>;
+    using input_stream = std::iostream;
     const static unsigned BUF_SIZE = 4096;
 private:
     struct jpeg_stream_wrapper
@@ -77,7 +72,7 @@ private:
     unsigned width_;
     unsigned height_;
 public:
-    explicit jpeg_reader(std::string const& file_name);
+    explicit jpeg_reader(std::string const& filename);
     explicit jpeg_reader(char const* data, size_t size);
     ~jpeg_reader();
     unsigned width() const final;
@@ -99,14 +94,14 @@ private:
 
 namespace
 {
-image_reader* create_jpeg_reader(std::string const& file)
+image_reader* create_jpeg_reader(std::string const& filename)
 {
-    return new jpeg_reader<boost::iostreams::file_source>(file);
+    return new jpeg_reader<std::filebuf>(filename);
 }
 
 image_reader* create_jpeg_reader2(char const* data, size_t size)
 {
-    return new jpeg_reader<boost::iostreams::array_source>(data, size);
+    return new jpeg_reader<mapnik::util::char_array_buffer>(data, size);
 }
 
 const bool registered  = register_image_reader("jpeg",create_jpeg_reader);
@@ -115,20 +110,21 @@ const bool registered2 = register_image_reader("jpeg",create_jpeg_reader2);
 
 // ctors
 template <typename T>
-jpeg_reader<T>::jpeg_reader(std::string const& file_name)
-    : source_(file_name,std::ios_base::in | std::ios_base::binary),
-      stream_(source_),
+jpeg_reader<T>::jpeg_reader(std::string const& filename)
+    : source_(),
+      stream_(&source_),
       width_(0),
       height_(0)
 {
-    if (!stream_) throw image_reader_exception("cannot open image file "+ file_name);
+    source_.open(filename, std::ios_base::in | std::ios_base::binary);
+    if (!stream_) throw image_reader_exception("cannot open image file "+ filename);
     init();
 }
 
 template <typename T>
 jpeg_reader<T>::jpeg_reader(char const* data, size_t size)
     : source_(data, size),
-      stream_(source_),
+      stream_(&source_),
       width_(0),
       height_(0)
 {
diff --git a/src/load_map.cpp b/src/load_map.cpp
index f217082..fcb04c0 100644
--- a/src/load_map.cpp
+++ b/src/load_map.cpp
@@ -200,7 +200,7 @@ void map_parser::parse_map(Map & map, xml_node const& node, std::string const& b
             {
                 map.set_background(*bgcolor);
             }
-            
+
             optional<std::string> image_filename = map_node.get_opt_attr<std::string>("background-image");
             if (image_filename)
             {
@@ -891,7 +891,7 @@ void map_parser::parse_symbolizer_base(symbolizer_base &sym, xml_node const& nod
 {
     set_symbolizer_property<symbolizer_base,double>(sym, keys::simplify_tolerance, node);
     set_symbolizer_property<symbolizer_base,double>(sym, keys::smooth, node);
-    set_symbolizer_property<symbolizer_base,boolean_type>(sym, keys::clip, node);
+    set_symbolizer_property<symbolizer_base,value_bool>(sym, keys::clip, node);
     set_symbolizer_property<symbolizer_base,composite_mode_e>(sym, keys::comp_op, node);
     set_symbolizer_property<symbolizer_base,transform_type>(sym, keys::geometry_transform, node);
     set_symbolizer_property<symbolizer_base,simplify_algorithm_e>(sym, keys::simplify_algorithm, node);
@@ -907,8 +907,8 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & node)
         point_symbolizer sym;
         parse_symbolizer_base(sym, node);
         set_symbolizer_property<symbolizer_base,double>(sym, keys::opacity, node);
-        set_symbolizer_property<symbolizer_base,boolean_type>(sym, keys::allow_overlap, node);
-        set_symbolizer_property<symbolizer_base,boolean_type>(sym, keys::ignore_placement, node);
+        set_symbolizer_property<symbolizer_base,value_bool>(sym, keys::allow_overlap, node);
+        set_symbolizer_property<symbolizer_base,value_bool>(sym, keys::ignore_placement, node);
         set_symbolizer_property<symbolizer_base,point_placement_enum>(sym, keys::point_placement_type, node);
         set_symbolizer_property<symbolizer_base,transform_type>(sym, keys::image_transform, node);
         if (file && !file->empty())
@@ -1011,9 +1011,9 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node)
         set_symbolizer_property<symbolizer_base,double>(sym, keys::offset, node);
         set_symbolizer_property<symbolizer_base,double>(sym, keys::width, node);
         set_symbolizer_property<symbolizer_base,double>(sym, keys::height, node);
-        set_symbolizer_property<symbolizer_base,boolean_type>(sym, keys::allow_overlap, node);
-        set_symbolizer_property<symbolizer_base,boolean_type>(sym, keys::avoid_edges, node);
-        set_symbolizer_property<symbolizer_base,boolean_type>(sym, keys::ignore_placement, node);
+        set_symbolizer_property<symbolizer_base,value_bool>(sym, keys::allow_overlap, node);
+        set_symbolizer_property<symbolizer_base,value_bool>(sym, keys::avoid_edges, node);
+        set_symbolizer_property<symbolizer_base,value_bool>(sym, keys::ignore_placement, node);
         set_symbolizer_property<symbolizer_base,color>(sym, keys::fill, node);
         set_symbolizer_property<symbolizer_base,transform_type>(sym, keys::image_transform, node);
         set_symbolizer_property<symbolizer_base,marker_placement_enum>(sym, keys::markers_placement_type, node);
@@ -1172,7 +1172,7 @@ void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& node)
         set_symbolizer_property<symbolizer_base,double>(sym, keys::shield_dx, node);
         set_symbolizer_property<symbolizer_base,double>(sym, keys::shield_dy, node);
         set_symbolizer_property<symbolizer_base,double>(sym, keys::opacity, node);
-        set_symbolizer_property<symbolizer_base,mapnik::boolean_type>(sym, keys::unlock_image, node);
+        set_symbolizer_property<symbolizer_base,value_bool>(sym, keys::unlock_image, node);
 
         std::string file = node.get_attr<std::string>("file");
         if (file.empty())
@@ -1343,7 +1343,7 @@ void map_parser::parse_raster_symbolizer(rule & rule, xml_node const & node)
 
         // premultiplied status of image
         optional<mapnik::boolean_type> premultiplied = node.get_opt_attr<mapnik::boolean_type>("premultiplied");
-        if (premultiplied) put(raster_sym, keys::premultiplied, *premultiplied);
+        if (premultiplied) put(raster_sym, keys::premultiplied, bool(*premultiplied));
 
         bool found_colorizer = false;
         for ( auto const& css : node)
diff --git a/src/map.cpp b/src/map.cpp
index 3aff557..ca813e2 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -746,7 +746,7 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const
         else s << " (map has no layers)";
         throw std::out_of_range(s.str());
     }
-    return featureset_ptr();
+    return mapnik::make_empty_featureset();
 }
 
 featureset_ptr Map::query_map_point(unsigned index, double x, double y) const
diff --git a/src/marker_helpers.cpp b/src/marker_helpers.cpp
index dc9f679..eb02021 100644
--- a/src/marker_helpers.cpp
+++ b/src/marker_helpers.cpp
@@ -37,6 +37,7 @@ void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, a
 {
     double width = 0.0;
     double height = 0.0;
+    double half_stroke_width = 0.0;
     if (has_key(sym,keys::width) && has_key(sym,keys::height))
     {
         width = get<double>(sym, keys::width, feature, vars, 0.0);
@@ -50,6 +51,10 @@ void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, a
     {
         width = height = get<double>(sym, keys::height, feature, vars, 0.0);
     }
+    if (has_key(sym,keys::stroke_width))
+    {
+        half_stroke_width = get<double>(sym, keys::stroke_width, feature, vars, 0.0) / 2.0;
+    }
     svg::svg_converter_type styled_svg(svg_path, marker_ellipse.attributes());
     styled_svg.push_attr();
     styled_svg.begin_path();
@@ -59,6 +64,10 @@ void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, a
     styled_svg.pop_attr();
     double lox,loy,hix,hiy;
     styled_svg.bounding_rect(&lox, &loy, &hix, &hiy);
+    lox -= half_stroke_width;
+    loy -= half_stroke_width;
+    hix += half_stroke_width;
+    hiy += half_stroke_width;
     styled_svg.set_dimensions(width,height);
     marker_ellipse.set_dimensions(width,height);
     marker_ellipse.set_bounding_box(lox,loy,hix,hiy);
diff --git a/src/palette.cpp b/src/palette.cpp
index 4c3807d..1fc5b8b 100644
--- a/src/palette.cpp
+++ b/src/palette.cpp
@@ -65,16 +65,6 @@ rgba_palette::rgba_palette()
 #endif
 }
 
-const std::vector<rgb>& rgba_palette::palette() const
-{
-    return rgb_pal_;
-}
-
-const std::vector<unsigned>& rgba_palette::alphaTable() const
-{
-    return alpha_pal_;
-}
-
 bool rgba_palette::valid() const
 {
     return colors_ > 0;
diff --git a/src/plugin.cpp b/src/plugin.cpp
index af9bef3..b218630 100644
--- a/src/plugin.cpp
+++ b/src/plugin.cpp
@@ -58,16 +58,24 @@ PluginInfo::PluginInfo(std::string const& filename,
           if (module_) module_->dl = LoadLibraryA(filename.c_str());
           if (module_ && module_->dl)
           {
-                name_func name = reinterpret_cast<name_func>(dlsym(module_->dl, library_name.c_str()));
+                callable_returning_string name = reinterpret_cast<callable_returning_string>(dlsym(module_->dl, library_name.c_str()));
                 if (name) name_ = name();
+                callable_returning_void init_once = reinterpret_cast<callable_returning_void>(dlsym(module_->dl, "on_plugin_load"));
+                if (init_once) {
+                    init_once();
+                }
           }
 #else
   #ifdef MAPNIK_HAS_DLCFN
           if (module_) module_->dl = dlopen(filename.c_str(),RTLD_LAZY);
           if (module_ && module_->dl)
           {
-                name_func name = reinterpret_cast<name_func>(dlsym(module_->dl, library_name.c_str()));
+                callable_returning_string name = reinterpret_cast<callable_returning_string>(dlsym(module_->dl, library_name.c_str()));
                 if (name) name_ = name();
+                callable_returning_void init_once = reinterpret_cast<callable_returning_void>(dlsym(module_->dl, "on_plugin_load"));
+                if (init_once) {
+                    init_once();
+                }
           }
   #else
           throw std::runtime_error("no support for loading dynamic objects (Mapnik not compiled with -DMAPNIK_HAS_DLCFN)");
@@ -108,7 +116,7 @@ void * PluginInfo::get_symbol(std::string const& sym_name) const
 #ifdef MAPNIK_SUPPORTS_DLOPEN
     return static_cast<void *>(dlsym(module_->dl, sym_name.c_str()));
 #else
-    return NULL;
+    return nullptr;
 #endif
 }
 
diff --git a/src/png_reader.cpp b/src/png_reader.cpp
index 30b470f..8c58b22 100644
--- a/src/png_reader.cpp
+++ b/src/png_reader.cpp
@@ -23,22 +23,17 @@
 // mapnik
 #include <mapnik/debug.hpp>
 #include <mapnik/image_reader.hpp>
+#include <mapnik/util/char_array_buffer.hpp>
 
 extern "C"
 {
 #include <png.h>
 }
 
-#pragma GCC diagnostic push
-#include <mapnik/warning_ignore.hpp>
-#include <boost/iostreams/device/file.hpp>
-#include <boost/iostreams/device/array.hpp>
-#include <boost/iostreams/stream.hpp>
-#pragma GCC diagnostic pop
-
 // stl
 #include <cstring>
 #include <memory>
+#include <fstream>
 
 namespace mapnik
 {
@@ -47,7 +42,7 @@ template <typename T>
 class png_reader : public image_reader
 {
     using source_type = T;
-    using input_stream = boost::iostreams::stream<source_type>;
+    using input_stream = std::istream;
 
     struct png_struct_guard
     {
@@ -73,7 +68,7 @@ private:
     int color_type_;
     bool has_alpha_;
 public:
-    explicit png_reader(std::string const& file_name);
+    explicit png_reader(std::string const& filename);
     png_reader(char const* data, std::size_t size);
     ~png_reader();
     unsigned width() const final;
@@ -90,14 +85,14 @@ private:
 namespace
 {
 
-image_reader* create_png_reader(std::string const& file)
+image_reader* create_png_reader(std::string const& filename)
 {
-      return new png_reader<boost::iostreams::file_source>(file);
+    return new png_reader<std::filebuf>(filename);
 }
 
 image_reader* create_png_reader2(char const * data, std::size_t size)
 {
-    return new png_reader<boost::iostreams::array_source>(data, size);
+    return new png_reader<mapnik::util::char_array_buffer>(data, size);
 }
 
 const bool registered = register_image_reader("png",create_png_reader);
@@ -128,31 +123,30 @@ void png_reader<T>::png_read_data(png_structp png_ptr, png_bytep data, png_size_
 }
 
 template <typename T>
-png_reader<T>::png_reader(std::string const& file_name)
-    : source_(file_name,std::ios_base::in | std::ios_base::binary),
-      stream_(source_),
+png_reader<T>::png_reader(std::string const& filename)
+    : source_(),
+      stream_(&source_),
       width_(0),
       height_(0),
       bit_depth_(0),
       color_type_(0),
       has_alpha_(false)
 {
-    if (!source_.is_open()) throw image_reader_exception("PNG reader: cannot open file '"+ file_name + "'");
-    if (!stream_) throw image_reader_exception("PNG reader: cannot open file '"+ file_name + "'");
+    source_.open(filename, std::ios_base::in | std::ios_base::binary);
+    if (!source_.is_open()) throw image_reader_exception("PNG reader: cannot open file '"+ filename + "'");
     init();
 }
 
 template <typename T>
 png_reader<T>::png_reader(char const* data, std::size_t size)
     : source_(data,size),
-      stream_(source_),
+      stream_(&source_),
       width_(0),
       height_(0),
       bit_depth_(0),
       color_type_(0),
       has_alpha_(false)
 {
-
     if (!stream_) throw image_reader_exception("PNG reader: cannot open image stream");
     init();
 }
diff --git a/src/proj_transform.cpp b/src/proj_transform.cpp
index a08b4d7..9c23d80 100644
--- a/src/proj_transform.cpp
+++ b/src/proj_transform.cpp
@@ -26,6 +26,7 @@
 #include <mapnik/projection.hpp>
 #include <mapnik/proj_transform.hpp>
 #include <mapnik/coord.hpp>
+#include <mapnik/util/is_clockwise.hpp>
 
 #ifdef MAPNIK_USE_PROJ4
 // proj4
@@ -124,7 +125,7 @@ unsigned int proj_transform::forward (geometry::line_string<double> & ls) const
     geometry::point<double> * ptr = ls.data();
     double * x = reinterpret_cast<double*>(ptr);
     double * y = x + 1;
-    double * z = NULL;
+    double * z = nullptr;
     if(!forward(x, y, z, size, 2))
     {
         return size;
@@ -199,20 +200,21 @@ bool proj_transform::backward (double * x, double * y , double * z, int point_co
 #ifdef MAPNIK_USE_PROJ4
     if (is_dest_longlat_)
     {
-        int i;
-        for(i=0; i<point_count; i++) {
-            x[i*offset] *= DEG_TO_RAD;
-            y[i*offset] *= DEG_TO_RAD;
+        for (int i = 0; i < point_count; ++i)
+        {
+            x[i * offset] *= DEG_TO_RAD;
+            y[i * offset] *= DEG_TO_RAD;
         }
     }
 
-    if (pj_transform( dest_.proj_, source_.proj_, point_count,
-                      offset, x,y,z) != 0)
+    if (pj_transform(dest_.proj_, source_.proj_, point_count,
+                     offset, x, y, z) != 0)
     {
         return false;
     }
 
-    for(int j=0; j<point_count; j++) {
+    for (int j = 0; j < point_count; ++j)
+    {
         if (x[j] == HUGE_VAL || y[j] == HUGE_VAL)
         {
             return false;
@@ -221,10 +223,10 @@ bool proj_transform::backward (double * x, double * y , double * z, int point_co
 
     if (is_source_longlat_)
     {
-        int i;
-        for(i=0; i<point_count; i++) {
-            x[i*offset] *= RAD_TO_DEG;
-            y[i*offset] *= RAD_TO_DEG;
+        for (int i = 0; i < point_count; ++i)
+        {
+            x[i * offset] *= RAD_TO_DEG;
+            y[i * offset] *= RAD_TO_DEG;
         }
     }
 #endif
@@ -264,8 +266,8 @@ unsigned int proj_transform::backward (geometry::line_string<double> & ls) const
     geometry::point<double> * ptr = ls.data();
     double * x = reinterpret_cast<double*>(ptr);
     double * y = x + 1;
-    double * z = NULL;
-    if(!backward(x, y, z, size, 2))
+    double * z = nullptr;
+    if (!backward(x, y, z, size, 2))
     {
         return size;
     }
@@ -311,31 +313,24 @@ bool proj_transform::backward (box2d<double> & box) const
     if (is_source_equal_dest_)
         return true;
 
-    double llx = box.minx();
-    double ulx = box.minx();
-    double lly = box.miny();
-    double lry = box.miny();
-    double lrx = box.maxx();
-    double urx = box.maxx();
-    double uly = box.maxy();
-    double ury = box.maxy();
-    double z = 0.0;
-    if (!backward(llx,lly,z))
-        return false;
-    if (!backward(lrx,lry,z))
-        return false;
-    if (!backward(ulx,uly,z))
-        return false;
-    if (!backward(urx,ury,z))
+    double x[4], y[4];
+    x[0] = box.minx(); // llx 0
+    y[0] = box.miny(); // lly 1
+    x[1] = box.maxx(); // lrx 2
+    y[1] = box.miny(); // lry 3
+    x[2] = box.minx(); // ulx 4
+    y[2] = box.maxy(); // uly 5
+    x[3] = box.maxx(); // urx 6
+    y[3] = box.maxy(); // ury 7
+
+    if (!backward(x, y, nullptr, 4, 1))
         return false;
-    double minx = std::min(ulx, llx);
-    double miny = std::min(lly, lry);
-    double maxx = std::max(urx, lrx);
-    double maxy = std::max(ury, uly);
-    box.init(minx,
-             miny,
-             maxx,
-             maxy);
+
+    double minx = std::min(x[0], x[2]);
+    double miny = std::min(y[0], y[1]);
+    double maxx = std::max(x[1], x[3]);
+    double maxy = std::max(y[2], y[3]);
+    box.init(minx, miny, maxx, maxy);
     return true;
 }
 
@@ -370,21 +365,6 @@ void envelope_points(std::vector< coord<double,2> > & coords, box2d<double>& env
     }
 }
 
-// determine if an ordered sequence of coordinates is in clockwise order
-bool is_clockwise(const std::vector< coord<double,2> > & coords)
-{
-    int n = coords.size();
-    coord<double,2> c1, c2;
-    double a = 0.0;
-
-    for (int i=0; i<n; i++) {
-        c1 = coords[i];
-        c2 = coords[(i + 1) % n];
-        a += (c1.x * c2.y - c2.x * c1.y);
-    }
-    return a <= 0.0;
-}
-
 box2d<double> calculate_bbox(std::vector<coord<double,2> > & points) {
     std::vector<coord<double,2> >::iterator it = points.begin();
     std::vector<coord<double,2> >::iterator it_end = points.end();
@@ -425,7 +405,7 @@ bool proj_transform::backward(box2d<double>& env, int points) const
     }
 
     box2d<double> result = calculate_bbox(coords);
-    if (is_source_longlat_ && !is_clockwise(coords))
+    if (is_source_longlat_ && !util::is_clockwise(coords))
     {
         // we've gone to a geographic CS, and our clockwise envelope has
         // changed into an anticlockwise one. This means we've crossed the antimeridian, and
@@ -439,7 +419,6 @@ bool proj_transform::backward(box2d<double>& env, int points) const
     env.re_center(result.center().x, result.center().y);
     env.height(result.height());
     env.width(result.width());
-
     return true;
 }
 
@@ -466,7 +445,7 @@ bool proj_transform::forward(box2d<double>& env, int points) const
 
     box2d<double> result = calculate_bbox(coords);
 
-    if (is_dest_longlat_ && !is_clockwise(coords))
+    if (is_dest_longlat_ && !util::is_clockwise(coords))
     {
         // we've gone to a geographic CS, and our clockwise envelope has
         // changed into an anticlockwise one. This means we've crossed the antimeridian, and
diff --git a/src/renderer_common/render_markers_symbolizer.cpp b/src/renderer_common/render_markers_symbolizer.cpp
index 2ecc51b..6706cb1 100644
--- a/src/renderer_common/render_markers_symbolizer.cpp
+++ b/src/renderer_common/render_markers_symbolizer.cpp
@@ -159,7 +159,7 @@ struct render_marker_symbolizer_visitor
 
         if (auto image_transform = get_optional<transform_type>(sym_, keys::image_transform))
         {
-            evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
+            evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
         }
 
         vector_dispatch_type rasterizer_dispatch(marker_ptr,
@@ -183,7 +183,7 @@ struct render_marker_symbolizer_visitor
 
         setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_);
         auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
-        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
+        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
         box2d<double> const& bbox = mark.bounding_box();
         mapnik::image_rgba8 const& marker = mark.get_data();
         // - clamp sizes to > 4 pixels of interactivity
diff --git a/src/text/formatting/layout.cpp b/src/text/formatting/layout.cpp
index ef908c5..9a5ca10 100644
--- a/src/text/formatting/layout.cpp
+++ b/src/text/formatting/layout.cpp
@@ -75,9 +75,9 @@ node_ptr layout_node::from_xml(xml_node const& xml, fontset_map const& fontsets)
     if (xml.has_attribute("text-ratio")) set_property_from_xml<double>(n->text_ratio, "text-ratio", xml);
     if (xml.has_attribute("wrap-width")) set_property_from_xml<double>(n->wrap_width, "wrap-width", xml);
     if (xml.has_attribute("wrap-character")) set_property_from_xml<std::string>(n->wrap_char, "wrap-character", xml);
-    if (xml.has_attribute("wrap-before")) set_property_from_xml<mapnik::boolean_type>(n->wrap_before, "wrap-before", xml);
-    if (xml.has_attribute("repeat-wrap-character")) set_property_from_xml<mapnik::boolean_type>(n->repeat_wrap_char, "repeat-wrap-character", xml);
-    if (xml.has_attribute("rotate-displacement")) set_property_from_xml<mapnik::boolean_type>(n->rotate_displacement, "rotate-displacement", xml);
+    if (xml.has_attribute("wrap-before")) set_property_from_xml<mapnik::value_bool>(n->wrap_before, "wrap-before", xml);
+    if (xml.has_attribute("repeat-wrap-character")) set_property_from_xml<mapnik::value_bool>(n->repeat_wrap_char, "repeat-wrap-character", xml);
+    if (xml.has_attribute("rotate-displacement")) set_property_from_xml<mapnik::value_bool>(n->rotate_displacement, "rotate-displacement", xml);
     if (xml.has_attribute("orientation")) set_property_from_xml<double>(n->orientation, "orientation", xml);
     if (xml.has_attribute("horizontal-alignment")) set_property_from_xml<horizontal_alignment_e>(n->halign, "horizontal-alignment", xml);
     if (xml.has_attribute("vertical-alignment")) set_property_from_xml<vertical_alignment_e>(n->valign, "vertical-alignment", xml);
diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp
index 04a2907..e4adddf 100644
--- a/src/text/placement_finder.cpp
+++ b/src/text/placement_finder.cpp
@@ -39,6 +39,41 @@
 namespace mapnik
 {
 
+namespace {
+box2d<double> get_bbox(text_layout const& layout, glyph_info const& glyph, pixel_position const& pos, rotation const& rot)
+{
+    /*
+
+      (0/ymax)           (width/ymax)
+      ***************
+      *             *
+      (0/0)*             *
+      *             *
+      ***************
+      (0/ymin)          (width/ymin)
+      Add glyph offset in y direction, but not in x direction (as we use the full cluster width anyways)!
+    */
+    double width = layout.cluster_width(glyph.char_index);
+    if (glyph.advance() <= 0) width = -width;
+    pixel_position tmp, tmp2;
+    tmp.set(0, glyph.ymax());
+    tmp = tmp.rotate(rot);
+    tmp2.set(width, glyph.ymax());
+    tmp2 = tmp2.rotate(rot);
+    box2d<double> bbox(tmp.x,  -tmp.y,
+                       tmp2.x, -tmp2.y);
+    tmp.set(width, glyph.ymin());
+    tmp = tmp.rotate(rot);
+    bbox.expand_to_include(tmp.x, -tmp.y);
+    tmp.set(0, glyph.ymin());
+    tmp = tmp.rotate(rot);
+    bbox.expand_to_include(tmp.x, -tmp.y);
+    pixel_position pos2 = pos + pixel_position(0, glyph.offset.y).rotate(rot);
+    bbox.move(pos2.x , -pos2.y);
+    return bbox;
+}
+} // anonymous namespace
+
 placement_finder::placement_finder(feature_impl const& feature,
                                    attributes const& attr,
                                    DetectorType &detector,
@@ -432,37 +467,4 @@ bool placement_finder::add_marker(glyph_positions_ptr & glyphs, pixel_position c
     return true;
 }
 
-box2d<double> placement_finder::get_bbox(text_layout const& layout, glyph_info const& glyph, pixel_position const& pos, rotation const& rot)
-{
-    /*
-
-      (0/ymax)           (width/ymax)
-      ***************
-      *             *
-      (0/0)*             *
-      *             *
-      ***************
-      (0/ymin)          (width/ymin)
-      Add glyph offset in y direction, but not in x direction (as we use the full cluster width anyways)!
-    */
-    double width = layout.cluster_width(glyph.char_index);
-    if (glyph.advance() <= 0) width = -width;
-    pixel_position tmp, tmp2;
-    tmp.set(0, glyph.ymax());
-    tmp = tmp.rotate(rot);
-    tmp2.set(width, glyph.ymax());
-    tmp2 = tmp2.rotate(rot);
-    box2d<double> bbox(tmp.x,  -tmp.y,
-                       tmp2.x, -tmp2.y);
-    tmp.set(width, glyph.ymin());
-    tmp = tmp.rotate(rot);
-    bbox.expand_to_include(tmp.x, -tmp.y);
-    tmp.set(0, glyph.ymin());
-    tmp = tmp.rotate(rot);
-    bbox.expand_to_include(tmp.x, -tmp.y);
-    pixel_position pos2 = pos + pixel_position(0, glyph.offset.y).rotate(rot);
-    bbox.move(pos2.x , -pos2.y);
-    return bbox;
-}
-
 }// ns mapnik
diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp
index 1d0fe48..fe5568a 100644
--- a/src/text/symbolizer_helpers.cpp
+++ b/src/text/symbolizer_helpers.cpp
@@ -41,8 +41,39 @@
 #include <mapnik/text/placements/base.hpp>
 #include <mapnik/text/placements/dummy.hpp>
 
+namespace mapnik {
+namespace geometry {
 
-namespace mapnik { namespace detail {
+struct envelope_impl
+{
+    template <typename T>
+    box2d<double> operator() (T const& ref) const
+    {
+        return envelope<T>(ref);
+    }
+};
+
+mapnik::box2d<double> envelope(mapnik::base_symbolizer_helper::geometry_cref const& geom)
+{
+    return mapnik::util::apply_visitor(envelope_impl(), geom);
+}
+
+struct geometry_type_impl
+{
+    template <typename T>
+    auto operator() (T const& ref) const -> decltype(geometry_type<T>(ref))
+    {
+        return geometry_type<T>(ref);
+    }
+};
+
+mapnik::geometry::geometry_types geometry_type(mapnik::base_symbolizer_helper::geometry_cref const& geom)
+{
+    return mapnik::util::apply_visitor(geometry_type_impl(), geom);
+}
+
+} // geometry
+namespace detail {
 
 template <typename Points>
 struct apply_vertex_placement
@@ -389,7 +420,7 @@ bool text_symbolizer_helper::next_point_placement() const
             return true;
         }
         //No placement for this point. Keep it in points_ for next try.
-        point_itr_++;
+        ++point_itr_;
     }
     return false;
 }
@@ -434,7 +465,7 @@ void text_symbolizer_helper::init_marker() const
     if (marker->is<marker_null>()) return;
     agg::trans_affine trans;
     auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
-    if (image_transform) evaluate_transform(trans, feature_, vars_, *image_transform);
+    if (image_transform) evaluate_transform(trans, feature_, vars_, *image_transform, scale_factor_);
     double width = marker->width();
     double height = marker->height();
     double px0 = - 0.5 * width;
@@ -460,7 +491,6 @@ void text_symbolizer_helper::init_marker() const
     finder_.set_marker(std::make_shared<marker_info>(marker, trans), bbox, unlock_image, marker_displacement);
 }
 
-
 template text_symbolizer_helper::text_symbolizer_helper(
     text_symbolizer const& sym,
     feature_impl const& feature,
diff --git a/src/text/text_line.cpp b/src/text/text_line.cpp
index f228e5b..2d05167 100644
--- a/src/text/text_line.cpp
+++ b/src/text/text_line.cpp
@@ -51,7 +51,7 @@ text_line::text_line(text_line && rhs)
 
 void text_line::add_glyph(glyph_info && glyph, double scale_factor_)
 {
-    line_height_ = std::max(line_height_, glyph.line_height() + glyph.format->line_spacing);
+    line_height_ = std::max(line_height_, glyph.line_height() + glyph.format->line_spacing * scale_factor_);
     double advance = glyph.advance();
     if (glyphs_.empty())
     {
diff --git a/src/text/text_properties.cpp b/src/text/text_properties.cpp
index ef5b9af..73210d1 100644
--- a/src/text/text_properties.cpp
+++ b/src/text/text_properties.cpp
@@ -103,9 +103,9 @@ void text_symbolizer_properties::text_properties_from_xml(xml_node const& node)
     set_property_from_xml<value_double>(expressions.label_position_tolerance, "label-position-tolerance", node);
     set_property_from_xml<value_double>(expressions.minimum_padding, "minimum-padding", node);
     set_property_from_xml<value_double>(expressions.minimum_path_length, "minimum-path-length", node);
-    set_property_from_xml<boolean_type>(expressions.avoid_edges, "avoid-edges", node);
-    set_property_from_xml<boolean_type>(expressions.allow_overlap, "allow-overlap", node);
-    set_property_from_xml<boolean_type>(expressions.largest_bbox_only, "largest-bbox-only", node);
+    set_property_from_xml<value_bool>(expressions.avoid_edges, "avoid-edges", node);
+    set_property_from_xml<value_bool>(expressions.allow_overlap, "allow-overlap", node);
+    set_property_from_xml<value_bool>(expressions.largest_bbox_only, "largest-bbox-only", node);
     set_property_from_xml<value_double>(expressions.max_char_angle_delta, "max-char-angle-delta", node);
     set_property_from_xml<text_upright_e>(expressions.upright, "upright", node);
 }
@@ -215,9 +215,9 @@ void text_layout_properties::from_xml(xml_node const &node, fontset_map const& f
     set_property_from_xml<double>(text_ratio, "text-ratio", node);
     set_property_from_xml<double>(wrap_width, "wrap-width", node);
     set_property_from_xml<std::string>(wrap_char, "wrap-character", node);
-    set_property_from_xml<boolean_type>(wrap_before, "wrap-before", node);
-    set_property_from_xml<boolean_type>(repeat_wrap_char, "repeat-wrap-character", node);
-    set_property_from_xml<boolean_type>(rotate_displacement, "rotate-displacement", node);
+    set_property_from_xml<value_bool>(wrap_before, "wrap-before", node);
+    set_property_from_xml<value_bool>(repeat_wrap_char, "repeat-wrap-character", node);
+    set_property_from_xml<value_bool>(rotate_displacement, "rotate-displacement", node);
     set_property_from_xml<double>(orientation, "orientation", node);
     set_property_from_xml<vertical_alignment_e>(valign, "vertical-alignment", node);
     set_property_from_xml<horizontal_alignment_e>(halign, "horizontal-alignment", node);
diff --git a/src/tiff_reader.cpp b/src/tiff_reader.cpp
index 1f88540..157e4d0 100644
--- a/src/tiff_reader.cpp
+++ b/src/tiff_reader.cpp
@@ -23,21 +23,15 @@
 // mapnik
 #include <mapnik/debug.hpp>
 #include <mapnik/image_reader.hpp>
-
+#include <mapnik/util/char_array_buffer.hpp>
 extern "C"
 {
 #include <tiffio.h>
 }
 
-#pragma GCC diagnostic push
-#include <mapnik/warning_ignore.hpp>
-#include <boost/iostreams/device/file.hpp>
-#include <boost/iostreams/device/array.hpp>
-#include <boost/iostreams/stream.hpp>
-#pragma GCC diagnostic pop
-
 // stl
 #include <memory>
+#include <fstream>
 
 namespace mapnik { namespace impl {
 
@@ -106,7 +100,7 @@ class tiff_reader : public image_reader
 {
     using tiff_ptr = std::shared_ptr<TIFF>;
     using source_type = T;
-    using input_stream = boost::iostreams::stream<source_type>;
+    using input_stream = std::istream;
 
     struct tiff_closer
     {
@@ -145,7 +139,7 @@ public:
         stripped,
         tiled
     };
-    explicit tiff_reader(std::string const& file_name);
+    explicit tiff_reader(std::string const& filename);
     tiff_reader(char const* data, std::size_t size);
     virtual ~tiff_reader();
     unsigned width() const final;
@@ -183,14 +177,14 @@ private:
 namespace
 {
 
-image_reader* create_tiff_reader(std::string const& file)
+image_reader* create_tiff_reader(std::string const& filename)
 {
-    return new tiff_reader<boost::iostreams::file_source>(file);
+    return new tiff_reader<std::filebuf>(filename);
 }
 
 image_reader* create_tiff_reader2(char const * data, std::size_t size)
 {
-    return new tiff_reader<boost::iostreams::array_source>(data, size);
+    return new tiff_reader<mapnik::util::char_array_buffer>(data, size);
 }
 
 const bool registered = register_image_reader("tiff",create_tiff_reader);
@@ -199,9 +193,9 @@ const bool registered2 = register_image_reader("tiff", create_tiff_reader2);
 }
 
 template <typename T>
-tiff_reader<T>::tiff_reader(std::string const& file_name)
-    : source_(file_name, std::ios_base::in | std::ios_base::binary),
-      stream_(source_),
+tiff_reader<T>::tiff_reader(std::string const& filename)
+    : source_(),
+      stream_(&source_),
       tif_(nullptr),
       read_method_(generic),
       rows_per_strip_(0),
@@ -218,14 +212,15 @@ tiff_reader<T>::tiff_reader(std::string const& file_name)
       has_alpha_(false),
       is_tiled_(false)
 {
-    if (!stream_) throw image_reader_exception("TIFF reader: cannot open file "+ file_name);
+    source_.open(filename, std::ios_base::in | std::ios_base::binary);
+    if (!stream_) throw image_reader_exception("TIFF reader: cannot open file "+ filename);
     init();
 }
 
 template <typename T>
 tiff_reader<T>::tiff_reader(char const* data, std::size_t size)
     : source_(data, size),
-      stream_(source_),
+      stream_(&source_),
       tif_(nullptr),
       read_method_(generic),
       rows_per_strip_(0),
@@ -243,8 +238,6 @@ tiff_reader<T>::tiff_reader(char const* data, std::size_t size)
       is_tiled_(false)
 {
     if (!stream_) throw image_reader_exception("TIFF reader: cannot open image stream ");
-    stream_.rdbuf()->pubsetbuf(0, 0);
-    stream_.seekg(0, std::ios::beg);
     init();
 }
 
diff --git a/src/value.cpp b/src/value.cpp
index a3c8770..b5ad606 100644
--- a/src/value.cpp
+++ b/src/value.cpp
@@ -673,7 +673,7 @@ struct convert<std::string>
 
     std::string operator()(value_null const&) const
     {
-        return std::string();
+        return std::string("");
     }
 };
 
@@ -708,7 +708,7 @@ struct to_unicode_impl
 
     value_unicode_string operator()(value_null const&) const
     {
-        return value_unicode_string();
+        return value_unicode_string("");
     }
 };
 
diff --git a/src/vertex_adapters.cpp b/src/vertex_adapters.cpp
index 06be6c2..e96a9e2 100644
--- a/src/vertex_adapters.cpp
+++ b/src/vertex_adapters.cpp
@@ -34,7 +34,7 @@ point_vertex_adapter<T>::point_vertex_adapter(point<T> const& pt)
       first_(true) {}
 
 template <typename T>
-unsigned point_vertex_adapter<T>::vertex(value_type * x, value_type * y) const
+unsigned point_vertex_adapter<T>::vertex(coord_type * x, coord_type * y) const
 {
     if (first_)
     {
@@ -67,7 +67,7 @@ line_string_vertex_adapter<T>::line_string_vertex_adapter(line_string<T> const&
 {}
 
 template <typename T>
-unsigned line_string_vertex_adapter<T>::vertex(value_type * x, value_type * y) const
+unsigned line_string_vertex_adapter<T>::vertex(coord_type * x, coord_type * y) const
 {
     if (current_index_ != end_index_)
     {
@@ -117,7 +117,7 @@ void polygon_vertex_adapter<T>::rewind(unsigned) const
     start_loop_ = true;
 }
 template <typename T>
-unsigned polygon_vertex_adapter<T>::vertex(value_type * x, value_type * y) const
+unsigned polygon_vertex_adapter<T>::vertex(coord_type * x, coord_type * y) const
 {
     if (rings_itr_ == rings_end_)
     {
@@ -177,7 +177,7 @@ void ring_vertex_adapter<T>::rewind(unsigned) const
 }
 
 template <typename T>
-unsigned ring_vertex_adapter<T>::vertex(value_type * x, value_type * y) const
+unsigned ring_vertex_adapter<T>::vertex(coord_type * x, coord_type * y) const
 {
     if (current_index_ < end_index_)
     {
diff --git a/src/webp_reader.cpp b/src/webp_reader.cpp
index c806df9..ca611ae 100644
--- a/src/webp_reader.cpp
+++ b/src/webp_reader.cpp
@@ -33,9 +33,6 @@ extern "C"
 #include <webp/decode.h>
 }
 
-#include <boost/iostreams/device/file.hpp>
-#include <boost/iostreams/device/array.hpp>
-#include <boost/iostreams/stream.hpp>
 #pragma GCC diagnostic pop
 
 // stl
diff --git a/src/xml_tree.cpp b/src/xml_tree.cpp
index e342e68..2e2a7df 100644
--- a/src/xml_tree.cpp
+++ b/src/xml_tree.cpp
@@ -69,11 +69,10 @@ DEFINE_NAME_TRAIT( double, "double")
 DEFINE_NAME_TRAIT( float, "float")
 DEFINE_NAME_TRAIT( unsigned, "unsigned")
 DEFINE_NAME_TRAIT( int, "int")
+DEFINE_NAME_TRAIT( bool, "bool")
 DEFINE_NAME_TRAIT( boolean_type, "boolean_type")
 #ifdef BIGINT
 DEFINE_NAME_TRAIT( mapnik::value_integer, "long long" )
-#else
-DEFINE_NAME_TRAIT( mapnik::value_integer, "int" )
 #endif
 DEFINE_NAME_TRAIT( std::string, "string" )
 DEFINE_NAME_TRAIT( color, "color" )
@@ -413,10 +412,13 @@ std::string xml_node::line_to_string() const
 #define compile_get_value(T) template T xml_node::get_value<T>() const
 
 compile_get_opt_attr(boolean_type);
+compile_get_opt_attr(mapnik::value_bool);
 compile_get_opt_attr(std::string);
 compile_get_opt_attr(int);
 compile_get_opt_attr(unsigned);
+#ifdef BIGINT
 compile_get_opt_attr(mapnik::value_integer);
+#endif
 compile_get_opt_attr(float);
 compile_get_opt_attr(double);
 compile_get_opt_attr(color);
diff --git a/test/run b/test/run
index f623609..014b798 100755
--- a/test/run
+++ b/test/run
@@ -1,5 +1,8 @@
 #!/usr/bin/env bash
 
+set -o pipefail
+set -eu
+
 failures=0
 
 cd "$( dirname "${BASH_SOURCE[0]}" )"
@@ -18,9 +21,8 @@ if [ -d "test/data" ]; then
 
     run_substep "Running C++ Unit tests..."
     if [[ -f ./test/unit/run ]]; then
-        ./test/unit/run
-        failures=$((failures+$?))
         ran_a_test=true
+        ./test/unit/run || failures=$((failures+$?))
     else
         run_warn "Skipping unit tests since they were not built"
     fi
@@ -31,8 +33,7 @@ if [ -d "test/data" ]; then
         for FILE in test/standalone/*-bin; do
           found_test=true
           ran_a_test=true
-          ${FILE};
-          failures=$((failures+$?))
+          ${FILE} || failures=$((failures+$?))
         done
     fi
     if [[ $found_test == false ]]; then
@@ -41,13 +42,12 @@ if [ -d "test/data" ]; then
 
     if [ -d "test/data-visual/styles" ]; then
         run_substep "Running visual tests..."
-        if [ -z "$JOBS" ]; then
+        if [ -z "${JOBS:-}" ]; then
             JOBS=1
         fi
         if [[ -f ./test/visual/run ]]; then
-            ./test/visual/run -j $JOBS
             ran_a_test=true
-            failures=$((failures+$?))
+            ./test/visual/run -j $JOBS || failures=$((failures+$?))
         else
             run_warn "Skipping visual tests since they were not built"
         fi
diff --git a/test/standalone/font_registration_test.cpp b/test/standalone/font_registration_test.cpp
index fda605f..db56a30 100644
--- a/test/standalone/font_registration_test.cpp
+++ b/test/standalone/font_registration_test.cpp
@@ -46,16 +46,16 @@ SECTION("registration") {
         REQUIRE( m.load_fonts() );
         REQUIRE( m.get_font_memory_cache().size() == 1 );
         REQUIRE( m.register_fonts(fontdir , true ) );
-        REQUIRE( m.get_font_file_mapping().size() == 22 );
+        REQUIRE( m.get_font_file_mapping().size() == 23 );
         REQUIRE( m.load_fonts() );
-        REQUIRE( m.get_font_memory_cache().size() == 22 );
+        REQUIRE( m.get_font_memory_cache().size() == 23 );
 
         // copy discards memory cache but not file mapping
         mapnik::Map m2(m);
         REQUIRE( m2.get_font_memory_cache().size() == 0 );
-        REQUIRE( m2.get_font_file_mapping().size() == 22 );
+        REQUIRE( m2.get_font_file_mapping().size() == 23 );
         REQUIRE( m2.load_fonts() );
-        REQUIRE( m2.get_font_memory_cache().size() == 22 );
+        REQUIRE( m2.get_font_memory_cache().size() == 23 );
 
         // test font-directory from XML
         mapnik::Map m3(1,1);
@@ -132,7 +132,7 @@ SECTION("registration") {
         // recurse to find all dejavu fonts
         REQUIRE( mapnik::freetype_engine::register_fonts(fontdir, true) );
         face_names = mapnik::freetype_engine::face_names();
-        REQUIRE( face_names.size() == 22 );
+        REQUIRE( face_names.size() == 23 );
 
         // we should have re-registered 'DejaVu Sans Mono Bold Oblique' again,
         // but now at a new path
@@ -154,7 +154,7 @@ SECTION("registration") {
         mapnik::Map m4(1,1);
         REQUIRE( m4.register_fonts(fontdir , true ) );
         REQUIRE( m4.get_font_memory_cache().size() == 0 );
-        REQUIRE( m4.get_font_file_mapping().size() == 22 );
+        REQUIRE( m4.get_font_file_mapping().size() == 23 );
         REQUIRE( !m4.load_fonts() );
         REQUIRE( m4.get_font_memory_cache().size() == 0 );
         REQUIRE( m4.register_fonts(dejavu_bold_oblique, false) );
@@ -166,7 +166,7 @@ SECTION("registration") {
         // https://github.com/mapnik/mapnik/issues/2274
         REQUIRE( mapnik::freetype_engine::register_font("test/data/fonts/NotoSans-Regular.ttc") );
         face_names = mapnik::freetype_engine::face_names();
-        REQUIRE( face_names.size() == 24 );
+        REQUIRE( face_names.size() == 25 );
 
         // now blindly register as many system fonts as possible
         // the goal here to make sure we don't crash
@@ -179,7 +179,7 @@ SECTION("registration") {
         // windows
         mapnik::freetype_engine::register_fonts("C:\\Windows\\Fonts", true);
         face_names = mapnik::freetype_engine::face_names();
-        REQUIRE( face_names.size() > 22 );
+        REQUIRE( face_names.size() > 23 );
     }
     catch (std::exception const & ex)
     {
@@ -188,4 +188,4 @@ SECTION("registration") {
     }
 
 }
-}
\ No newline at end of file
+}
diff --git a/test/unit/core/expressions_test.cpp b/test/unit/core/expressions_test.cpp
index 64cdd9e..e60d612 100644
--- a/test/unit/core/expressions_test.cpp
+++ b/test/unit/core/expressions_test.cpp
@@ -108,6 +108,9 @@ TEST_CASE("expressions")
     TRY_CHECK(eval(" rad_to_deg * atan(1.0) ").to_double() == approx(45.0));
     // exp
     TRY_CHECK(eval(" exp(0.0) ") == 1.0);
+    // log
+    TRY_CHECK(eval(" log(1.0) ") == 0.0);
+    TRY_CHECK(eval(" log(exp(1.0)) ") == 1.0);
     // abs
     TRY_CHECK(eval(" abs(cos(-pi)) ") == 1.0);
     // length (string)
diff --git a/test/unit/datasource/ds_test_util.hpp b/test/unit/datasource/ds_test_util.hpp
index 6e1c222..46868d4 100644
--- a/test/unit/datasource/ds_test_util.hpp
+++ b/test/unit/datasource/ds_test_util.hpp
@@ -106,13 +106,18 @@ inline std::size_t count_features(mapnik::featureset_ptr features) {
 }
 
 using attr = std::tuple<std::string, mapnik::value>;
+
+#define REQUIRE_ATTRIBUTES(feature, attrs) \
+    REQUIRE(bool(feature)); \
+    for (auto const &kv : attrs) { \
+        REQUIRE(feature->has_key(std::get<0>(kv))); \
+        CHECK(feature->get(std::get<0>(kv)) == std::get<1>(kv)); \
+    } \
+
+
 inline void require_attributes(mapnik::feature_ptr feature,
                         std::initializer_list<attr> const &attrs) {
-    REQUIRE(bool(feature));
-    for (auto const &kv : attrs) {
-        REQUIRE(feature->has_key(std::get<0>(kv)));
-        CHECK(feature->get(std::get<0>(kv)) == std::get<1>(kv));
-    }
+    REQUIRE_ATTRIBUTES(feature, attrs);
 }
 
 namespace detail {
diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp
index 0b0f205..1edeb4b 100644
--- a/test/unit/datasource/geojson.cpp
+++ b/test/unit/datasource/geojson.cpp
@@ -23,6 +23,7 @@
 #include "catch.hpp"
 #include "ds_test_util.hpp"
 
+#include <mapnik/unicode.hpp>
 #include <mapnik/datasource.hpp>
 #include <mapnik/datasource_cache.hpp>
 #include <mapnik/geometry.hpp>
@@ -654,5 +655,72 @@ TEST_CASE("geojson") {
                 }
             }
         }
+
+        SECTION("GeoJSON properties are properly expressed")
+        {
+            mapnik::transcoder tr("utf8");
+            mapnik::parameters params;
+            params["type"] = "geojson";
+
+            std::string filename("./test/data/json/escaped.geojson");
+            params["file"] = filename;
+
+            // cleanup in the case of a failed previous run
+            if (mapnik::util::exists(filename + ".index"))
+            {
+                mapnik::util::remove(filename + ".index");
+            }
+
+            for (auto create_index : { true, false })
+            {
+                if (create_index)
+                {
+                    CHECK(!mapnik::util::exists(filename + ".index"));
+                    int ret = create_disk_index(filename);
+                    int ret_posix = (ret >> 8) & 0x000000ff;
+                    INFO(ret);
+                    INFO(ret_posix);
+                    CHECK(mapnik::util::exists(filename + ".index"));
+                }
+
+                for (auto cache_features : {true, false})
+                {
+                    params["cache_features"] = cache_features;
+                    auto ds = mapnik::datasource_cache::instance().create(params);
+                    REQUIRE(bool(ds));
+                    auto fields = ds->get_descriptor().get_descriptors();
+                    std::initializer_list<std::string> names = {"NOM_FR","array","boolean","description","double","empty_array", "empty_object","int","name","object","spaces"};
+                    REQUIRE_FIELD_NAMES(fields, names);
+
+                    auto fs = all_features(ds);
+                    REQUIRE(bool(fs));
+                    std::initializer_list<attr> attrs = {
+                        attr{"name", tr.transcode("Test")},
+                        attr{"NOM_FR", tr.transcode("Québec")},
+                        attr{"boolean", mapnik::value_bool("true")},
+                        attr{"description", tr.transcode("Test: \u005C")},
+                        attr{"double", mapnik::value_double(1.1)},
+                        attr{"int", mapnik::value_integer(1)},
+                        attr{"object", tr.transcode("{name:\"waka\",spaces:\"value with spaces\",int:1,double:1.1,boolean:false"
+                                                    ",NOM_FR:\"Québec\",array:[\"string\",\"value with spaces\",3,1.1,null,true"
+                                                    ",\"Québec\"],another_object:{name:\"nested object\"}}")},
+                        attr{"spaces", tr.transcode("this has spaces")},
+                        attr{"array", tr.transcode("[\"string\",\"value with spaces\",3,1.1,null,true,"
+                                                   "\"Québec\",{name:\"object within an array\"},"
+                                                   "[\"array\",\"within\",\"an\",\"array\"]]")},
+                        attr{"empty_array", tr.transcode("[]")},
+                        attr{"empty_object", tr.transcode("{}")},
+                    };
+                    auto feature = fs->next();
+                    REQUIRE(bool(feature));
+                    REQUIRE_ATTRIBUTES(feature, attrs);
+                }
+                // cleanup
+                if (create_index && mapnik::util::exists(filename + ".index"))
+                {
+                    mapnik::util::remove(filename + ".index");
+                }
+            }
+        }
     }
 }
diff --git a/test/unit/datasource/postgis.cpp b/test/unit/datasource/postgis.cpp
index 2689952..95ee658 100644
--- a/test/unit/datasource/postgis.cpp
+++ b/test/unit/datasource/postgis.cpp
@@ -29,13 +29,15 @@
 #include <mapnik/util/fs.hpp>
 
 /*
-Compile and run just this test:
-clang++ -o test-postgis -g -I./test/ test/unit/run.cpp test/unit/datasource/postgis.cpp `mapnik-config --all-flags` && ./test-postgis -d yes
+  Compile and run just this test:
+  clang++ -o test-postgis -g -I./test/ test/unit/run.cpp test/unit/datasource/postgis.cpp `mapnik-config --all-flags` && ./test-postgis -d yes
 */
 
 #include <boost/optional/optional_io.hpp>
 
-int run(std::string const& command, bool okay_to_fail = false)
+namespace {
+
+bool run(std::string const& command, bool okay_to_fail = false)
 {
     std::string cmd;
     if (std::getenv("DYLD_LIBRARY_PATH") != nullptr)
@@ -61,251 +63,278 @@ int run(std::string const& command, bool okay_to_fail = false)
     return worked;
 }
 
-std::string dbname("mapnik-tmp-postgis-test-db");
 
-TEST_CASE("postgis") {
+std::string const dbname("mapnik-tmp-postgis-test-db");
+bool status = false;
 
-    SECTION("Postgis data initialization")
-    {
-        //don't add 'true' here, to get error message, when drop fails. If it works nothing is output
-        REQUIRE(run("dropdb --if-exists " + dbname));
-        REQUIRE(run("createdb -T template_postgis " + dbname));
-        //REQUIRE(run("createdb " + dbname));
-        // Breaks when raster support is missing (unfortunately this is common)
-        //REQUIRE(run("psql -c 'CREATE EXTENSION postgis;' " + dbname, true));
-        REQUIRE(run("psql -q -f ./test/data/sql/postgis-create-db-and-tables.sql " + dbname));
-    }
+bool ping_postmaster()
+{
+    return (run("psql --version")
+            && run("dropdb --if-exists " + dbname)
+            && run("createdb -T template_postgis " + dbname));
+}
 
-    mapnik::parameters base_params;
-    base_params["type"] = "postgis";
-    base_params["dbname"] = dbname;
+}
 
-    SECTION("Postgis should throw without 'table' parameter")
-    {
-        mapnik::parameters params(base_params);
-        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
-    }
+TEST_CASE("postgis") {
 
-    SECTION("Postgis should throw with 'max_async_connection' greater than 'max_size'")
+    SECTION("Ping Postmaster (check if server is runnging and accessible")
     {
-        mapnik::parameters params(base_params);
-        params["table"] = "test";
-        params["max_async_connection"] = "2";
-        params["max_size"] = "1";
-        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+        if (!ping_postmaster())
+        {
+            WARN("Can't run postgis.input tests - check postmaster is running and accessible");
+            return;
+        }
+        else
+        {
+            status = true;
+        }
     }
-
-    SECTION("Postgis should throw with invalid metadata query")
+    if (status)
     {
-        mapnik::parameters params(base_params);
-        params["table"] = "does_not_exist";
-        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
-    }
+        SECTION("Postgis data initialization")
+        {
+            //don't add 'true' here, to get error message, when drop fails. If it works nothing is output
+            REQUIRE(run("dropdb --if-exists " + dbname));
+            REQUIRE(run("createdb -T template_postgis " + dbname));
+            //REQUIRE(run("createdb " + dbname));
+            // Breaks when raster support is missing (unfortunately this is common)
+            //REQUIRE(run("psql -c 'CREATE EXTENSION postgis;' " + dbname, true));
+            REQUIRE(run("psql -q -f ./test/data/sql/postgis-create-db-and-tables.sql " + dbname));
+        }
 
-    SECTION("Postgis should throw with invalid key field")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "test_invalid_id";
-        params["key_field"] = "id";
-        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
-    }
+        mapnik::parameters base_params;
+        base_params["type"] = "postgis";
+        base_params["dbname"] = dbname;
 
-    SECTION("Postgis should throw with multicolumn primary key")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "test_invalid_multi_col_pk";
-        params["autodetect_key_field"] = "true";
-        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
-    }
+        SECTION("Postgis should throw without 'table' parameter")
+        {
+            mapnik::parameters params(base_params);
+            CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+        }
 
-    SECTION("Postgis should throw without geom column")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "test_no_geom_col";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        CHECK_THROWS(all_features(ds));
-    }
+        SECTION("Postgis should throw with 'max_async_connection' greater than 'max_size'")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "test";
+            params["max_async_connection"] = "2";
+            params["max_size"] = "1";
+            CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+        }
 
-    SECTION("Postgis should throw with invalid credentials")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "test";
-        params["user"] = "not_a_valid_user";
-        params["password"] = "not_a_valid_pwd";
-        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
-    }
+        SECTION("Postgis should throw with invalid metadata query")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "does_not_exist";
+            CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+        }
 
-    SECTION("Postgis initialize dataset with persist_connection, schema, extent, geometry field, autodectect key field, simplify_geometries, row_limit")
-    {
-        mapnik::parameters params(base_params);
-        params["persist_connection"] = "false";
-        params["table"] = "public.test";
-        params["geometry_field"] = "geom";
-        params["autodetect_key_field"] = "true";
-        params["extent"] = "-1 -1, -1 2, 4 3, 3 -1, -1 -1";
-        params["simplify_geometries"] = "true";
-        params["row_limit"] = "1";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-    }
+        SECTION("Postgis should throw with invalid key field")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "test_invalid_id";
+            params["key_field"] = "id";
+            CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+        }
 
-    SECTION("Postgis dataset geometry type")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "(SELECT * FROM test WHERE gid=1) as data";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point);
-    }
+        SECTION("Postgis should throw with multicolumn primary key")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "test_invalid_multi_col_pk";
+            params["autodetect_key_field"] = "true";
+            CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+        }
 
-    SECTION("Postgis query field names")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "test";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        REQUIRE(ds->type() == mapnik::datasource::datasource_t::Vector);
-        auto fields = ds->get_descriptor().get_descriptors();
-        require_field_names(fields, { "gid", "colbigint", "col_text", "col-char", "col+bool", "colnumeric", "colsmallint", "colfloat4", "colfloat8", "colcharacter" });
-        require_field_types(fields, { mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Boolean, mapnik::Double, mapnik::Integer, mapnik::Double, mapnik::Double, mapnik::String });
-    }
+        SECTION("Postgis should throw without geom column")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "test_no_geom_col";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+            REQUIRE(ds != nullptr);
+            CHECK_THROWS(all_features(ds));
+        }
 
-    SECTION("Postgis iterate features")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "test";
-        params["key_field"] = "gid";
-        params["max_async_connection"] = "2";
-        //params["cursor_size"] = "2";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-
-        auto featureset = ds->features_at_point(mapnik::coord2d(1, 1));
-        mapnik::feature_ptr feature;
-        while ((bool(feature = featureset->next()))) {
-            REQUIRE(feature->get(2).to_string() == feature->get("col_text").to_string());
-            REQUIRE(feature->get(4).to_bool() == feature->get("col+bool").to_bool());
-            REQUIRE(feature->get(5).to_double() == feature->get("colnumeric").to_double());
-            REQUIRE(feature->get(5).to_string() == feature->get("colnumeric").to_string());
+        SECTION("Postgis should throw with invalid credentials")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "test";
+            params["user"] = "not_a_valid_user";
+            params["password"] = "not_a_valid_pwd";
+            CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
         }
 
-        featureset = all_features(ds);
-        feature = featureset->next();
-        //deactivate char tests for now: not yet implemented.
-        //add at postgis_datasource.cpp:423
-        //case 18:    // char
-        //REQUIRE("A" == feature->get("col-char").to_string());
-        feature = featureset->next();
-        //REQUIRE("B" == feature->get("col-char").to_string());
-        feature = featureset->next();
-        REQUIRE(false == feature->get("col+bool").to_bool());
-    }
+        SECTION("Postgis initialize dataset with persist_connection, schema, extent, geometry field, autodectect key field, simplify_geometries, row_limit")
+        {
+            mapnik::parameters params(base_params);
+            params["persist_connection"] = "false";
+            params["table"] = "public.test";
+            params["geometry_field"] = "geom";
+            params["autodetect_key_field"] = "true";
+            params["extent"] = "-1 -1, -1 2, 4 3, 3 -1, -1 -1";
+            params["simplify_geometries"] = "true";
+            params["row_limit"] = "1";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+        }
 
-    SECTION("Postgis cursorresultest")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "(SELECT * FROM test) as data";
-        params["cursor_size"] = "2";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        auto featureset = all_features(ds);
-        CHECK(count_features(featureset) == 8);
-
-        featureset = all_features(ds);
-        mapnik::feature_ptr feature;
-        while (bool(feature = featureset->next())) {
-            CHECK(feature->size() == 10);
+        SECTION("Postgis dataset geometry type")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "(SELECT * FROM test WHERE gid=1) as data";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+            REQUIRE(ds != nullptr);
+            CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point);
         }
 
-        featureset = all_features(ds);
-        require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point);
-        require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point);
-        require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPoint);
-        require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::LineString);
-        require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiLineString);
-        require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Polygon);
-        require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPolygon);
-        require_geometry(featureset->next(), 3, mapnik::geometry::geometry_types::GeometryCollection);
-    }
+        SECTION("Postgis query field names")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "test";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+            REQUIRE(ds != nullptr);
+            REQUIRE(ds->type() == mapnik::datasource::datasource_t::Vector);
+            auto fields = ds->get_descriptor().get_descriptors();
+            require_field_names(fields, { "gid", "colbigint", "col_text", "col-char", "col+bool", "colnumeric", "colsmallint", "colfloat4", "colfloat8", "colcharacter" });
+            require_field_types(fields, { mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Boolean, mapnik::Double, mapnik::Integer, mapnik::Double, mapnik::Double, mapnik::String });
+        }
 
-    SECTION("Postgis bbox query")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "(SELECT * FROM public.test) as data WHERE geom && !bbox!";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        mapnik::box2d<double> ext = ds->envelope();
-        CAPTURE(ext);
-        INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
-        REQUIRE(ext.minx() == -2);
-        REQUIRE(ext.miny() == -2);
-        REQUIRE(ext.maxx() == 5);
-        REQUIRE(ext.maxy() == 4);
-    }
+        SECTION("Postgis iterate features")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "test";
+            params["key_field"] = "gid";
+            params["max_async_connection"] = "2";
+            //params["cursor_size"] = "2";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+            REQUIRE(ds != nullptr);
 
-    SECTION("Postgis query extent: full dataset")
-    {
-        //include schema to increase coverage
-        mapnik::parameters params(base_params);
-        params["table"] = "(SELECT * FROM public.test) as data";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        mapnik::box2d<double> ext = ds->envelope();
-        CAPTURE(ext);
-        INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
-        REQUIRE(ext.minx() == -2);
-        REQUIRE(ext.miny() == -2);
-        REQUIRE(ext.maxx() == 5);
-        REQUIRE(ext.maxy() == 4);
-    }
+            auto featureset = ds->features_at_point(mapnik::coord2d(1, 1));
+            mapnik::feature_ptr feature;
+            while ((bool(feature = featureset->next()))) {
+                REQUIRE(feature->get(2).to_string() == feature->get("col_text").to_string());
+                REQUIRE(feature->get(4).to_bool() == feature->get("col+bool").to_bool());
+                REQUIRE(feature->get(5).to_double() == feature->get("colnumeric").to_double());
+                REQUIRE(feature->get(5).to_string() == feature->get("colnumeric").to_string());
+            }
+
+            featureset = all_features(ds);
+            feature = featureset->next();
+            //deactivate char tests for now: not yet implemented.
+            //add at postgis_datasource.cpp:423
+            //case 18:    // char
+            //REQUIRE("A" == feature->get("col-char").to_string());
+            feature = featureset->next();
+            //REQUIRE("B" == feature->get("col-char").to_string());
+            feature = featureset->next();
+            REQUIRE(false == feature->get("col+bool").to_bool());
+        }
+
+        SECTION("Postgis cursorresultest")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "(SELECT * FROM test) as data";
+            params["cursor_size"] = "2";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+            REQUIRE(ds != nullptr);
+            auto featureset = all_features(ds);
+            CHECK(count_features(featureset) == 8);
+
+            featureset = all_features(ds);
+            mapnik::feature_ptr feature;
+            while (bool(feature = featureset->next())) {
+                CHECK(feature->size() == 10);
+            }
+
+            featureset = all_features(ds);
+            require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point);
+            require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point);
+            require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPoint);
+            require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::LineString);
+            require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiLineString);
+            require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Polygon);
+            require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPolygon);
+            require_geometry(featureset->next(), 3, mapnik::geometry::geometry_types::GeometryCollection);
+        }
+
+        SECTION("Postgis bbox query")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "(SELECT * FROM public.test) as data WHERE geom && !bbox!";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+            REQUIRE(ds != nullptr);
+            mapnik::box2d<double> ext = ds->envelope();
+            CAPTURE(ext);
+            INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+            REQUIRE(ext.minx() == -2);
+            REQUIRE(ext.miny() == -2);
+            REQUIRE(ext.maxx() == 5);
+            REQUIRE(ext.maxy() == 4);
+        }
+
+        SECTION("Postgis query extent: full dataset")
+        {
+            //include schema to increase coverage
+            mapnik::parameters params(base_params);
+            params["table"] = "(SELECT * FROM public.test) as data";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+            REQUIRE(ds != nullptr);
+            mapnik::box2d<double> ext = ds->envelope();
+            CAPTURE(ext);
+            INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+            REQUIRE(ext.minx() == -2);
+            REQUIRE(ext.miny() == -2);
+            REQUIRE(ext.maxx() == 5);
+            REQUIRE(ext.maxy() == 4);
+        }
 /* deactivated for merging: still investigating a proper fix
-    SECTION("Postgis query extent from subquery")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "(SELECT * FROM test where gid=4) as data";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        mapnik::box2d<double> ext = ds->envelope();
-        CAPTURE(ext);
-        INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
-        REQUIRE(ext.minx() == 0);
-        REQUIRE(ext.miny() == 0);
-        REQUIRE(ext.maxx() == 1);
-        REQUIRE(ext.maxy() == 2);
-    }
+   SECTION("Postgis query extent from subquery")
+   {
+   mapnik::parameters params(base_params);
+   params["table"] = "(SELECT * FROM test where gid=4) as data";
+   auto ds = mapnik::datasource_cache::instance().create(params);
+   REQUIRE(ds != nullptr);
+   mapnik::box2d<double> ext = ds->envelope();
+   CAPTURE(ext);
+   INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+   REQUIRE(ext.minx() == 0);
+   REQUIRE(ext.miny() == 0);
+   REQUIRE(ext.maxx() == 1);
+   REQUIRE(ext.maxy() == 2);
+   }
 */
-    SECTION("Postgis query extent: from subquery with 'extent_from_subquery=true'")
-    {
-        mapnik::parameters params(base_params);
-        params["table"] = "(SELECT * FROM test where gid=4) as data";
-        params["extent_from_subquery"] = "true";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        mapnik::box2d<double> ext = ds->envelope();
-        CAPTURE(ext);
-        INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
-        REQUIRE(ext.minx() == 0);
-        REQUIRE(ext.miny() == 0);
-        REQUIRE(ext.maxx() == 1);
-        REQUIRE(ext.maxy() == 2);
-    }
+        SECTION("Postgis query extent: from subquery with 'extent_from_subquery=true'")
+        {
+            mapnik::parameters params(base_params);
+            params["table"] = "(SELECT * FROM test where gid=4) as data";
+            params["extent_from_subquery"] = "true";
+            auto ds = mapnik::datasource_cache::instance().create(params);
+            REQUIRE(ds != nullptr);
+            mapnik::box2d<double> ext = ds->envelope();
+            CAPTURE(ext);
+            INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+            REQUIRE(ext.minx() == 0);
+            REQUIRE(ext.miny() == 0);
+            REQUIRE(ext.maxx() == 1);
+            REQUIRE(ext.maxy() == 2);
+        }
 /* deactivated for merging: still investigating a proper fix
-    SECTION("Postgis query extent: subset with 'extent_from_subquery=true' and 'scale_denominator'")
-    {
-        mapnik::parameters params(base_params);
-        // !!!! postgis-vt-util::z() returns 'null' when 'scale_denominator > 600000000'
-        // https://github.com/mapbox/postgis-vt-util/blob/559f073877696a6bfea41baf3e1065f9cf4d18d1/postgis-vt-util.sql#L615-L617
-        params["table"] = "(SELECT * FROM test where gid=4 AND z(!scale_denominator!) BETWEEN 0 AND 22) as data";
-        params["extent_from_subquery"] = "true";
-        auto ds = mapnik::datasource_cache::instance().create(params);
-        REQUIRE(ds != nullptr);
-        mapnik::box2d<double> ext = ds->envelope();
-        CAPTURE(ext);
-        INFO("" << std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
-        REQUIRE(ext.minx() == 0);
-        REQUIRE(ext.miny() == 0);
-        REQUIRE(ext.maxx() == 1);
-        REQUIRE(ext.maxy() == 2);
-    }
+   SECTION("Postgis query extent: subset with 'extent_from_subquery=true' and 'scale_denominator'")
+   {
+   mapnik::parameters params(base_params);
+   // !!!! postgis-vt-util::z() returns 'null' when 'scale_denominator > 600000000'
+   // https://github.com/mapbox/postgis-vt-util/blob/559f073877696a6bfea41baf3e1065f9cf4d18d1/postgis-vt-util.sql#L615-L617
+   params["table"] = "(SELECT * FROM test where gid=4 AND z(!scale_denominator!) BETWEEN 0 AND 22) as data";
+   params["extent_from_subquery"] = "true";
+   auto ds = mapnik::datasource_cache::instance().create(params);
+   REQUIRE(ds != nullptr);
+   mapnik::box2d<double> ext = ds->envelope();
+   CAPTURE(ext);
+   INFO("" << std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+   REQUIRE(ext.minx() == 0);
+   REQUIRE(ext.miny() == 0);
+   REQUIRE(ext.maxx() == 1);
+   REQUIRE(ext.maxy() == 2);
+   }
 */
-}
\ No newline at end of file
+
+    }
+}
diff --git a/test/unit/datasource/topojson.cpp b/test/unit/datasource/topojson.cpp
index 25f9ffd..6b1a2e1 100644
--- a/test/unit/datasource/topojson.cpp
+++ b/test/unit/datasource/topojson.cpp
@@ -21,6 +21,7 @@
  *****************************************************************************/
 
 #include "catch.hpp"
+#include "ds_test_util.hpp"
 
 #include <mapnik/util/fs.hpp>
 #include <mapnik/util/file_io.hpp>
@@ -50,7 +51,7 @@ bool parse_topology(std::string const& filename, mapnik::topojson::topology & to
 
 }
 
-TEST_CASE("topology")
+TEST_CASE("topojson")
 {
     SECTION("geometry parsing")
     {
@@ -72,4 +73,42 @@ TEST_CASE("topology")
             }
         }
     }
+
+    SECTION("TopoJSON properties are properly expressed")
+    {
+        std::string filename("./test/data/topojson/escaped.topojson");
+        mapnik::context_ptr ctx = std::make_shared<mapnik::context_type>();
+        mapnik::transcoder tr("utf8");
+        mapnik::topojson::topology topo;
+        REQUIRE(parse_topology(filename, topo));
+        mapnik::value_integer feature_id = 0;
+        for (auto const& geom : topo.geometries)
+        {
+            mapnik::box2d<double> bbox = mapnik::util::apply_visitor(mapnik::topojson::bounding_box_visitor(topo), geom);
+            CHECK(bbox.valid());
+            mapnik::topojson::feature_generator<mapnik::context_ptr> visitor(ctx, tr, topo, feature_id);
+            mapnik::feature_ptr feature = mapnik::util::apply_visitor(visitor, geom);
+            CHECK(feature);
+            CHECK(feature->envelope() == bbox);
+            std::initializer_list<attr> attrs = {
+                attr{"name", tr.transcode("Test")},
+                attr{"NOM_FR", tr.transcode("Québec")},
+                attr{"boolean", mapnik::value_bool("true")},
+                attr{"description", tr.transcode("Test: \u005C")},
+                attr{"double", mapnik::value_double(1.1)},
+                attr{"int", mapnik::value_integer(1)},
+                attr{"object", tr.transcode("{name:\"waka\",spaces:\"value with spaces\",int:1,double:1.1,boolean:false"
+                                                            ",NOM_FR:\"Québec\",array:[\"string\",\"value with spaces\",3,1.1,null,true"
+                                                            ",\"Québec\"],another_object:{name:\"nested object\"}}")},
+                attr{"spaces", tr.transcode("this has spaces")},
+                attr{"array", tr.transcode("[\"string\",\"value with spaces\",3,1.1,null,true,"
+                                                           "\"Québec\",{name:\"object within an array\"},"
+                                                           "[\"array\",\"within\",\"an\",\"array\"]]")},
+                attr{"empty_array", tr.transcode("[]")},
+                attr{"empty_object", tr.transcode("{}")},
+            };
+            REQUIRE_ATTRIBUTES(feature, attrs);
+        }
+    }
+
 }
diff --git a/test/unit/geometry/geometry_envelope_test.cpp b/test/unit/geometry/geometry_envelope_test.cpp
index fd5d78f..7504982 100644
--- a/test/unit/geometry/geometry_envelope_test.cpp
+++ b/test/unit/geometry/geometry_envelope_test.cpp
@@ -1,17 +1,19 @@
-
 #include "catch.hpp"
 
 #include <mapnik/geometry.hpp>
 #include <mapnik/geometry_envelope.hpp>
-#include <mapnik/geometry_correct.hpp>
 
-TEST_CASE("geometry ops - envelope") {
+namespace {
 
-SECTION("envelope_test - double") {
+template <typename T>
+void envelope_test()
+{
     using namespace mapnik::geometry;
+    using coord_type = T;
+
     {
-        geometry<double> geom(point<double>(1,2));
-        mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
+        geometry<coord_type> geom(point<coord_type>(1,2));
+        mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
         REQUIRE( bbox.minx() == 1 );
         REQUIRE( bbox.miny() == 2 );
         REQUIRE( bbox.maxx() == 1 );
@@ -19,80 +21,67 @@ SECTION("envelope_test - double") {
     }
     {
         // Test empty geom
-        geometry<double> geom = mapnik::geometry::geometry_empty();
-        mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
+        geometry<coord_type> geom = mapnik::geometry::geometry_empty();
+        mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
         REQUIRE_FALSE( bbox.valid() );
     }
     {
-        line_string<double> line;
+        line_string<coord_type> line;
         line.add_coord(0,0);
         line.add_coord(1,1);
         line.add_coord(2,2);
-        geometry<double> geom(line);
-        mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
+        geometry<coord_type> geom(line);
+        mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
         REQUIRE( bbox.minx() == 0 );
         REQUIRE( bbox.miny() == 0 );
         REQUIRE( bbox.maxx() == 2 );
         REQUIRE( bbox.maxy() == 2 );
     }
     {
-        line_string<double> line;
+        line_string<coord_type> line;
         line.add_coord(0,0);
         line.add_coord(1,1);
         line.add_coord(2,2);
-        line_string<double> line2;
+        line_string<coord_type> line2;
         line2.add_coord(0,0);
         line2.add_coord(-1,-1);
         line2.add_coord(-2,-2);
-        multi_line_string<double> multi_line;
+        multi_line_string<coord_type> multi_line;
         multi_line.emplace_back(std::move(line));
         multi_line.emplace_back(std::move(line2));
-        geometry<double> geom(multi_line);
-        mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
+        geometry<coord_type> geom(multi_line);
+        mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
         REQUIRE( bbox.minx() == -2 );
         REQUIRE( bbox.miny() == -2 );
         REQUIRE( bbox.maxx() == 2 );
         REQUIRE( bbox.maxy() == 2 );
     }
     {
-        polygon<double> poly;
-        linear_ring<double> ring;
+        polygon<coord_type> poly;
+        linear_ring<coord_type> ring;
         ring.add_coord(0,0);
         ring.add_coord(-10,0);
         ring.add_coord(-10,10);
         ring.add_coord(0,10);
         ring.add_coord(0,0);
         poly.set_exterior_ring(std::move(ring));
-        geometry<double> geom(poly);
-        mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
+        geometry<coord_type> geom(poly);
+        mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(geom);
         REQUIRE( bbox.minx() == -10 );
         REQUIRE( bbox.miny() == 0 );
         REQUIRE( bbox.maxx() == 0 );
         REQUIRE( bbox.maxy() == 10 );
 
-        multi_polygon<double> mp;
+        multi_polygon<coord_type> mp;
         mp.push_back(poly);
-        geometry<double> geom_mp(mp);
+        geometry<coord_type> geom_mp(mp);
         bbox = mapnik::geometry::envelope(geom_mp);
         REQUIRE( bbox.minx() == -10 );
         REQUIRE( bbox.miny() == 0 );
         REQUIRE( bbox.maxx() == 0 );
         REQUIRE( bbox.maxy() == 10 );
 
-        correct(geom);
-        bbox = mapnik::geometry::envelope(geom);
-        REQUIRE( bbox.minx() == -10 );
-        REQUIRE( bbox.miny() == 0 );
-        REQUIRE( bbox.maxx() == 0 );
-        REQUIRE( bbox.maxy() == 10 );
-        correct(geom_mp);
-        bbox = mapnik::geometry::envelope(geom_mp);
-        REQUIRE( bbox.minx() == -10 );
-        REQUIRE( bbox.miny() == 0 );
-        REQUIRE( bbox.maxx() == 0 );
-        REQUIRE( bbox.maxy() == 10 );
-
-        geometry_collection<double> gc;
+        geometry_collection<coord_type> gc;
         bbox = mapnik::geometry::envelope(gc);
         REQUIRE_FALSE( bbox.valid() );
         gc.push_back(geom_mp);
@@ -101,7 +90,7 @@ SECTION("envelope_test - double") {
         REQUIRE( bbox.miny() == 0 );
         REQUIRE( bbox.maxx() == 0 );
         REQUIRE( bbox.maxy() == 10 );
-        gc.emplace_back(point<double>(-50,-50));
+        gc.emplace_back(point<coord_type>(-50,-50));
         bbox = mapnik::geometry::envelope(gc);
         REQUIRE( bbox.minx() == -50 );
         REQUIRE( bbox.miny() == -50 );
@@ -111,30 +100,30 @@ SECTION("envelope_test - double") {
 
     {
         // polygon with hole
-        polygon<double> poly;
-        linear_ring<double> ring;
+        polygon<coord_type> poly;
+        linear_ring<coord_type> ring;
         ring.add_coord(0,0);
         ring.add_coord(-10,0);
         ring.add_coord(-10,10);
         ring.add_coord(0,10);
         ring.add_coord(0,0);
         poly.set_exterior_ring(std::move(ring));
-        linear_ring<double> hole;
+        linear_ring<coord_type> hole;
         hole.add_coord(-7,7);
         hole.add_coord(-7,3);
         hole.add_coord(-3,3);
         hole.add_coord(-3,7);
         hole.add_coord(-7,7);
         poly.add_hole(std::move(hole));
-        geometry<double> geom(poly);
-        mapnik::box2d<double> bbox = mapnik::geometry::envelope(poly);
+        geometry<coord_type> geom(poly);
+        mapnik::box2d<coord_type> bbox = mapnik::geometry::envelope(poly);
         REQUIRE( bbox.minx() == -10 );
         REQUIRE( bbox.miny() == 0 );
         REQUIRE( bbox.maxx() == 0 );
         REQUIRE( bbox.maxy() == 10 );
         // add another hole inside the first hole
         // which should be considered a hit
-        linear_ring<double> fill;
+        linear_ring<coord_type> fill;
         fill.add_coord(-6,4);
         fill.add_coord(-6,6);
         fill.add_coord(-4,6);
@@ -150,3 +139,14 @@ SECTION("envelope_test - double") {
 }
 
 }
+
+TEST_CASE("geometry ops - envelope") {
+
+SECTION("envelope_test")
+{
+    envelope_test<int>();
+    envelope_test<double>();
+    envelope_test<float>();
+}
+
+}
diff --git a/test/unit/geometry/geometry_equal.hpp b/test/unit/geometry/geometry_equal.hpp
index f8ebc4d..4ff80ab 100644
--- a/test/unit/geometry/geometry_equal.hpp
+++ b/test/unit/geometry/geometry_equal.hpp
@@ -1,4 +1,3 @@
-
 #ifndef MAPNIK_UNIT_GEOMETRY_EQUAL
 #define MAPNIK_UNIT_GEOMETRY_EQUAL
 
@@ -85,7 +84,15 @@ auto zip_crange(Conts&... conts)
 #include <mapnik/geometry.hpp>
 #include <mapnik/util/variant.hpp>
 
-using namespace mapnik::geometry;
+using mapnik::geometry::geometry;
+using mapnik::geometry::geometry_empty;
+using mapnik::geometry::point;
+using mapnik::geometry::line_string;
+using mapnik::geometry::polygon;
+using mapnik::geometry::multi_point;
+using mapnik::geometry::multi_line_string;
+using mapnik::geometry::multi_polygon;
+using mapnik::geometry::geometry_collection;
 
 template <typename T>
 void assert_g_equal(geometry<T> const& g1, geometry<T> const& g2);
diff --git a/test/unit/geometry/geometry_test_helper.cpp b/test/unit/geometry/geometry_test_helper.cpp
new file mode 100644
index 0000000..44bcce5
--- /dev/null
+++ b/test/unit/geometry/geometry_test_helper.cpp
@@ -0,0 +1,15 @@
+#include <mapnik/geometry.hpp>
+#include <mapnik/geometry_envelope.hpp>
+#include <mapnik/geometry_envelope_impl.hpp>
+
+
+namespace mapnik { namespace geometry {
+// instantiate types required by geometry_envelope_test
+template mapnik::box2d<int> envelope(geometry<int> const& geom);
+template mapnik::box2d<float> envelope(geometry<float> const& geom);
+template mapnik::box2d<int> envelope(polygon<int> const& geom);
+template mapnik::box2d<float> envelope(polygon<float> const& geom);
+template mapnik::box2d<int> envelope(geometry_collection<int> const& geom);
+template mapnik::box2d<float> envelope(geometry_collection<float> const& geom);
+
+}}
diff --git a/test/unit/geometry/is_clockwise.cpp b/test/unit/geometry/is_clockwise.cpp
new file mode 100644
index 0000000..42aebf6
--- /dev/null
+++ b/test/unit/geometry/is_clockwise.cpp
@@ -0,0 +1,27 @@
+#include "catch.hpp"
+
+#include <mapnik/geometry.hpp>
+#include <mapnik/util/is_clockwise.hpp>
+
+TEST_CASE("Ring is_clockwise") {
+
+    // Input is rather thin triangle to test precision issues aren't getting in the way.
+    SECTION("Clockwise")
+    {
+        mapnik::geometry::linear_ring<double> ring;
+        ring.emplace_back(-13499697.0366658326, 4698431.85179749783);
+        ring.emplace_back(-13499697.1113113686, 4698431.85179749783);
+        ring.emplace_back(-13499697.0366658326, 4698431.92644303292);
+        ring.emplace_back(-13499697.0366658326, 4698431.85179749783);
+        REQUIRE(mapnik::util::is_clockwise(ring) == true);
+    }
+    SECTION("Anti-Clockwise")
+    {
+        mapnik::geometry::linear_ring<double> ring;
+        ring.emplace_back(-13499697.0366658326, 4698431.85179749783);
+        ring.emplace_back(-13499697.0366658326, 4698431.92644303292);
+        ring.emplace_back(-13499697.1113113686, 4698431.85179749783);
+        ring.emplace_back(-13499697.0366658326, 4698431.85179749783);
+        REQUIRE(mapnik::util::is_clockwise(ring) == false);
+    }
+}
diff --git a/test/unit/imaging/image_io_test.cpp b/test/unit/imaging/image_io_test.cpp
index c562148..18c01da 100644
--- a/test/unit/imaging/image_io_test.cpp
+++ b/test/unit/imaging/image_io_test.cpp
@@ -1,7 +1,5 @@
-
 #include "catch.hpp"
 
-#include <iostream>
 #include <cstring>
 #include <mapnik/color.hpp>
 #include <mapnik/image.hpp>
@@ -24,6 +22,31 @@ inline void make_directory(std::string const& dir) {
     boost::filesystem::create_directories(dir);
 }
 
+namespace {
+template <typename T>
+void check_tiny_png_image_quantising(T const& im)
+{
+    std::ostringstream ss(std::ios::binary);
+    mapnik::save_to_stream(im, ss, "png8");
+    ss.flush();
+    std::string str = ss.str();
+    std::unique_ptr<mapnik::image_reader> reader(mapnik::get_image_reader(str.data(), str.size()));
+    auto w = reader->width();
+    auto h = reader->height();
+    CHECK(w > 0);
+    CHECK(h > 0);
+    auto im2 = mapnik::util::get<mapnik::image_rgba8>(reader->read(0, 0, w, h));
+    for (std::size_t i = 0; i < w; ++i)
+    {
+        for (std::size_t j = 0; j < h; ++j)
+        {
+            REQUIRE(im2(i,j) == im(i,j));
+        }
+    }
+}
+
+}
+
 TEST_CASE("image io") {
 
 SECTION("readers") {
@@ -186,4 +209,28 @@ SECTION("image_util : save_to_file/save_to_stream/save_to_string")
         }
     }
 }
+
+SECTION("Quantising small (less than 3 pixel images preserve original colours")
+{
+#if defined(HAVE_PNG)
+    { // 1x1
+        mapnik::image_rgba8 im(1,1); // 1 pixel
+        im(0,0) = mapnik::color("green").rgba();
+        check_tiny_png_image_quantising(im);
+    }
+    { // 1x2
+        mapnik::image_rgba8 im(1,2); // 2 pixels
+        mapnik::fill(im, mapnik::color("red").rgba());
+        im(0,0) = mapnik::color("green").rgba();
+        check_tiny_png_image_quantising(im);
+    }
+    { // 2x1
+        mapnik::image_rgba8 im(2,1); // 2 pixels
+        mapnik::fill(im, mapnik::color("red").rgba());
+        im(0,0) = mapnik::color("green").rgba();
+        check_tiny_png_image_quantising(im);
+    }
+#endif
+} // END SECTION
+
 } // END TEST_CASE
diff --git a/test/unit/imaging/tiff_io.cpp b/test/unit/imaging/tiff_io.cpp
index 1ace38f..14f9e5f 100644
--- a/test/unit/imaging/tiff_io.cpp
+++ b/test/unit/imaging/tiff_io.cpp
@@ -1,4 +1,3 @@
-
 // disabled on windows due to https://github.com/mapnik/mapnik/issues/2838
 // TODO - get to the bottom of why including `tiff_reader.cpp` breaks windows
 // or re-write image_readers to allow `#include tiff_reader.hpp`
@@ -12,7 +11,7 @@
 #include "../../../src/tiff_reader.cpp"
 
 #define TIFF_ASSERT(filename) \
-    mapnik::tiff_reader<boost::iostreams::file_source> tiff_reader(filename); \
+    mapnik::tiff_reader<std::filebuf> tiff_reader(filename);    \
     REQUIRE( tiff_reader.width() == 256 ); \
     REQUIRE( tiff_reader.height() == 256 ); \
     REQUIRE( tiff_reader.planar_config() == PLANARCONFIG_CONTIG ); \
@@ -20,7 +19,7 @@
     REQUIRE( reader->width() == 256 ); \
     REQUIRE( reader->height() == 256 ); \
     mapnik::util::file file(filename); \
-    mapnik::tiff_reader<boost::iostreams::array_source> tiff_reader2(file.data().get(),file.size()); \
+    mapnik::tiff_reader<mapnik::util::char_array_buffer> tiff_reader2(file.data().get(),file.size()); \
     REQUIRE( tiff_reader2.width() == 256 ); \
     REQUIRE( tiff_reader2.height() == 256 ); \
     std::unique_ptr<mapnik::image_reader> reader2(mapnik::get_image_reader(file.data().get(),file.size())); \
@@ -57,11 +56,13 @@
     REQUIRE( subimage.width() == 1 ); \
     REQUIRE( subimage.height() == 1 ); \
 
-TEST_CASE("tiff io") {
+TEST_CASE("tiff io")
+{
 
-SECTION("scan rgb8 striped") {
+SECTION("scan rgb8 striped")
+{
     std::string filename("./test/data/tiff/scan_512x512_rgb8_striped.tif");
-    mapnik::tiff_reader<boost::iostreams::file_source> tiff_reader(filename);
+    mapnik::tiff_reader<std::filebuf> tiff_reader(filename);
     REQUIRE( tiff_reader.width() == 512 );
     REQUIRE( tiff_reader.height() == 512 );
     REQUIRE( tiff_reader.planar_config() == PLANARCONFIG_CONTIG );
@@ -76,7 +77,7 @@ SECTION("scan rgb8 striped") {
     REQUIRE( reader->width() == 512 );
     REQUIRE( reader->height() == 512 );
     mapnik::util::file file(filename);
-    mapnik::tiff_reader<boost::iostreams::array_source> tiff_reader2(file.data().get(),file.size());
+    mapnik::tiff_reader<mapnik::util::char_array_buffer> tiff_reader2(file.data().get(),file.size());
     REQUIRE( tiff_reader2.width() == 512 );
     REQUIRE( tiff_reader2.height() == 512 );
     std::unique_ptr<mapnik::image_reader> reader2(mapnik::get_image_reader(file.data().get(),file.size()));
@@ -91,7 +92,7 @@ SECTION("scan rgb8 striped") {
 
 SECTION("scan rgb8 tiled") {
     std::string filename("./test/data/tiff/scan_512x512_rgb8_tiled.tif");
-    mapnik::tiff_reader<boost::iostreams::file_source> tiff_reader(filename);
+    mapnik::tiff_reader<std::filebuf> tiff_reader(filename);
     REQUIRE( tiff_reader.width() == 512 );
     REQUIRE( tiff_reader.height() == 512 );
     REQUIRE( tiff_reader.planar_config() == PLANARCONFIG_CONTIG );
@@ -106,7 +107,7 @@ SECTION("scan rgb8 tiled") {
     REQUIRE( reader->width() == 512 );
     REQUIRE( reader->height() == 512 );
     mapnik::util::file file(filename);
-    mapnik::tiff_reader<boost::iostreams::array_source> tiff_reader2(file.data().get(),file.size());
+    mapnik::tiff_reader<mapnik::util::char_array_buffer> tiff_reader2(file.data().get(),file.size());
     REQUIRE( tiff_reader2.width() == 512 );
     REQUIRE( tiff_reader2.height() == 512 );
     std::unique_ptr<mapnik::image_reader> reader2(mapnik::get_image_reader(file.data().get(),file.size()));
diff --git a/test/unit/serialization/wkb_test.cpp b/test/unit/serialization/wkb_test.cpp
index f82c4f3..8a617df 100644
--- a/test/unit/serialization/wkb_test.cpp
+++ b/test/unit/serialization/wkb_test.cpp
@@ -16,6 +16,7 @@
 #include <vector>
 #include <fstream>
 
+#if BOOST_VERSION >= 105800
 namespace  {
 
 struct spatially_equal_visitor
@@ -79,6 +80,7 @@ bool spatially_equal(mapnik::geometry::geometry<T> const& g0, mapnik::geometry::
 }
 
 }
+#endif
 
 TEST_CASE("Well-known-geometries")
 {
@@ -106,7 +108,9 @@ TEST_CASE("Well-known-geometries")
             {
                 REQUIRE(wkt0 == wkt1);
                 // compare spatially (NOTE: GeometryCollection comparison also enforces strict order)
+#if BOOST_VERSION >= 105800
                 REQUIRE(spatially_equal(geom_0, geom_1));
+#endif
             }
         }
     }
diff --git a/test/visual/renderer.hpp b/test/visual/renderer.hpp
index 6b033f9..7ea9198 100644
--- a/test/visual/renderer.hpp
+++ b/test/visual/renderer.hpp
@@ -38,10 +38,21 @@
 #if defined(GRID_RENDERER)
 #include <mapnik/grid/grid_renderer.hpp>
 #endif
+
 #if defined(HAVE_CAIRO)
 #include <mapnik/cairo/cairo_renderer.hpp>
 #include <mapnik/cairo/cairo_image_util.hpp>
+#ifdef CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
 #endif
+#endif
+
 #if defined(SVG_RENDERER)
 #include <mapnik/svg/output/svg_renderer.hpp>
 #endif
@@ -53,7 +64,7 @@ namespace visual_tests
 {
 
 template <typename ImageType>
-struct renderer_base
+struct raster_renderer_base
 {
     using image_type = ImageType;
 
@@ -80,7 +91,35 @@ struct renderer_base
     }
 };
 
-struct agg_renderer : renderer_base<mapnik::image_rgba8>
+struct vector_renderer_base
+{
+    using image_type = std::string;
+
+    static constexpr const bool support_tiles = false;
+
+    unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const
+    {
+        std::ifstream stream(reference.string().c_str(), std::ios_base::in | std::ios_base::binary);
+        if (!stream)
+        {
+            throw std::runtime_error("Could not open: " + reference.string());
+        }
+        std::string expected(std::istreambuf_iterator<char>(stream.rdbuf()), std::istreambuf_iterator<char>());
+        return std::max(actual.size(), expected.size()) - std::min(actual.size(), expected.size());
+    }
+
+    void save(image_type const & image, boost::filesystem::path const& path) const
+    {
+        std::ofstream file(path.string().c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
+        if (!file)
+        {
+            throw std::runtime_error("Cannot open file for writing: " + path.string());
+        }
+        file << image;
+    }
+};
+
+struct agg_renderer : raster_renderer_base<mapnik::image_rgba8>
 {
     static constexpr const char * name = "agg";
 
@@ -94,7 +133,7 @@ struct agg_renderer : renderer_base<mapnik::image_rgba8>
 };
 
 #if defined(HAVE_CAIRO)
-struct cairo_renderer : renderer_base<mapnik::image_rgba8>
+struct cairo_renderer : raster_renderer_base<mapnik::image_rgba8>
 {
     static constexpr const char * name = "cairo";
 
@@ -111,14 +150,65 @@ struct cairo_renderer : renderer_base<mapnik::image_rgba8>
         return image;
     }
 };
+
+using surface_create_type = cairo_surface_t *(&)(cairo_write_func_t, void *, double, double);
+
+template <surface_create_type SurfaceCreateFunction>
+struct cairo_vector_renderer : vector_renderer_base
+{
+    static cairo_status_t write(void *closure,
+                                const unsigned char *data,
+                                unsigned int length)
+    {
+        std::ostringstream & ss = *reinterpret_cast<std::ostringstream*>(closure);
+        ss.write(reinterpret_cast<char const *>(data), length);
+        return ss ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
+    }
+
+    image_type render(mapnik::Map const & map, double scale_factor) const
+    {
+        std::ostringstream ss(std::stringstream::binary);
+        mapnik::cairo_surface_ptr image_surface(
+            SurfaceCreateFunction(write, &ss, map.width(), map.height()),
+            mapnik::cairo_surface_closer());
+        mapnik::cairo_ptr image_context(mapnik::create_context(image_surface));
+        mapnik::cairo_renderer<mapnik::cairo_ptr> ren(map, image_context, scale_factor);
+        ren.apply();
+        cairo_surface_finish(&*image_surface);
+        return ss.str();
+    }
+};
+
+#ifdef CAIRO_HAS_SVG_SURFACE
+struct cairo_svg_renderer : cairo_vector_renderer<cairo_svg_surface_create_for_stream>
+{
+    static constexpr const char * name = "cairo-svg";
+    static constexpr const char * ext = ".svg";
+};
+#endif
+
+#ifdef CAIRO_HAS_PS_SURFACE
+struct cairo_ps_renderer : cairo_vector_renderer<cairo_ps_surface_create_for_stream>
+{
+    static constexpr const char * name = "cairo-ps";
+    static constexpr const char * ext = ".ps";
+};
+#endif
+
+#ifdef CAIRO_HAS_PDF_SURFACE
+struct cairo_pdf_renderer : cairo_vector_renderer<cairo_pdf_surface_create_for_stream>
+{
+    static constexpr const char * name = "cairo-pdf";
+    static constexpr const char * ext = ".pdf";
+};
+#endif
 #endif
 
 #if defined(SVG_RENDERER)
-struct svg_renderer : renderer_base<std::string>
+struct svg_renderer : vector_renderer_base
 {
     static constexpr const char * name = "svg";
     static constexpr const char * ext = ".svg";
-    static constexpr const bool support_tiles = false;
 
     image_type render(mapnik::Map const & map, double scale_factor) const
     {
@@ -128,35 +218,11 @@ struct svg_renderer : renderer_base<std::string>
         ren.apply();
         return ss.str();
     }
-
-    unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const
-    {
-        std::ifstream stream(reference.string().c_str(),std::ios_base::in|std::ios_base::binary);
-        if (!stream.is_open())
-        {
-            throw std::runtime_error("could not open: '" + reference.string() + "'");
-        }
-        std::string expected(std::istreambuf_iterator<char>(stream.rdbuf()),(std::istreambuf_iterator<char>()));
-        stream.close();
-        return std::max(actual.size(), expected.size()) - std::min(actual.size(), expected.size());
-    }
-
-    void save(image_type const & image, boost::filesystem::path const& path) const
-    {
-        std::ofstream file(path.string().c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
-        if (!file) {
-            throw std::runtime_error((std::string("cannot open file for writing file ") + path.string()).c_str());
-        } else {
-            file << image;
-            file.close();
-        }
-    }
-
 };
 #endif
 
 #if defined(GRID_RENDERER)
-struct grid_renderer : renderer_base<mapnik::image_rgba8>
+struct grid_renderer : raster_renderer_base<mapnik::image_rgba8>
 {
     static constexpr const char * name = "grid";
 
@@ -335,6 +401,15 @@ private:
 using renderer_type = mapnik::util::variant<renderer<agg_renderer>
 #if defined(HAVE_CAIRO)
                                             ,renderer<cairo_renderer>
+#ifdef CAIRO_HAS_SVG_SURFACE
+                                            ,renderer<cairo_svg_renderer>
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+                                            ,renderer<cairo_ps_renderer>
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+                                            ,renderer<cairo_pdf_renderer>
+#endif
 #endif
 #if defined(SVG_RENDERER)
                                             ,renderer<svg_renderer>
diff --git a/test/visual/run.cpp b/test/visual/run.cpp
index 70284bc..5877279 100644
--- a/test/visual/run.cpp
+++ b/test/visual/run.cpp
@@ -48,30 +48,48 @@ namespace po = boost::program_options;
 
 runner::renderer_container create_renderers(po::variables_map const & args,
                                             boost::filesystem::path const & output_dir,
-                                            bool append_all = false)
+                                            bool force_append = false)
 {
     boost::filesystem::path reference_dir(args["images-dir"].as<std::string>());
     bool overwrite = args.count("overwrite");
     runner::renderer_container renderers;
 
-    if (append_all || args.count(agg_renderer::name))
+    if (force_append || args.count(agg_renderer::name))
     {
         renderers.emplace_back(renderer<agg_renderer>(output_dir, reference_dir, overwrite));
     }
 #if defined(HAVE_CAIRO)
-    if (append_all || args.count(cairo_renderer::name))
+    if (force_append || args.count(cairo_renderer::name))
     {
         renderers.emplace_back(renderer<cairo_renderer>(output_dir, reference_dir, overwrite));
     }
+#ifdef CAIRO_HAS_SVG_SURFACE
+    if (args.count(cairo_svg_renderer::name))
+    {
+        renderers.emplace_back(renderer<cairo_svg_renderer>(output_dir, reference_dir, overwrite));
+    }
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+    if (args.count(cairo_ps_renderer::name))
+    {
+        renderers.emplace_back(renderer<cairo_ps_renderer>(output_dir, reference_dir, overwrite));
+    }
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+    if (args.count(cairo_pdf_renderer::name))
+    {
+        renderers.emplace_back(renderer<cairo_pdf_renderer>(output_dir, reference_dir, overwrite));
+    }
+#endif
 #endif
 #if defined(SVG_RENDERER)
-    if (append_all || args.count(svg_renderer::name))
+    if (force_append || args.count(svg_renderer::name))
     {
         renderers.emplace_back(renderer<svg_renderer>(output_dir, reference_dir, overwrite));
     }
 #endif
 #if defined(GRID_RENDERER)
-    if (append_all || args.count(grid_renderer::name))
+    if (force_append || args.count(grid_renderer::name))
     {
         renderers.emplace_back(renderer<grid_renderer>(output_dir, reference_dir, overwrite));
     }
@@ -112,6 +130,15 @@ int main(int argc, char** argv)
         (agg_renderer::name, "render with AGG renderer")
 #if defined(HAVE_CAIRO)
         (cairo_renderer::name, "render with Cairo renderer")
+#ifdef CAIRO_HAS_SVG_SURFACE
+        (cairo_svg_renderer::name, "render with Cairo SVG renderer")
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+        (cairo_ps_renderer::name, "render with Cairo PS renderer")
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+        (cairo_pdf_renderer::name, "render with Cairo PDF renderer")
+#endif
 #endif
 #if defined(SVG_RENDERER)
         (svg_renderer::name, "render with SVG renderer")
@@ -183,7 +210,7 @@ int main(int argc, char** argv)
     }
     catch (std::exception & e)
     {
-        std::cerr << "Error runnig tests: " << e.what() << std::endl;
+        std::cerr << "Error running tests: " << e.what() << std::endl;
         return 1;
     }
 
diff --git a/utils/mapnik-index/mapnik-index.cpp b/utils/mapnik-index/mapnik-index.cpp
index 3e1b8bb..9b1859b 100644
--- a/utils/mapnik-index/mapnik-index.cpp
+++ b/utils/mapnik-index/mapnik-index.cpp
@@ -180,7 +180,11 @@ int main (int argc, char** argv)
         {
             std::clog << "processing '" << filename << "' as CSV\n";
             auto result = mapnik::detail::process_csv_file(boxes, filename, manual_headers, separator, quote);
-            if (!result.first) continue;
+            if (!result.first)
+            {
+                std::clog << "Error: failed to process " << filename << std::endl;
+                return EXIT_FAILURE;
+            }
             extent = result.second;
         }
         else if (mapnik::detail::is_geojson(filename))
@@ -190,7 +194,7 @@ int main (int argc, char** argv)
             if (!result.first)
             {
                 std::clog << "Error: failed to process " << filename << std::endl;
-                continue;
+                return EXIT_FAILURE;
             }
             extent = result.second;
         }
@@ -224,6 +228,11 @@ int main (int argc, char** argv)
                 file.close();
             }
         }
+        else
+        {
+            std::clog << "Invalid extent " << extent << std::endl;
+            return EXIT_FAILURE;
+        }
     }
     std::clog << "done!" << std::endl;
     return EXIT_SUCCESS;
diff --git a/utils/mapnik-index/process_csv_file.cpp b/utils/mapnik-index/process_csv_file.cpp
index b9834ce..a0342cf 100644
--- a/utils/mapnik-index/process_csv_file.cpp
+++ b/utils/mapnik-index/process_csv_file.cpp
@@ -46,6 +46,7 @@ namespace mapnik { namespace detail {
 template <typename T>
 std::pair<bool,typename T::value_type::first_type> process_csv_file(T & boxes, std::string const& filename, std::string const& manual_headers, char separator, char quote)
 {
+    using box_type = typename T::value_type::first_type;
     csv_utils::csv_file_parser p;
     p.manual_headers_ = manual_headers;
     p.separator_ = separator;
@@ -65,7 +66,7 @@ std::pair<bool,typename T::value_type::first_type> process_csv_file(T & boxes, s
     else
     {
         std::clog << "Error : cannot mmap " << filename << std::endl;
-        return std::make_pair(false, box2d<float>(p.extent_));
+        return std::make_pair(false, box_type(p.extent_));
     }
 #else
  #if defined(_WINDOWS)
@@ -76,18 +77,18 @@ std::pair<bool,typename T::value_type::first_type> process_csv_file(T & boxes, s
     if (!csv_file.is_open())
     {
         std::clog << "Error : cannot open " << filename << std::endl;
-        return std::make_pair(false, box2d<float>(p.extent_));
+        return std::make_pair(false, box_type(p.extent_));
     }
 #endif
     try
     {
         p.parse_csv_and_boxes(csv_file, boxes);
-        return std::make_pair(true, box2d<float>(p.extent_));
+        return std::make_pair(true, box_type(p.extent_));
     }
     catch (std::exception const& ex)
     {
         std::clog << ex.what() << std::endl;
-        return std::make_pair(false, box2d<float>(p.extent_));
+        return std::make_pair(false, box_type(p.extent_));
     }
 }
 
diff --git a/utils/mapnik-render/mapnik-render.cpp b/utils/mapnik-render/mapnik-render.cpp
index c399226..c172995 100644
--- a/utils/mapnik-render/mapnik-render.cpp
+++ b/utils/mapnik-render/mapnik-render.cpp
@@ -21,7 +21,7 @@ int main (int argc,char** argv)
     namespace po = boost::program_options;
 
     bool verbose = false;
-    bool auto_open = true;
+    bool auto_open = false;
     int return_value = 0;
     std::string xml_file;
     std::string img_file;
@@ -37,7 +37,7 @@ int main (int argc,char** argv)
             ("help,h", "produce usage message")
             ("version,V","print version string")
             ("verbose,v","verbose output")
-            ("open","automatically open the file after rendering (os x only)")
+            ("open","automatically open the file after rendering")
             ("xml",po::value<std::string>(),"xml map to read")
             ("img",po::value<std::string>(),"image to render")
             ("scale-factor",po::value<double>(),"scale factor for rendering")
diff --git a/utils/pgsql2sqlite/pgsql2sqlite.hpp b/utils/pgsql2sqlite/pgsql2sqlite.hpp
index e339a8f..9de8867 100644
--- a/utils/pgsql2sqlite/pgsql2sqlite.hpp
+++ b/utils/pgsql2sqlite/pgsql2sqlite.hpp
@@ -352,23 +352,23 @@ void pgsql2sqlite(Connection conn,
                     break;
                 }
                 case 23:
-                    output_rec.push_back(sqlite::value_type(int4net(buf)));
+                    output_rec.emplace_back(int4net(buf));
                     break;
                 case 21:
-                    output_rec.push_back(sqlite::value_type(int2net(buf)));
+                    output_rec.emplace_back(int(int2net(buf)));
                     break;
                 case 700:
                 {
                     float val;
                     float4net(val,buf);
-                    output_rec.push_back(sqlite::value_type(val));
+                    output_rec.emplace_back(double(val));
                     break;
                 }
                 case 701:
                 {
                     double val;
                     float8net(val,buf);
-                    output_rec.push_back(sqlite::value_type(val));
+                    output_rec.emplace_back(val);
                     break;
                 }
                 case 1700:
@@ -377,7 +377,7 @@ void pgsql2sqlite(Connection conn,
                     double val;
                     if (mapnik::util::string2double(str,val))
                     {
-                        output_rec.push_back(sqlite::value_type(val));
+                        output_rec.emplace_back(val);
                     }
                     break;
                 }

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



More information about the Pkg-grass-devel mailing list