[mapnik] 01/07: Imported Upstream version 3.0.6+ds

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Sat Oct 10 15:10:28 UTC 2015


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

sebastic pushed a commit to branch master
in repository mapnik.

commit fd3c511f3160a5d6e7c0bb36152cb4c45178bd31
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Oct 10 11:48:40 2015 +0200

    Imported Upstream version 3.0.6+ds
---
 .gitattributes                                     |    1 +
 .gitignore                                         |    1 +
 .travis.yml                                        |   12 +-
 CHANGELOG.md                                       |   18 +-
 INSTALL.md                                         |    3 -
 README.md                                          |    4 +-
 SConstruct                                         | 1961 ++++++++++----------
 appveyor.yml                                       |   26 +
 benchmark/build.py                                 |    1 +
 benchmark/run                                      |    2 +-
 benchmark/test_getline.cpp                         |  125 ++
 benchmark/test_rendering.cpp                       |    2 +-
 bootstrap.sh                                       |   22 +-
 demo/viewer/mapwidget.cpp                          |    2 +-
 demo/viewer/viewer.pro                             |    1 +
 include/mapnik/css_color_grammar.hpp               |    1 +
 include/mapnik/css_color_grammar_impl.hpp          |    9 +-
 include/mapnik/csv/csv_grammar.hpp                 |   58 +-
 include/mapnik/expression_grammar.hpp              |    1 +
 include/mapnik/expression_grammar_impl.hpp         |    8 +
 include/mapnik/feature.hpp                         |    3 -
 include/mapnik/feature_style_processor.hpp         |    2 +-
 include/mapnik/feature_style_processor_impl.hpp    |   31 +-
 include/mapnik/geometry_is_simple.hpp              |   47 +-
 include/mapnik/geometry_is_valid.hpp               |  178 +-
 include/mapnik/image_any.hpp                       |    4 +-
 include/mapnik/jpeg_io.hpp                         |   10 +-
 include/mapnik/json/error_handler.hpp              |    2 +-
 .../mapnik/json/extract_bounding_box_grammar.hpp   |   14 +-
 include/mapnik/quad_tree.hpp                       |  172 +-
 .../renderer_common/process_group_symbolizer.hpp   |    4 +-
 include/mapnik/simplify_converter.hpp              |    4 +-
 include/mapnik/text/placements/base.hpp            |    4 +-
 include/mapnik/text/placements/dummy.hpp           |    6 +-
 include/mapnik/text/placements/simple.hpp          |    4 +-
 include/mapnik/util/singleton.hpp                  |    9 +
 include/mapnik/util/spatial_index.hpp              |  155 ++
 include/mapnik/value.hpp                           |   33 +-
 include/mapnik/version.hpp                         |    2 +-
 include/mapnik/webp_io.hpp                         |    6 +-
 plugins/input/csv/build.py                         |    7 +-
 plugins/input/csv/csv_datasource.cpp               |  250 ++-
 plugins/input/csv/csv_datasource.hpp               |   14 +-
 plugins/input/csv/csv_featureset.cpp               |    5 +-
 plugins/input/csv/csv_featureset.hpp               |   11 +-
 plugins/input/csv/csv_index_featureset.cpp         |  130 ++
 ...csv_featureset.hpp => csv_index_featureset.hpp} |   52 +-
 plugins/input/csv/csv_inline_featureset.cpp        |    6 +-
 plugins/input/csv/csv_inline_featureset.hpp        |   14 +-
 plugins/input/csv/csv_utils.hpp                    |   74 +-
 plugins/input/gdal/build.py                        |    2 +-
 plugins/input/geojson/build.py                     |    6 +-
 plugins/input/geojson/geojson_datasource.cpp       |    6 +-
 plugins/input/ogr/build.py                         |    2 +-
 plugins/input/pgraster/build.py                    |    5 +-
 plugins/input/postgis/build.py                     |    4 +-
 plugins/input/postgis/postgis_datasource.cpp       |   22 +-
 plugins/input/postgis/postgis_datasource.hpp       |    1 +
 plugins/input/postgis/postgis_featureset.cpp       |   14 +-
 plugins/input/postgis/postgis_featureset.hpp       |    4 +-
 plugins/input/raster/build.py                      |    6 +-
 plugins/input/raster/raster_datasource.cpp         |    1 +
 plugins/input/shape/build.py                       |    6 +-
 plugins/input/shape/shape_index_featureset.cpp     |   21 +-
 plugins/input/shape/shape_index_featureset.hpp     |    4 +-
 plugins/input/shape/shp_index.hpp                  |  103 -
 plugins/input/sqlite/build.py                      |    6 +-
 plugins/input/topojson/build.py                    |    6 +-
 scripts/build-appveyor.bat                         |   70 +
 scripts/build-local.bat                            |   32 +
 scripts/parse-commit-message.ps1                   |    5 +
 src/cairo/process_debug_symbolizer.cpp             |    1 -
 src/debug.cpp                                      |   16 +-
 src/image_util.cpp                                 |    1 -
 src/svg/svg_parser.cpp                             |   54 +-
 src/text/placements/simple.cpp                     |    1 +
 test/standalone/csv_test.cpp                       |   16 +-
 test/standalone/datasource_registration_test.cpp   |   32 +-
 test/unit/color/css_color.cpp                      |  131 +-
 test/unit/core/comparison_test.cpp                 |   49 +
 test/unit/datasource/spatial_index.cpp             |   90 +
 test/unit/geometry/geometry_is_simple.cpp          |  320 ++++
 test/unit/geometry/geometry_is_valid.cpp           |  431 ++++-
 test/unit/imaging/image_filter.cpp                 |   46 +
 test/unit/serialization/wkb_formats_test.cpp       |   12 +-
 test/unit/svg/svg_parser_test.cpp                  |   20 +
 test/visual/renderer.hpp                           |   17 +-
 test/visual/report.cpp                             |   54 +-
 test/visual/report.hpp                             |    2 +-
 test/visual/run.cpp                                |   66 +-
 test/visual/runner.cpp                             |   27 +-
 test/visual/runner.hpp                             |   29 +-
 utils/mapnik-index/build.py                        |   59 +
 utils/mapnik-index/mapnik-index.cpp                |  353 ++++
 utils/pgsql2sqlite/pgsql2sqlite.hpp                |    2 +-
 utils/shapeindex/quadtree.hpp                      |  297 ---
 utils/shapeindex/shapeindex.cpp                    |    8 +-
 97 files changed, 4083 insertions(+), 1888 deletions(-)

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..4976173
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.svg text eol=lf
diff --git a/.gitignore b/.gitignore
index 816bc0e..ddd8581 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ tests/python_tests/raster_colorizer_test.png
 tests/python_tests/raster_colorizer_test_save.xml
 utils/mapnik-config/mapnik-config
 utils/shapeindex/shapeindex
+utils/mapnik-index/mapnik-index
 utils/ogrindex/ogrindex
 utils/pgsql2sqlite/pgsql2sqlite
 utils/svg2png/svg2png
diff --git a/.travis.yml b/.travis.yml
index b9493a9..c0e2afb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,12 +31,12 @@ matrix:
     - os: linux
       compiler: gcc
       env: JOBS=6
-    #- os: osx
-    #  compiler: clang
-    #  env: JOBS=8 MASON_PUBLISH=true
-    #- os: osx
-    #  compiler: clang
-    #  env: JOBS=8 COVERAGE=true
+    - os: osx
+      compiler: clang
+      env: JOBS=8 MASON_PUBLISH=true
+    - os: osx
+      compiler: clang
+      env: JOBS=8 COVERAGE=true
 
 before_install:
  - export COVERAGE=${COVERAGE:-false}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 538d4ca..d4ecd27 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,11 +6,23 @@ Developers: Please commit along with changes.
 
 For a complete change history, see the git log.
 
-## Future
+## 3.0.6
 
-Released: YYYY XX, 2015
+Released: October 7, 2015
 
-(Packaged from xxxx)
+(Packaged from 3cebe97)
+
+#### Summary
+
+- PostGIS plugin: added `key_field_as_attribute` option. Defaults to `True` to preserve current behavior of having the `key_field` added both
+  as an attribute and as the `feature.id` value. If `key_field_as_attribute=false` is passed then the attribute is discarded (https://github.com/mapnik/mapnik/issues/3115)
+- CSV plugin has been further optimized and has gained experimental support for on-disk indexes (https://github.com/mapnik/mapnik/issues/3089)
+- SVG parser now fallsback to using `viewbox` if explicit dimensions are lacking (https://github.com/mapnik/mapnik/issues/3081)
+- Visual tests: new command line arguments `--agg`, `--cairo`, `--svg`, `--grid` for selecting renderers (https://github.com/mapnik/mapnik/pull/3074)
+- Visual tests: new command line argument `--scale-factor` or abbreviated `-s` for setting scale factor (https://github.com/mapnik/mapnik/pull/3074)
+- Fixed parsing colors in hexadecimal notation (https://github.com/mapnik/mapnik/pull/3075)
+- Removed mapnik::Feature type alias of mapnik::feature_impl (https://github.com/mapnik/mapnik/pull/3099)
+- Fixed linking order for plugins to avoid possible linking errors on linux systems (https://github.com/mapnik/mapnik/issues/3105)
 
 ## 3.0.5
 
diff --git a/INSTALL.md b/INSTALL.md
index b88abd2..229a315 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -94,9 +94,6 @@ Additional optional dependencies:
     - pg_config - PostgreSQL installation capabilities
  * libgdal - GDAL/OGR input (For gdal and ogr plugin support)
  * libsqlite3 - SQLite input (needs RTree support builtin) (sqlite plugin support)
- * libocci - Oracle input plugin support
- * libcurl - OSM input plugin support
- * librasterlite - Rasterlite input plugin support
 
 Instructions for installing many of these dependencies on
 various platforms can be found at the Mapnik Wiki:
diff --git a/README.md b/README.md
index dcb049e..4d84bb4 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,9 @@ _/      _/    _/_/_/  _/_/_/    _/    _/  _/  _/    _/
                     _/
 ```
 
-[![Build Status](https://secure.travis-ci.org/mapnik/mapnik.png)](http://travis-ci.org/mapnik/mapnik)
+[![Build Status Linux](https://secure.travis-ci.org/mapnik/mapnik.png)](http://travis-ci.org/mapnik/mapnik)
+[![Build status Windows](https://ci.appveyor.com/api/projects/status/hc9l7okdjtucfqqn?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)
 
 Mapnik is an open source toolkit for developing mapping applications. At the core is a C++ shared library providing algorithms and patterns for spatial data access and visualization.
 
diff --git a/SConstruct b/SConstruct
index b1c7f30..0474561 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,6 +1,6 @@
 # This file is part of Mapnik (c++ mapping toolkit)
 #
-# Copyright (C) 2013 Artem Pavlenko
+# Copyright (C) 2015 Artem Pavlenko
 #
 # Mapnik is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -105,28 +105,28 @@ pretty_dep_names = {
 # Core plugin build configuration
 # opts.AddVariables still hardcoded however...
 PLUGINS = { # plugins with external dependencies
-            # configured by calling project, hence 'path':None
-            'postgis': {'default':True,'path':None,'inc':'libpq-fe.h','lib':'pq','lang':'C'},
-            'pgraster': {'default':True,'path':None,'inc':'libpq-fe.h','lib':'pq','lang':'C'},
-            'gdal':    {'default':True,'path':None,'inc':'gdal_priv.h','lib':'gdal','lang':'C++'},
-            'ogr':     {'default':True,'path':None,'inc':'ogrsf_frmts.h','lib':'gdal','lang':'C++'},
-            'sqlite':  {'default':True,'path':'SQLITE','inc':'sqlite3.h','lib':'sqlite3','lang':'C'},
-            # plugins without external dependencies requiring CheckLibWithHeader...
-            'shape':   {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
-            'csv':     {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
-            'raster':  {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
-            'geojson': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
-            'topojson':{'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}
-            }
+	    # configured by calling project, hence 'path':None
+	    'postgis': {'default':True,'path':None,'inc':'libpq-fe.h','lib':'pq','lang':'C'},
+	    'pgraster': {'default':True,'path':None,'inc':'libpq-fe.h','lib':'pq','lang':'C'},
+	    'gdal':    {'default':True,'path':None,'inc':'gdal_priv.h','lib':'gdal','lang':'C++'},
+	    'ogr':     {'default':True,'path':None,'inc':'ogrsf_frmts.h','lib':'gdal','lang':'C++'},
+	    'sqlite':  {'default':True,'path':'SQLITE','inc':'sqlite3.h','lib':'sqlite3','lang':'C'},
+	    # plugins without external dependencies requiring CheckLibWithHeader...
+	    'shape':   {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
+	    'csv':     {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
+	    'raster':  {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
+	    'geojson': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'},
+	    'topojson':{'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}
+	    }
 
 
 def init_environment(env):
     env.Decider('MD5-timestamp')
     env.SourceCode(".", None)
     if os.environ.get('RANLIB'):
-        env['RANLIB'] = os.environ['RANLIB']
+	env['RANLIB'] = os.environ['RANLIB']
     if os.environ.get('AR'):
-        env['AR'] = os.environ['AR']
+	env['AR'] = os.environ['AR']
 
 #### SCons build options and initial setup ####
 env = Environment(ENV=os.environ)
@@ -142,58 +142,58 @@ def color_print(color,text,newline=True):
     # 4 - blue
     text = "\033[9%sm%s\033[0m" % (color,text)
     if not newline:
-        print text,
+	print text,
     else:
-        print text
+	print text
 
 def regular_print(color,text,newline=True):
     if not newline:
-        print text,
+	print text,
     else:
-        print text
+	print text
 
 def call(cmd, silent=False):
     stdin, stderr = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate()
     if not stderr:
-        return stdin.strip()
+	return stdin.strip()
     elif not silent:
-        color_print(1,'Problem encounted with SCons scripts, please post bug report to: https://github.com/mapnik/mapnik/issues \nError was: %s' % stderr)
+	color_print(1,'Problem encounted with SCons scripts, please post bug report to: https://github.com/mapnik/mapnik/issues \nError was: %s' % stderr)
 
 def strip_first(string,find,replace=''):
     if string.startswith(find):
-        return string.replace(find,replace,1)
+	return string.replace(find,replace,1)
     return string
 
 # http://www.scons.org/wiki/InstallTargets
 def create_uninstall_target(env, path, is_glob=False):
     if 'uninstall' in COMMAND_LINE_TARGETS:
-        if is_glob:
-            all_files = Glob(path,strings=True)
-            for filei in all_files:
-                env.Command( "uninstall-"+filei, filei,
-                [
-                Delete("$SOURCE"),
-                ])
-                env.Alias("uninstall", "uninstall-"+filei)
-        else:
-            if os.path.exists(path):
-                env.Command( "uninstall-"+path, path,
-                [
-                Delete("$SOURCE"),
-                ])
-                env.Alias("uninstall", "uninstall-"+path)
+	if is_glob:
+	    all_files = Glob(path,strings=True)
+	    for filei in all_files:
+		env.Command( "uninstall-"+filei, filei,
+		[
+		Delete("$SOURCE"),
+		])
+		env.Alias("uninstall", "uninstall-"+filei)
+	else:
+	    if os.path.exists(path):
+		env.Command( "uninstall-"+path, path,
+		[
+		Delete("$SOURCE"),
+		])
+		env.Alias("uninstall", "uninstall-"+path)
 
 def shortest_name(libs):
     name = '-'*200
     for lib in libs:
-        if len(name) > len(lib):
-            name = lib
+	if len(name) > len(lib):
+	    name = lib
     return name
 
 def rm_path(item,set,_env):
     for i in _env[set]:
-        if i.startswith(item):
-            _env[set].remove(i)
+	if i.startswith(item):
+	    _env[set].remove(i)
 
 def sort_paths(items,priority):
     """Sort paths such that compiling and linking will globally prefer custom or local libs
@@ -217,57 +217,57 @@ def sort_paths(items,priority):
     # parse types of paths into logical/meaningful groups
     # based on commonly encountered lib directories on linux and osx
     for i in items:
-        # internal paths for code kept inside
-        # the mapnik sources
-        if i.startswith('#'):
-            path_types['internal'].append(i)
-        # Mac OS X user installed frameworks
-        elif '/Library/Frameworks' in i:
-            path_types['frameworks'].append(i)
-        # various 'local' installs like /usr/local or /opt/local
-        elif 'local' in i or '/sw' in i:
-            if '/usr/local' in i:
-                path_types['user'].insert(0,i)
-            else:
-                path_types['user'].append(i)
-        # key system libs (likely others will fall into 'other')
-        elif '/usr/' in i or '/System' in i or i.startswith('/lib'):
-            path_types['system'].append(i)
-        # anything not yet matched...
-        # likely a combo of rare system lib paths and
-        # very custom user paths that should ideally be
-        # in 'user'
-        else:
-            path_types['other'].append(i)
+	# internal paths for code kept inside
+	# the mapnik sources
+	if i.startswith('#'):
+	    path_types['internal'].append(i)
+	# Mac OS X user installed frameworks
+	elif '/Library/Frameworks' in i:
+	    path_types['frameworks'].append(i)
+	# various 'local' installs like /usr/local or /opt/local
+	elif 'local' in i or '/sw' in i:
+	    if '/usr/local' in i:
+		path_types['user'].insert(0,i)
+	    else:
+		path_types['user'].append(i)
+	# key system libs (likely others will fall into 'other')
+	elif '/usr/' in i or '/System' in i or i.startswith('/lib'):
+	    path_types['system'].append(i)
+	# anything not yet matched...
+	# likely a combo of rare system lib paths and
+	# very custom user paths that should ideally be
+	# in 'user'
+	else:
+	    path_types['other'].append(i)
     # build up new list based on priority list
     for path in priority:
-        if path_types.has_key(path):
-            dirs = path_types[path]
-            new.extend(dirs)
-            path_types.pop(path)
-        else:
-            color_print(1,'\nSorry, "%s" is NOT a valid value for option "LINK_PRIORITY": values include: %s' % (path,','.join(path_types.keys())))
-            color_print(1,'\tinternal: the local directory of the Mapnik sources (prefix #) (eg. used to link internal agg)')
-            color_print(1,'\tframeworks: on osx the /Library/Frameworks directory')
-            color_print(1,'\tuser: any path with "local" or "/sw" inside it')
-            color_print(1,'\tsystem: any path not yet matched with "/usr/","/lib", or "/System" (osx) inside it')
-            color_print(1,'\tother: any paths you specified not matched by criteria used to parse the others')
-            color_print(1,'\tother: any paths you specified not matched by criteria used to parse the others')
-            color_print(1,'The Default priority is: %s' % ','.join(DEFAULT_LINK_PRIORITY))
-            color_print(1,'Any priority groups not listed will be appended to the list at the end')
-            Exit(1)
+	if path_types.has_key(path):
+	    dirs = path_types[path]
+	    new.extend(dirs)
+	    path_types.pop(path)
+	else:
+	    color_print(1,'\nSorry, "%s" is NOT a valid value for option "LINK_PRIORITY": values include: %s' % (path,','.join(path_types.keys())))
+	    color_print(1,'\tinternal: the local directory of the Mapnik sources (prefix #) (eg. used to link internal agg)')
+	    color_print(1,'\tframeworks: on osx the /Library/Frameworks directory')
+	    color_print(1,'\tuser: any path with "local" or "/sw" inside it')
+	    color_print(1,'\tsystem: any path not yet matched with "/usr/","/lib", or "/System" (osx) inside it')
+	    color_print(1,'\tother: any paths you specified not matched by criteria used to parse the others')
+	    color_print(1,'\tother: any paths you specified not matched by criteria used to parse the others')
+	    color_print(1,'The Default priority is: %s' % ','.join(DEFAULT_LINK_PRIORITY))
+	    color_print(1,'Any priority groups not listed will be appended to the list at the end')
+	    Exit(1)
     # append remaining paths potentially not requested
     # by any custom priority list defined by user
     for k,v in path_types.items():
-        new.extend(v)
+	new.extend(v)
     return new
 
 def pretty_dep(dep):
     pretty = pretty_dep_names.get(dep)
     if pretty:
-        return '%s (%s)' % (dep,pretty)
+	return '%s (%s)' % (dep,pretty)
     elif 'boost' in dep:
-        return '%s (%s)' % (dep,'more info see: https://github.com/mapnik/mapnik/wiki/Mapnik-Installation & http://www.boost.org')
+	return '%s (%s)' % (dep,'more info see: https://github.com/mapnik/mapnik/wiki/Mapnik-Installation & http://www.boost.org')
     return dep
 
 
@@ -400,6 +400,7 @@ opts.AddVariables(
     BoolVariable('DEMO', 'Compile demo c++ application', 'True'),
     BoolVariable('PGSQL2SQLITE', 'Compile and install a utility to convert postgres tables to sqlite', 'False'),
     BoolVariable('SHAPEINDEX', 'Compile and install a utility to generate shapefile indexes in the custom format (.index) Mapnik supports', 'True'),
+    BoolVariable('MAPNIK_INDEX', 'Compile and install a utility to generate file indexes for CSV and GeoJSON in the custom format (.index) Mapnik supports', 'True'),
     BoolVariable('SVG2PNG', 'Compile and install a utility to generate render an svg file to a png on the command line', 'False'),
     BoolVariable('NIK2IMG', 'Compile and install a utility to generate render a map to an image', 'True'),
     BoolVariable('COLOR_PRINT', 'Print build status information in color', 'True'),
@@ -410,74 +411,74 @@ opts.AddVariables(
 # these include all scons core variables as well as custom
 # env variables needed in SConscript files
 pickle_store = [# Scons internal variables
-        'CC', # compiler user to check if c deps compile during configure
-        'CXX', # C++ compiler to compile mapnik
-        'CFLAGS',
-        'CPPDEFINES',
-        'CPPFLAGS', # c preprocessor flags
-        'CPPPATH',
-        'CXXFLAGS', # C++ flags built up during configure
-        'LIBPATH',
-        'LIBS',
-        'LINKFLAGS',
-        'CUSTOM_LDFLAGS', # user submitted
-        'CUSTOM_DEFINES', # user submitted
-        'CUSTOM_CXXFLAGS', # user submitted
-        'CUSTOM_CFLAGS', # user submitted
-        'MAPNIK_LIB_NAME',
-        'LINK',
-        'RUNTIME_LINK',
-        # Mapnik's SConstruct build variables
-        'PLUGINS',
-        'ABI_VERSION',
-        'MAPNIK_VERSION_STRING',
-        'MAPNIK_VERSION',
-        'PLATFORM',
-        'BOOST_ABI',
-        'BOOST_APPEND',
-        'LIBDIR_SCHEMA',
-        'REQUESTED_PLUGINS',
-        'COLOR_PRINT',
-        'HAS_CAIRO',
-        'MAPNIK_HAS_DLFCN',
-        'HAS_PYCAIRO',
-        'PYCAIRO_PATHS',
-        'HAS_LIBXML2',
-        'PKG_CONFIG_PATH',
-        'PATH',
-        'PATH_REMOVE',
-        'PATH_REPLACE',
-        'MAPNIK_LIB_DIR',
-        'MAPNIK_LIB_DIR_DEST',
-        'INSTALL_PREFIX',
-        'MAPNIK_INPUT_PLUGINS',
-        'MAPNIK_INPUT_PLUGINS_DEST',
-        'MAPNIK_FONTS',
-        'MAPNIK_FONTS_DEST',
-        'MAPNIK_BUNDLED_SHARE_DIRECTORY',
-        'MAPNIK_LIB_BASE',
-        'MAPNIK_LIB_BASE_DEST',
-        'EXTRA_FREETYPE_LIBS',
-        'LIBMAPNIK_CPPATHS',
-        'LIBMAPNIK_DEFINES',
-        'LIBMAPNIK_CXXFLAGS',
-        'CAIRO_LIBPATHS',
-        'CAIRO_ALL_LIBS',
-        'CAIRO_CPPPATHS',
-        'GRID_RENDERER',
-        'SVG_RENDERER',
-        'SQLITE_LINKFLAGS',
-        'BOOST_LIB_VERSION_FROM_HEADER',
-        'BIGINT',
-        'HOST'
-        ]
+	'CC', # compiler user to check if c deps compile during configure
+	'CXX', # C++ compiler to compile mapnik
+	'CFLAGS',
+	'CPPDEFINES',
+	'CPPFLAGS', # c preprocessor flags
+	'CPPPATH',
+	'CXXFLAGS', # C++ flags built up during configure
+	'LIBPATH',
+	'LIBS',
+	'LINKFLAGS',
+	'CUSTOM_LDFLAGS', # user submitted
+	'CUSTOM_DEFINES', # user submitted
+	'CUSTOM_CXXFLAGS', # user submitted
+	'CUSTOM_CFLAGS', # user submitted
+	'MAPNIK_LIB_NAME',
+	'LINK',
+	'RUNTIME_LINK',
+	# Mapnik's SConstruct build variables
+	'PLUGINS',
+	'ABI_VERSION',
+	'MAPNIK_VERSION_STRING',
+	'MAPNIK_VERSION',
+	'PLATFORM',
+	'BOOST_ABI',
+	'BOOST_APPEND',
+	'LIBDIR_SCHEMA',
+	'REQUESTED_PLUGINS',
+	'COLOR_PRINT',
+	'HAS_CAIRO',
+	'MAPNIK_HAS_DLFCN',
+	'HAS_PYCAIRO',
+	'PYCAIRO_PATHS',
+	'HAS_LIBXML2',
+	'PKG_CONFIG_PATH',
+	'PATH',
+	'PATH_REMOVE',
+	'PATH_REPLACE',
+	'MAPNIK_LIB_DIR',
+	'MAPNIK_LIB_DIR_DEST',
+	'INSTALL_PREFIX',
+	'MAPNIK_INPUT_PLUGINS',
+	'MAPNIK_INPUT_PLUGINS_DEST',
+	'MAPNIK_FONTS',
+	'MAPNIK_FONTS_DEST',
+	'MAPNIK_BUNDLED_SHARE_DIRECTORY',
+	'MAPNIK_LIB_BASE',
+	'MAPNIK_LIB_BASE_DEST',
+	'EXTRA_FREETYPE_LIBS',
+	'LIBMAPNIK_CPPATHS',
+	'LIBMAPNIK_DEFINES',
+	'LIBMAPNIK_CXXFLAGS',
+	'CAIRO_LIBPATHS',
+	'CAIRO_ALL_LIBS',
+	'CAIRO_CPPPATHS',
+	'GRID_RENDERER',
+	'SVG_RENDERER',
+	'SQLITE_LINKFLAGS',
+	'BOOST_LIB_VERSION_FROM_HEADER',
+	'BIGINT',
+	'HOST'
+	]
 
 # Add all other user configurable options to pickle pickle_store
 # We add here more options than are needed for the build stage
 # but helpful so that scons -h shows the exact cached options
 for opt in opts.options:
     if opt.key not in pickle_store:
-        pickle_store.append(opt.key)
+	pickle_store.append(opt.key)
 
 # Method of adding configure behavior to Scons adapted from:
 # http://freeorion.svn.sourceforge.net/svnroot/freeorion/trunk/FreeOrion/SConstruct
@@ -509,24 +510,24 @@ opts.Update(env)
 # if we are not configuring overwrite environment with pickled settings
 if not force_configure:
     if os.path.exists(SCONS_CONFIGURE_CACHE):
-        try:
-            pickled_environment = open(SCONS_CONFIGURE_CACHE, 'r')
-            pickled_values = pickle.load(pickled_environment)
-            for key, value in pickled_values.items():
-                env[key] = value
-            preconfigured = True
-        except:
-            preconfigured = False
+	try:
+	    pickled_environment = open(SCONS_CONFIGURE_CACHE, 'r')
+	    pickled_values = pickle.load(pickled_environment)
+	    for key, value in pickled_values.items():
+		env[key] = value
+	    preconfigured = True
+	except:
+	    preconfigured = False
     else:
-        preconfigured = False
+	preconfigured = False
 
 # check for missing keys in pickled settings
 # which can occur when keys are added or changed between
 # rebuilds, e.g. for folks following trunk
 for opt in pickle_store:
     if not opt in env:
-        #print 'missing opt', opt
-        preconfigured = False
+	#print 'missing opt', opt
+	preconfigured = False
 
 # if custom arguments are supplied make sure to accept them
 if opts.args:
@@ -539,8 +540,8 @@ if opts.args:
 
 elif preconfigured:
     if not HELP_REQUESTED:
-        color_print(4,'Using previous successful configuration...')
-        color_print(4,'Re-configure by running "python scons/scons.py configure".')
+	color_print(4,'Using previous successful configuration...')
+	color_print(4,'Re-configure by running "python scons/scons.py configure".')
 
 if env.has_key('COLOR_PRINT') and env['COLOR_PRINT'] == False:
     color_print = regular_print
@@ -556,11 +557,11 @@ def prioritize_paths(context,silent=True):
     env = context.env
     prefs = env['LINK_PRIORITY'].split(',')
     if not silent:
-        context.Message( 'Sorting lib and inc compiler paths...')
+	context.Message( 'Sorting lib and inc compiler paths...')
     env['LIBPATH'] = sort_paths(env['LIBPATH'],prefs)
     env['CPPPATH'] = sort_paths(env['CPPPATH'],prefs)
     if silent:
-        context.did_show_result=1
+	context.did_show_result=1
     ret = context.Result( True )
     return ret
 
@@ -587,42 +588,42 @@ def parse_config(context, config, checks='--libs --cflags'):
     tool = config.lower().replace('_','-')
     toolname = tool
     if config in ('GDAL_CONFIG'):
-        toolname += ' %s' % checks
+	toolname += ' %s' % checks
     context.Message( 'Checking for %s... ' % toolname)
     cmd = '%s %s' % (env[config],checks)
     ret = context.TryAction(cmd)[0]
     parsed = False
     if ret:
-        try:
-            if 'gdal-config' in cmd:
-                env.ParseConfig(cmd)
-                # hack for potential -framework GDAL syntax
-                # which will not end up being added to env['LIBS']
-                # and thus breaks knowledge below that gdal worked
-                # TODO - upgrade our scons logic to support Framework linking
-                if env['PLATFORM'] == 'Darwin':
-                    value = call(cmd,silent=True)
-                    if value and '-framework GDAL' in value:
-                        env['LIBS'].append('gdal')
-                        if os.path.exists('/Library/Frameworks/GDAL.framework/unix/lib'):
-                            env['LIBPATH'].insert(0,'/Library/Frameworks/GDAL.framework/unix/lib')
-                    if 'GDAL' in env.get('FRAMEWORKS',[]):
-                        env["FRAMEWORKS"].remove("GDAL")
-            else:
-                env.ParseConfig(cmd)
-            parsed = True
-        except OSError, e:
-            ret = False
-            print ' (xml2-config not found!)'
+	try:
+	    if 'gdal-config' in cmd:
+		env.ParseConfig(cmd)
+		# hack for potential -framework GDAL syntax
+		# which will not end up being added to env['LIBS']
+		# and thus breaks knowledge below that gdal worked
+		# TODO - upgrade our scons logic to support Framework linking
+		if env['PLATFORM'] == 'Darwin':
+		    value = call(cmd,silent=True)
+		    if value and '-framework GDAL' in value:
+			env['LIBS'].append('gdal')
+			if os.path.exists('/Library/Frameworks/GDAL.framework/unix/lib'):
+			    env['LIBPATH'].insert(0,'/Library/Frameworks/GDAL.framework/unix/lib')
+		    if 'GDAL' in env.get('FRAMEWORKS',[]):
+			env["FRAMEWORKS"].remove("GDAL")
+	    else:
+		env.ParseConfig(cmd)
+	    parsed = True
+	except OSError, e:
+	    ret = False
+	    print ' (xml2-config not found!)'
     if not parsed:
-        if config in ('GDAL_CONFIG'):
-            # optional deps...
-            if tool not in env['SKIPPED_DEPS']:
-                env['SKIPPED_DEPS'].append(tool)
-            conf.rollback_option(config)
-        else: # freetype and libxml2, not optional
-            if tool not in env['MISSING_DEPS']:
-                env['MISSING_DEPS'].append(tool)
+	if config in ('GDAL_CONFIG'):
+	    # optional deps...
+	    if tool not in env['SKIPPED_DEPS']:
+		env['SKIPPED_DEPS'].append(tool)
+	    conf.rollback_option(config)
+	else: # freetype and libxml2, not optional
+	    if tool not in env['MISSING_DEPS']:
+		env['MISSING_DEPS'].append(tool)
     context.Result( ret )
     return ret
 
@@ -635,22 +636,22 @@ def get_pkg_lib(context, config, lib):
     ret = context.TryAction(cmd)[0]
     parsed = False
     if ret:
-        try:
-            value = call(cmd,silent=True)
-            if ' ' in value:
-                parts = value.split(' ')
-                if len(parts) > 1:
-                    value = parts[1]
-            libnames = re.findall(libpattern,value)
-            if libnames:
-                libname = libnames[0]
-            else:
-                # osx 1.8 install gives '-framework GDAL'
-                libname = 'gdal'
-        except Exception, e:
-            ret = False
-            print ' unable to determine library name:'# %s' % str(e)
-            return None
+	try:
+	    value = call(cmd,silent=True)
+	    if ' ' in value:
+		parts = value.split(' ')
+		if len(parts) > 1:
+		    value = parts[1]
+	    libnames = re.findall(libpattern,value)
+	    if libnames:
+		libname = libnames[0]
+	    else:
+		# osx 1.8 install gives '-framework GDAL'
+		libname = 'gdal'
+	except Exception, e:
+	    ret = False
+	    print ' unable to determine library name:'# %s' % str(e)
+	    return None
     context.Result( libname )
     return libname
 
@@ -661,15 +662,15 @@ def parse_pg_config(context, config):
     context.Message( 'Checking for %s... ' % tool)
     ret = context.TryAction(env[config])[0]
     if ret:
-        lib_path = call('%s --libdir' % env[config])
-        inc_path = call('%s --includedir' % env[config])
-        env.AppendUnique(CPPPATH = fix_path(inc_path))
-        env.AppendUnique(LIBPATH = fix_path(lib_path))
-        lpq = env['PLUGINS']['postgis']['lib']
-        env.Append(LIBS = lpq)
+	lib_path = call('%s --libdir' % env[config])
+	inc_path = call('%s --includedir' % env[config])
+	env.AppendUnique(CPPPATH = fix_path(inc_path))
+	env.AppendUnique(LIBPATH = fix_path(lib_path))
+	lpq = env['PLUGINS']['postgis']['lib']
+	env.Append(LIBS = lpq)
     else:
-        env['SKIPPED_DEPS'].append(tool)
-        conf.rollback_option(config)
+	env['SKIPPED_DEPS'].append(tool)
+	conf.rollback_option(config)
     context.Result( ret )
     return ret
 
@@ -678,8 +679,8 @@ def ogr_enabled(context):
     context.Message( 'Checking if gdal is ogr enabled... ')
     ret = context.TryAction('%s --ogr-enabled' % env['GDAL_CONFIG'])[0]
     if not ret:
-        if 'ogr' not in env['SKIPPED_DEPS']:
-            env['SKIPPED_DEPS'].append('ogr')
+	if 'ogr' not in env['SKIPPED_DEPS']:
+	    env['SKIPPED_DEPS'].append('ogr')
     context.Result( ret )
     return ret
 
@@ -687,8 +688,8 @@ def rollback_option(context,variable):
     global opts
     env = context.env
     for item in opts.options:
-        if item.key == variable:
-            env[variable] = item.default
+	if item.key == variable:
+	    env[variable] = item.default
 
 def FindBoost(context, prefixes, thread_flag):
     """Routine to auto-find boost header dir, lib dir, and library naming structure.
@@ -703,72 +704,72 @@ def FindBoost(context, prefixes, thread_flag):
     env['BOOST_APPEND'] = str()
 
     if env['THREADING'] == 'multi':
-        search_lib = 'libboost_thread'
+	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'
     prefixes.insert(0,os.path.dirname(os.path.normpath(env['BOOST_INCLUDES'])))
     prefixes.insert(0,os.path.dirname(os.path.normpath(env['BOOST_LIBS'])))
     for searchDir in prefixes:
-        libItems = glob(os.path.join(searchDir, env['LIBDIR_SCHEMA'], '%s*.*' % search_lib))
-        if not libItems:
-            libItems = glob(os.path.join(searchDir, 'lib/%s*.*' % search_lib))
-        incItems = glob(os.path.join(searchDir, 'include/boost*/'))
-        if len(libItems) >= 1 and len(incItems) >= 1:
-            BOOST_LIB_DIR = os.path.dirname(libItems[0])
-            BOOST_INCLUDE_DIR = incItems[0].rstrip('boost/')
-            shortest_lib_name = shortest_name(libItems)
-            match = re.search(r'%s(.*)\..*' % search_lib, shortest_lib_name)
-            if hasattr(match,'groups'):
-                BOOST_APPEND = match.groups()[0]
-            break
+	libItems = glob(os.path.join(searchDir, env['LIBDIR_SCHEMA'], '%s*.*' % search_lib))
+	if not libItems:
+	    libItems = glob(os.path.join(searchDir, 'lib/%s*.*' % search_lib))
+	incItems = glob(os.path.join(searchDir, 'include/boost*/'))
+	if len(libItems) >= 1 and len(incItems) >= 1:
+	    BOOST_LIB_DIR = os.path.dirname(libItems[0])
+	    BOOST_INCLUDE_DIR = incItems[0].rstrip('boost/')
+	    shortest_lib_name = shortest_name(libItems)
+	    match = re.search(r'%s(.*)\..*' % search_lib, shortest_lib_name)
+	    if hasattr(match,'groups'):
+		BOOST_APPEND = match.groups()[0]
+	    break
 
     msg = str()
 
     if BOOST_LIB_DIR:
-        msg += '\nFound boost libs: %s' % BOOST_LIB_DIR
-        env['BOOST_LIBS'] = BOOST_LIB_DIR
+	msg += '\nFound boost libs: %s' % BOOST_LIB_DIR
+	env['BOOST_LIBS'] = BOOST_LIB_DIR
     elif not env['BOOST_LIBS']:
-        env['BOOST_LIBS'] = '/usr/' + env['LIBDIR_SCHEMA']
-        msg += '\nUsing default boost lib dir: %s' % env['BOOST_LIBS']
+	env['BOOST_LIBS'] = '/usr/' + env['LIBDIR_SCHEMA']
+	msg += '\nUsing default boost lib dir: %s' % env['BOOST_LIBS']
     else:
-        msg += '\nUsing boost lib dir: %s' % env['BOOST_LIBS']
+	msg += '\nUsing boost lib dir: %s' % env['BOOST_LIBS']
 
     if BOOST_INCLUDE_DIR:
-        msg += '\nFound boost headers: %s' % BOOST_INCLUDE_DIR
-        env['BOOST_INCLUDES'] = BOOST_INCLUDE_DIR
+	msg += '\nFound boost headers: %s' % BOOST_INCLUDE_DIR
+	env['BOOST_INCLUDES'] = BOOST_INCLUDE_DIR
     elif not env['BOOST_INCLUDES']:
-        env['BOOST_INCLUDES'] = '/usr/include'
-        msg += '\nUsing default boost include dir: %s' % env['BOOST_INCLUDES']
+	env['BOOST_INCLUDES'] = '/usr/include'
+	msg += '\nUsing default boost include dir: %s' % env['BOOST_INCLUDES']
     else:
-        msg += '\nUsing boost include dir: %s' % env['BOOST_INCLUDES']
+	msg += '\nUsing boost include dir: %s' % env['BOOST_INCLUDES']
 
     if not env['BOOST_TOOLKIT'] and not env['BOOST_ABI'] and not env['BOOST_VERSION']:
-        if BOOST_APPEND:
-            msg += '\nFound boost lib name extension: %s' % BOOST_APPEND
-            env['BOOST_APPEND'] = BOOST_APPEND
+	if BOOST_APPEND:
+	    msg += '\nFound boost lib name extension: %s' % BOOST_APPEND
+	    env['BOOST_APPEND'] = BOOST_APPEND
     else:
-        # Creating BOOST_APPEND according to the Boost library naming order,
-        # which goes <toolset>-<threading>-<abi>-<version>. See:
-        #  http://www.boost.org/doc/libs/1_35_0/more/getting_started/unix-variants.html#library-naming
-        append_params = ['']
-        if env['BOOST_TOOLKIT']: append_params.append(env['BOOST_TOOLKIT'])
-        if thread_flag: append_params.append(thread_flag)
-        if env['BOOST_ABI']: append_params.append(env['BOOST_ABI'])
-        if env['BOOST_VERSION']: append_params.append(env['BOOST_VERSION'])
-
-        # Constructing the BOOST_APPEND setting that will be used to find the
-        # Boost libraries.
-        if len(append_params) > 1:
-            env['BOOST_APPEND'] = '-'.join(append_params)
-        msg += '\nFound boost lib name extension: %s' % env['BOOST_APPEND']
+	# Creating BOOST_APPEND according to the Boost library naming order,
+	# which goes <toolset>-<threading>-<abi>-<version>. See:
+	#  http://www.boost.org/doc/libs/1_35_0/more/getting_started/unix-variants.html#library-naming
+	append_params = ['']
+	if env['BOOST_TOOLKIT']: append_params.append(env['BOOST_TOOLKIT'])
+	if thread_flag: append_params.append(thread_flag)
+	if env['BOOST_ABI']: append_params.append(env['BOOST_ABI'])
+	if env['BOOST_VERSION']: append_params.append(env['BOOST_VERSION'])
+
+	# Constructing the BOOST_APPEND setting that will be used to find the
+	# Boost libraries.
+	if len(append_params) > 1:
+	    env['BOOST_APPEND'] = '-'.join(append_params)
+	msg += '\nFound boost lib name extension: %s' % env['BOOST_APPEND']
 
     env.AppendUnique(CPPPATH = fix_path(env['BOOST_INCLUDES']))
     env.AppendUnique(LIBPATH = fix_path(env['BOOST_LIBS']))
     if env['COLOR_PRINT']:
-        msg = "\033[94m%s\033[0m" % (msg)
+	msg = "\033[94m%s\033[0m" % (msg)
     ret = context.Result(msg)
     return ret
 
@@ -777,14 +778,14 @@ def CheckBoost(context, version, silent=False):
     v_arr = version.split(".")
     version_n = 0
     if len(v_arr) > 0:
-        version_n += int(v_arr[0])*100000
+	version_n += int(v_arr[0])*100000
     if len(v_arr) > 1:
-        version_n += int(v_arr[1])*100
+	version_n += int(v_arr[1])*100
     if len(v_arr) > 2:
-        version_n += int(v_arr[2])
+	version_n += int(v_arr[2])
 
     if not silent:
-        context.Message('Checking for Boost version >= %s... ' % (version))
+	context.Message('Checking for Boost version >= %s... ' % (version))
     ret = context.TryRun("""
 
 #include <boost/version.hpp>
@@ -796,13 +797,13 @@ int main()
 
 """ % version_n, '.cpp')[0]
     if silent:
-        context.did_show_result=1
+	context.did_show_result=1
     context.Result(ret)
     return ret
 
 def CheckCairoHasFreetype(context, silent=False):
     if not silent:
-        context.Message('Checking for cairo freetype font support ... ')
+	context.Message('Checking for cairo freetype font support ... ')
     context.env.AppendUnique(CPPPATH=copy(env['CAIRO_CPPPATHS']))
 
     ret = context.TryRun("""
@@ -820,15 +821,15 @@ int main()
 
 """, '.cpp')[0]
     if silent:
-        context.did_show_result=1
+	context.did_show_result=1
     context.Result(ret)
     for item in env['CAIRO_CPPPATHS']:
-        rm_path(item,'CPPPATH',context.env)
+	rm_path(item,'CPPPATH',context.env)
     return ret
 
 def CheckHasDlfcn(context, silent=False):
     if not silent:
-        context.Message('Checking for dlfcn.h support ... ')
+	context.Message('Checking for dlfcn.h support ... ')
     ret = context.TryCompile("""
 
 #include <dlfcn.h>
@@ -840,7 +841,7 @@ int main()
 
 """, '.cpp')
     if silent:
-        context.did_show_result=1
+	context.did_show_result=1
     context.Result(ret)
     return ret
 
@@ -865,7 +866,7 @@ return 0;
 
 def CheckBoostScopedEnum(context, silent=False):
     if not silent:
-        context.Message('Checking whether Boost was compiled with C++11 scoped enums ... ')
+	context.Message('Checking whether Boost was compiled with C++11 scoped enums ... ')
     ret = context.TryLink("""
 #include <boost/filesystem.hpp>
 
@@ -877,7 +878,7 @@ int main()
 }
 """, '.cpp')
     if silent:
-        context.did_show_result=1
+	context.did_show_result=1
     context.Result(ret)
     return ret
 
@@ -899,13 +900,13 @@ int main()
     context.did_show_result=1
     result = ret[1].strip()
     if not result:
-        context.Result('error, could not get major and minor version from unicode/uversion.h')
-        return False
+	context.Result('error, could not get major and minor version from unicode/uversion.h')
+	return False
 
     major, minor = map(int,result.split('.'))
     if major >= 4 and minor >= 0:
-        color_print(4,'found: icu %s' % result)
-        return True
+	color_print(4,'found: icu %s' % result)
+	return True
 
     color_print(1,'\nFound insufficient icu version... %s' % result)
     return False
@@ -928,24 +929,24 @@ int main()
     context.did_show_result=1
     result = ret[1].strip()
     if not result:
-        context.Result('error, could not get version from hb.h')
-        return False
+	context.Result('error, could not get version from hb.h')
+	return False
 
     items = result.split(';')
     if items[0] == '1':
-        color_print(4,'found: HarfBuzz %s' % items[1])
-        return True
+	color_print(4,'found: HarfBuzz %s' % items[1])
+	return True
 
     color_print(1,'\nHarfbuzz >= %s required but found ... %s' % (HARFBUZZ_MIN_VERSION_STRING,items[1]))
     return False
 
 def boost_regex_has_icu(context):
     if env['RUNTIME_LINK'] == 'static':
-        # re-order icu libs to ensure linux linker is happy
-        for lib_name in ['icui18n',env['ICU_LIB_NAME'],'icudata']:
-            if lib_name in context.env['LIBS']:
-                context.env['LIBS'].remove(lib_name)
-            context.env.Append(LIBS=lib_name)
+	# re-order icu libs to ensure linux linker is happy
+	for lib_name in ['icui18n',env['ICU_LIB_NAME'],'icudata']:
+	    if lib_name in context.env['LIBS']:
+		context.env['LIBS'].remove(lib_name)
+	    context.env.Append(LIBS=lib_name)
     ret = context.TryRun("""
 
 #include <boost/regex/icu.hpp>
@@ -955,12 +956,12 @@ int main()
 {
     U_NAMESPACE_QUALIFIER UnicodeString ustr;
     try {
-        boost::u32regex pattern = boost::make_u32regex(ustr);
+	boost::u32regex pattern = boost::make_u32regex(ustr);
     }
     // an exception is fine, still indicates support is
     // likely compiled into regex
     catch (...) {
-        return 0;
+	return 0;
     }
     return 0;
 }
@@ -969,7 +970,7 @@ int main()
     context.Message('Checking if boost_regex was built with ICU unicode support... ')
     context.Result(ret[0])
     if ret[0]:
-        return True
+	return True
     return False
 
 def sqlite_has_rtree(context, silent=False):
@@ -991,20 +992,20 @@ int main()
     rc = sqlite3_open(":memory:", &db);
     if (rc != SQLITE_OK)
     {
-        printf("error 1: %s\\n", sqlite3_errmsg(db));
+	printf("error 1: %s\\n", sqlite3_errmsg(db));
     }
     const char * sql = "create virtual table foo using rtree(pkid, xmin, xmax, ymin, ymax)";
     rc = sqlite3_exec(db, sql, 0, 0, 0);
     if (rc != SQLITE_OK)
     {
-        printf("error 2: %s\\n", sqlite3_errmsg(db));
-        sqlite3_close(db);
+	printf("error 2: %s\\n", sqlite3_errmsg(db));
+	sqlite3_close(db);
     }
     else
     {
-        printf("yes, has rtree!\\n");
-        sqlite3_close(db);
-        return 0;
+	printf("yes, has rtree!\\n");
+	sqlite3_close(db);
+	return 0;
     }
 
     return -1;
@@ -1012,12 +1013,12 @@ int main()
 
 """, '.c')
     if not silent:
-        context.Message('Checking if SQLite supports RTREE... ')
+	context.Message('Checking if SQLite supports RTREE... ')
     if silent:
-        context.did_show_result=1
+	context.did_show_result=1
     context.Result(ret[0])
     if ret[0]:
-        return True
+	return True
     return False
 
 def supports_cxx11(context,silent=False):
@@ -1034,54 +1035,54 @@ int main()
 
 """, '.cpp')
     if not silent:
-        context.Message('Checking if compiler (%s) supports -std=c++11 flag... ' % context.env.get('CXX','CXX'))
+	context.Message('Checking if compiler (%s) supports -std=c++11 flag... ' % context.env.get('CXX','CXX'))
     if silent:
-        context.did_show_result=1
+	context.did_show_result=1
     context.Result(ret[0])
     if ret[0]:
-        return True
+	return True
     return False
 
 
 
 conf_tests = { 'prioritize_paths'      : prioritize_paths,
-               'CheckPKGConfig'        : CheckPKGConfig,
-               'CheckPKG'              : CheckPKG,
-               'CheckPKGVersion'       : CheckPKGVersion,
-               'FindBoost'             : FindBoost,
-               'CheckBoost'            : CheckBoost,
-               'CheckCairoHasFreetype' : CheckCairoHasFreetype,
-               'CheckHasDlfcn'         : CheckHasDlfcn,
-               'GetBoostLibVersion'    : GetBoostLibVersion,
-               'parse_config'          : parse_config,
-               'parse_pg_config'       : parse_pg_config,
-               'ogr_enabled'           : ogr_enabled,
-               'get_pkg_lib'           : get_pkg_lib,
-               'rollback_option'       : rollback_option,
-               'icu_at_least_four_two' : icu_at_least_four_two,
-               'harfbuzz_version'      : harfbuzz_version,
-               'boost_regex_has_icu'   : boost_regex_has_icu,
-               'sqlite_has_rtree'      : sqlite_has_rtree,
-               'supports_cxx11'        : supports_cxx11,
-               'CheckBoostScopedEnum'  : CheckBoostScopedEnum,
-               }
+	       'CheckPKGConfig'        : CheckPKGConfig,
+	       'CheckPKG'              : CheckPKG,
+	       'CheckPKGVersion'       : CheckPKGVersion,
+	       'FindBoost'             : FindBoost,
+	       'CheckBoost'            : CheckBoost,
+	       'CheckCairoHasFreetype' : CheckCairoHasFreetype,
+	       'CheckHasDlfcn'         : CheckHasDlfcn,
+	       'GetBoostLibVersion'    : GetBoostLibVersion,
+	       'parse_config'          : parse_config,
+	       'parse_pg_config'       : parse_pg_config,
+	       'ogr_enabled'           : ogr_enabled,
+	       'get_pkg_lib'           : get_pkg_lib,
+	       'rollback_option'       : rollback_option,
+	       'icu_at_least_four_two' : icu_at_least_four_two,
+	       'harfbuzz_version'      : harfbuzz_version,
+	       'boost_regex_has_icu'   : boost_regex_has_icu,
+	       'sqlite_has_rtree'      : sqlite_has_rtree,
+	       'supports_cxx11'        : supports_cxx11,
+	       'CheckBoostScopedEnum'  : CheckBoostScopedEnum,
+	       }
 
 def GetMapnikLibVersion():
     ver = []
     is_pre = False
     for line in open('include/mapnik/version.hpp').readlines():
-        if line.startswith('#define MAPNIK_MAJOR_VERSION'):
-            ver.append(line.split(' ')[2].strip())
-        if line.startswith('#define MAPNIK_MINOR_VERSION'):
-            ver.append(line.split(' ')[2].strip())
-        if line.startswith('#define MAPNIK_PATCH_VERSION'):
-            ver.append(line.split(' ')[2].strip())
-        if line.startswith('#define MAPNIK_VERSION_IS_RELEASE'):
-            if line.split(' ')[2].strip() == "0":
-                is_pre = True
+	if line.startswith('#define MAPNIK_MAJOR_VERSION'):
+	    ver.append(line.split(' ')[2].strip())
+	if line.startswith('#define MAPNIK_MINOR_VERSION'):
+	    ver.append(line.split(' ')[2].strip())
+	if line.startswith('#define MAPNIK_PATCH_VERSION'):
+	    ver.append(line.split(' ')[2].strip())
+	if line.startswith('#define MAPNIK_VERSION_IS_RELEASE'):
+	    if line.split(' ')[2].strip() == "0":
+		is_pre = True
     version_string = ".".join(ver)
     if is_pre:
-        version_string += '-pre'
+	version_string += '-pre'
     return version_string
 
 if not preconfigured:
@@ -1089,40 +1090,40 @@ if not preconfigured:
     color_print(4,'Configuring build environment...')
 
     if not env['FAST']:
-        SetCacheMode('force')
+	SetCacheMode('force')
 
     if env['USE_CONFIG']:
-        if not env['CONFIG'].endswith('.py'):
-            color_print(1,'SCons CONFIG file specified is not a python file, will not be read...')
-        else:
-            # Accept more than one file as comma-delimited list
-            user_confs = env['CONFIG'].split(',')
-            # If they exist add the files to the existing `opts`
-            for conf in user_confs:
-                if os.path.exists(conf):
-                    opts.files.append(conf)
-                    color_print(4,"SCons CONFIG found: '%s', variables will be inherited..." % conf)
-                    optfile = file(conf)
-                    #print optfile.read().replace("\n", " ").replace("'","").replace(" = ","=")
-                    optfile.close()
-
-                elif not conf == SCONS_LOCAL_CONFIG:
-                    # if default missing, no worries
-                    # but if the default is overridden and the file is not found, give warning
-                    color_print(1,"SCons CONFIG not found: '%s'" % conf)
-            # Recreate the base environment using modified `opts`
-            env = Environment(ENV=os.environ,options=opts)
-            init_environment(env)
-            env['USE_CONFIG'] = True
+	if not env['CONFIG'].endswith('.py'):
+	    color_print(1,'SCons CONFIG file specified is not a python file, will not be read...')
+	else:
+	    # Accept more than one file as comma-delimited list
+	    user_confs = env['CONFIG'].split(',')
+	    # If they exist add the files to the existing `opts`
+	    for conf in user_confs:
+		if os.path.exists(conf):
+		    opts.files.append(conf)
+		    color_print(4,"SCons CONFIG found: '%s', variables will be inherited..." % conf)
+		    optfile = file(conf)
+		    #print optfile.read().replace("\n", " ").replace("'","").replace(" = ","=")
+		    optfile.close()
+
+		elif not conf == SCONS_LOCAL_CONFIG:
+		    # if default missing, no worries
+		    # but if the default is overridden and the file is not found, give warning
+		    color_print(1,"SCons CONFIG not found: '%s'" % conf)
+	    # Recreate the base environment using modified `opts`
+	    env = Environment(ENV=os.environ,options=opts)
+	    init_environment(env)
+	    env['USE_CONFIG'] = True
     else:
-        color_print(4,'SCons USE_CONFIG specified as false, will not inherit variables python config file...')
+	color_print(4,'SCons USE_CONFIG specified as false, will not inherit variables python config file...')
 
     conf = Configure(env, custom_tests = conf_tests)
 
     if env['DEBUG']:
-        mode = 'debug mode'
+	mode = 'debug mode'
     else:
-        mode = 'release mode'
+	mode = 'release mode'
 
     env['PLATFORM'] = platform.uname()[0]
     color_print(4,"Configuring on %s in *%s*..." % (env['PLATFORM'],mode))
@@ -1147,7 +1148,7 @@ if not preconfigured:
     # previously a leading / was expected for LIB_DIR_NAME
     # now strip it to ensure expected behavior
     if env['LIB_DIR_NAME'].startswith(os.path.sep):
-        env['LIB_DIR_NAME'] = strip_first(env['LIB_DIR_NAME'],os.path.sep)
+	env['LIB_DIR_NAME'] = strip_first(env['LIB_DIR_NAME'],os.path.sep)
 
     # base install location
     env['MAPNIK_LIB_BASE'] = os.path.join(env['PREFIX'],env['LIBDIR_SCHEMA'])
@@ -1157,9 +1158,9 @@ if not preconfigured:
     env['MAPNIK_INPUT_PLUGINS'] = os.path.join(env['MAPNIK_LIB_DIR'],'input')
     # fonts sub directory
     if env['SYSTEM_FONTS']:
-        env['MAPNIK_FONTS'] = os.path.normpath(env['SYSTEM_FONTS'])
+	env['MAPNIK_FONTS'] = os.path.normpath(env['SYSTEM_FONTS'])
     else:
-        env['MAPNIK_FONTS'] = os.path.join(env['MAPNIK_LIB_DIR'],'fonts')
+	env['MAPNIK_FONTS'] = os.path.join(env['MAPNIK_LIB_DIR'],'fonts')
 
     # install prefix is a pre-pended base location to
     # re-route the install and only intended for package building
@@ -1172,9 +1173,9 @@ if not preconfigured:
     env['MAPNIK_LIB_DIR_DEST'] =  os.path.join(env['MAPNIK_LIB_BASE_DEST'],env['LIB_DIR_NAME'])
     env['MAPNIK_INPUT_PLUGINS_DEST'] = os.path.join(env['MAPNIK_LIB_DIR_DEST'],'input')
     if env['SYSTEM_FONTS']:
-        env['MAPNIK_FONTS_DEST'] = os.path.normpath(env['SYSTEM_FONTS'])
+	env['MAPNIK_FONTS_DEST'] = os.path.normpath(env['SYSTEM_FONTS'])
     else:
-        env['MAPNIK_FONTS_DEST'] = os.path.join(env['MAPNIK_LIB_DIR_DEST'],'fonts')
+	env['MAPNIK_FONTS_DEST'] = os.path.join(env['MAPNIK_LIB_DIR_DEST'],'fonts')
 
     if env['LINKING'] == 'static':
        env['MAPNIK_LIB_NAME'] = '${LIBPREFIX}${MAPNIK_NAME}${LIBSUFFIX}'
@@ -1182,15 +1183,15 @@ if not preconfigured:
        env['MAPNIK_LIB_NAME'] = '${SHLIBPREFIX}${MAPNIK_NAME}${SHLIBSUFFIX}'
 
     if env['PKG_CONFIG_PATH']:
-        env['ENV']['PKG_CONFIG_PATH'] = fix_path(env['PKG_CONFIG_PATH'])
-        # otherwise this variable == os.environ["PKG_CONFIG_PATH"]
+	env['ENV']['PKG_CONFIG_PATH'] = fix_path(env['PKG_CONFIG_PATH'])
+	# otherwise this variable == os.environ["PKG_CONFIG_PATH"]
 
     if env['PATH']:
-        env['ENV']['PATH'] = fix_path(env['PATH']) + ':' + env['ENV']['PATH']
+	env['ENV']['PATH'] = fix_path(env['PATH']) + ':' + env['ENV']['PATH']
 
     if env['SYSTEM_FONTS']:
-        if not os.path.isdir(env['SYSTEM_FONTS']):
-            color_print(1,'Warning: Directory specified for SYSTEM_FONTS does not exist!')
+	if not os.path.isdir(env['SYSTEM_FONTS']):
+	    color_print(1,'Warning: Directory specified for SYSTEM_FONTS does not exist!')
 
     # Set up for libraries and headers dependency checks
     env['CPPPATH'] = ['#include']
@@ -1198,8 +1199,8 @@ if not preconfigured:
 
     # set any custom cxxflags and ldflags to come first
     if sys.platform == 'darwin' and not env['HOST']:
-        DEFAULT_CXX11_CXXFLAGS += ' -stdlib=libc++'
-        DEFAULT_CXX11_LINKFLAGS = ' -stdlib=libc++'
+	DEFAULT_CXX11_CXXFLAGS += ' -stdlib=libc++'
+	DEFAULT_CXX11_LINKFLAGS = ' -stdlib=libc++'
     env.Append(CPPDEFINES = env['CUSTOM_DEFINES'])
     env.Append(CXXFLAGS = DEFAULT_CXX11_CXXFLAGS)
     env.Append(CXXFLAGS = env['CUSTOM_CXXFLAGS'])
@@ -1211,11 +1212,11 @@ if not preconfigured:
 
     thread_suffix = 'mt'
     if env['PLATFORM'] == 'FreeBSD':
-        thread_suffix = ''
-        env.Append(LIBS = 'pthread')
+	thread_suffix = ''
+	env.Append(LIBS = 'pthread')
 
     if env['SHAPE_MEMORY_MAPPED_FILE']:
-        env.Append(CPPDEFINES = '-DSHAPE_MEMORY_MAPPED_FILE')
+	env.Append(CPPDEFINES = '-DSHAPE_MEMORY_MAPPED_FILE')
 
     # allow for mac osx /usr/lib/libicucore.dylib compatibility
     # requires custom supplied headers since Apple does not include them
@@ -1224,365 +1225,365 @@ if not preconfigured:
     # http://www.opensource.apple.com/tarballs/ICU/
     # then copy the headers to a location that mapnik will find
     if 'core' in env['ICU_LIB_NAME']:
-        env.Append(CPPDEFINES = '-DU_HIDE_DRAFT_API')
-        env.Append(CPPDEFINES = '-DUDISABLE_RENAMING')
-        if os.path.exists(env['ICU_LIB_NAME']):
-            #-sICU_LINK=" -L/usr/lib -licucore
-            env['ICU_LIB_NAME'] = os.path.basename(env['ICU_LIB_NAME']).replace('.dylib','').replace('lib','')
+	env.Append(CPPDEFINES = '-DU_HIDE_DRAFT_API')
+	env.Append(CPPDEFINES = '-DUDISABLE_RENAMING')
+	if os.path.exists(env['ICU_LIB_NAME']):
+	    #-sICU_LINK=" -L/usr/lib -licucore
+	    env['ICU_LIB_NAME'] = os.path.basename(env['ICU_LIB_NAME']).replace('.dylib','').replace('lib','')
 
     # Adding the required prerequisite library directories to the include path for
     # compiling and the library path for linking, respectively.
     for required in ('ICU', 'SQLITE', 'HB'):
-        inc_path = env['%s_INCLUDES' % required]
-        lib_path = env['%s_LIBS' % required]
-        env.AppendUnique(CPPPATH = fix_path(inc_path))
-        env.AppendUnique(LIBPATH = fix_path(lib_path))
+	inc_path = env['%s_INCLUDES' % required]
+	lib_path = env['%s_LIBS' % required]
+	env.AppendUnique(CPPPATH = fix_path(inc_path))
+	env.AppendUnique(LIBPATH = fix_path(lib_path))
 
     REQUIRED_LIBSHEADERS = [
-        ['z', 'zlib.h', True,'C'],
-        [env['ICU_LIB_NAME'],'unicode/unistr.h',True,'C++'],
-        ['harfbuzz', 'harfbuzz/hb.h',True,'C++']
+	['z', 'zlib.h', True,'C'],
+	[env['ICU_LIB_NAME'],'unicode/unistr.h',True,'C++'],
+	['harfbuzz', 'harfbuzz/hb.h',True,'C++']
     ]
 
     if env.get('FREETYPE_LIBS') or env.get('FREETYPE_INCLUDES'):
-        REQUIRED_LIBSHEADERS.insert(0,['freetype','ft2build.h',True,'C'])
-        if env.get('FREETYPE_INCLUDES'):
-            inc_path = env['FREETYPE_INCLUDES']
-            env.AppendUnique(CPPPATH = fix_path(inc_path))
-        if env.get('FREETYPE_LIBS'):
-            lib_path = env['FREETYPE_LIBS']
-            env.AppendUnique(LIBPATH = fix_path(lib_path))
+	REQUIRED_LIBSHEADERS.insert(0,['freetype','ft2build.h',True,'C'])
+	if env.get('FREETYPE_INCLUDES'):
+	    inc_path = env['FREETYPE_INCLUDES']
+	    env.AppendUnique(CPPPATH = fix_path(inc_path))
+	if env.get('FREETYPE_LIBS'):
+	    lib_path = env['FREETYPE_LIBS']
+	    env.AppendUnique(LIBPATH = fix_path(lib_path))
     elif conf.parse_config('FREETYPE_CONFIG'):
-        # check if freetype links to bz2
-        if env['RUNTIME_LINK'] == 'static':
-            temp_env = env.Clone()
-            temp_env['LIBS'] = []
-            try:
-                # TODO - freetype-config accepts --static as of v2.5.3
-                temp_env.ParseConfig('%s --libs' % env['FREETYPE_CONFIG'])
-                if 'bz2' in temp_env['LIBS']:
-                    env['EXTRA_FREETYPE_LIBS'].append('bz2')
-            except OSError,e:
-                pass
+	# check if freetype links to bz2
+	if env['RUNTIME_LINK'] == 'static':
+	    temp_env = env.Clone()
+	    temp_env['LIBS'] = []
+	    try:
+		# TODO - freetype-config accepts --static as of v2.5.3
+		temp_env.ParseConfig('%s --libs' % env['FREETYPE_CONFIG'])
+		if 'bz2' in temp_env['LIBS']:
+		    env['EXTRA_FREETYPE_LIBS'].append('bz2')
+	    except OSError,e:
+		pass
 
     # libxml2 should be optional but is currently not
     # https://github.com/mapnik/mapnik/issues/913
     if env.get('XMLPARSER') and env['XMLPARSER'] == 'libxml2':
-        if env.get('XML2_LIBS') or env.get('XML2_INCLUDES'):
-            OPTIONAL_LIBSHEADERS.insert(0,['libxml2','libxml/parser.h',True,'C'])
-            if env.get('XML2_INCLUDES'):
-                inc_path = env['XML2_INCLUDES']
-                env.AppendUnique(CPPPATH = fix_path(inc_path))
-            if env.get('XML2_LIBS'):
-                lib_path = env['XML2_LIBS']
-                env.AppendUnique(LIBPATH = fix_path(lib_path))
-        elif conf.parse_config('XML2_CONFIG',checks='--cflags'):
-            env['HAS_LIBXML2'] = True
-        else:
-            env['MISSING_DEPS'].append('libxml2')
+	if env.get('XML2_LIBS') or env.get('XML2_INCLUDES'):
+	    OPTIONAL_LIBSHEADERS.insert(0,['libxml2','libxml/parser.h',True,'C'])
+	    if env.get('XML2_INCLUDES'):
+		inc_path = env['XML2_INCLUDES']
+		env.AppendUnique(CPPPATH = fix_path(inc_path))
+	    if env.get('XML2_LIBS'):
+		lib_path = env['XML2_LIBS']
+		env.AppendUnique(LIBPATH = fix_path(lib_path))
+	elif conf.parse_config('XML2_CONFIG',checks='--cflags'):
+	    env['HAS_LIBXML2'] = True
+	else:
+	    env['MISSING_DEPS'].append('libxml2')
 
     if not env['HOST']:
-        if conf.CheckHasDlfcn():
-            env.Append(CPPDEFINES = '-DMAPNIK_HAS_DLCFN')
-        else:
-            env['SKIPPED_DEPS'].extend(['dlfcn'])
+	if conf.CheckHasDlfcn():
+	    env.Append(CPPDEFINES = '-DMAPNIK_HAS_DLCFN')
+	else:
+	    env['SKIPPED_DEPS'].extend(['dlfcn'])
 
     OPTIONAL_LIBSHEADERS = []
 
     if env['JPEG']:
-        OPTIONAL_LIBSHEADERS.append(['jpeg', ['stdio.h', 'jpeglib.h'], False,'C','-DHAVE_JPEG'])
-        inc_path = env['%s_INCLUDES' % 'JPEG']
-        lib_path = env['%s_LIBS' % 'JPEG']
-        env.AppendUnique(CPPPATH = fix_path(inc_path))
-        env.AppendUnique(LIBPATH = fix_path(lib_path))
+	OPTIONAL_LIBSHEADERS.append(['jpeg', ['stdio.h', 'jpeglib.h'], False,'C','-DHAVE_JPEG'])
+	inc_path = env['%s_INCLUDES' % 'JPEG']
+	lib_path = env['%s_LIBS' % 'JPEG']
+	env.AppendUnique(CPPPATH = fix_path(inc_path))
+	env.AppendUnique(LIBPATH = fix_path(lib_path))
     else:
-        env['SKIPPED_DEPS'].extend(['jpeg'])
+	env['SKIPPED_DEPS'].extend(['jpeg'])
 
     if env['PROJ']:
-        OPTIONAL_LIBSHEADERS.append(['proj', 'proj_api.h', False,'C','-DMAPNIK_USE_PROJ4'])
-        inc_path = env['%s_INCLUDES' % 'PROJ']
-        lib_path = env['%s_LIBS' % 'PROJ']
-        env.AppendUnique(CPPPATH = fix_path(inc_path))
-        env.AppendUnique(LIBPATH = fix_path(lib_path))
+	OPTIONAL_LIBSHEADERS.append(['proj', 'proj_api.h', False,'C','-DMAPNIK_USE_PROJ4'])
+	inc_path = env['%s_INCLUDES' % 'PROJ']
+	lib_path = env['%s_LIBS' % 'PROJ']
+	env.AppendUnique(CPPPATH = fix_path(inc_path))
+	env.AppendUnique(LIBPATH = fix_path(lib_path))
     else:
-        env['SKIPPED_DEPS'].extend(['proj'])
+	env['SKIPPED_DEPS'].extend(['proj'])
 
     if env['PNG']:
-        OPTIONAL_LIBSHEADERS.append(['png', 'png.h', False,'C','-DHAVE_PNG'])
-        inc_path = env['%s_INCLUDES' % 'PNG']
-        lib_path = env['%s_LIBS' % 'PNG']
-        env.AppendUnique(CPPPATH = fix_path(inc_path))
-        env.AppendUnique(LIBPATH = fix_path(lib_path))
+	OPTIONAL_LIBSHEADERS.append(['png', 'png.h', False,'C','-DHAVE_PNG'])
+	inc_path = env['%s_INCLUDES' % 'PNG']
+	lib_path = env['%s_LIBS' % 'PNG']
+	env.AppendUnique(CPPPATH = fix_path(inc_path))
+	env.AppendUnique(LIBPATH = fix_path(lib_path))
     else:
-        env['SKIPPED_DEPS'].extend(['png'])
+	env['SKIPPED_DEPS'].extend(['png'])
 
     if env['WEBP']:
-        OPTIONAL_LIBSHEADERS.append(['webp', 'webp/decode.h', False,'C','-DHAVE_WEBP'])
-        inc_path = env['%s_INCLUDES' % 'WEBP']
-        lib_path = env['%s_LIBS' % 'WEBP']
-        env.AppendUnique(CPPPATH = fix_path(inc_path))
-        env.AppendUnique(LIBPATH = fix_path(lib_path))
+	OPTIONAL_LIBSHEADERS.append(['webp', 'webp/decode.h', False,'C','-DHAVE_WEBP'])
+	inc_path = env['%s_INCLUDES' % 'WEBP']
+	lib_path = env['%s_LIBS' % 'WEBP']
+	env.AppendUnique(CPPPATH = fix_path(inc_path))
+	env.AppendUnique(LIBPATH = fix_path(lib_path))
     else:
-        env['SKIPPED_DEPS'].extend(['webp'])
+	env['SKIPPED_DEPS'].extend(['webp'])
 
     if env['TIFF']:
-        OPTIONAL_LIBSHEADERS.append(['tiff', 'tiff.h', False,'C','-DHAVE_TIFF'])
-        inc_path = env['%s_INCLUDES' % 'TIFF']
-        lib_path = env['%s_LIBS' % 'TIFF']
-        env.AppendUnique(CPPPATH = fix_path(inc_path))
-        env.AppendUnique(LIBPATH = fix_path(lib_path))
+	OPTIONAL_LIBSHEADERS.append(['tiff', 'tiff.h', False,'C','-DHAVE_TIFF'])
+	inc_path = env['%s_INCLUDES' % 'TIFF']
+	lib_path = env['%s_LIBS' % 'TIFF']
+	env.AppendUnique(CPPPATH = fix_path(inc_path))
+	env.AppendUnique(LIBPATH = fix_path(lib_path))
     else:
-        env['SKIPPED_DEPS'].extend(['tiff'])
+	env['SKIPPED_DEPS'].extend(['tiff'])
 
     # if requested, sort LIBPATH and CPPPATH before running CheckLibWithHeader tests
     if env['PRIORITIZE_LINKING']:
-        conf.prioritize_paths(silent=True)
+	conf.prioritize_paths(silent=True)
 
     # 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)")
-        Exit(1)
+	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)")
+	Exit(1)
 
     if not env['HOST']:
-        for libname, headers, required, lang in REQUIRED_LIBSHEADERS:
-            if not conf.CheckLibWithHeader(libname, headers, lang):
-                if required:
-                    color_print(1, 'Could not find required header or shared library for %s' % libname)
-                    env['MISSING_DEPS'].append(libname)
-                else:
-                    color_print(4, 'Could not find optional header or shared library for %s' % libname)
-                    env['SKIPPED_DEPS'].append(libname)
-            else:
-                if libname == env['ICU_LIB_NAME']:
-                    if env['ICU_LIB_NAME'] not in env['MISSING_DEPS']:
-                        if not conf.icu_at_least_four_two():
-                            # expression_string.cpp and map.cpp use fromUTF* function only available in >= ICU 4.2
-                            env['MISSING_DEPS'].append(env['ICU_LIB_NAME'])
-                elif libname == 'harfbuzz':
-                    if not conf.harfbuzz_version():
-                        env['SKIPPED_DEPS'].append('harfbuzz-min-version')
+	for libname, headers, required, lang in REQUIRED_LIBSHEADERS:
+	    if not conf.CheckLibWithHeader(libname, headers, lang):
+		if required:
+		    color_print(1, 'Could not find required header or shared library for %s' % libname)
+		    env['MISSING_DEPS'].append(libname)
+		else:
+		    color_print(4, 'Could not find optional header or shared library for %s' % libname)
+		    env['SKIPPED_DEPS'].append(libname)
+	    else:
+		if libname == env['ICU_LIB_NAME']:
+		    if env['ICU_LIB_NAME'] not in env['MISSING_DEPS']:
+			if not conf.icu_at_least_four_two():
+			    # expression_string.cpp and map.cpp use fromUTF* function only available in >= ICU 4.2
+			    env['MISSING_DEPS'].append(env['ICU_LIB_NAME'])
+		elif libname == 'harfbuzz':
+		    if not conf.harfbuzz_version():
+			env['SKIPPED_DEPS'].append('harfbuzz-min-version')
 
     if env['BIGINT']:
-        env.Append(CPPDEFINES = '-DBIGINT')
+	env.Append(CPPDEFINES = '-DBIGINT')
 
     if env['THREADING'] == 'multi':
-        thread_flag = thread_suffix
+	thread_flag = thread_suffix
     else:
-        thread_flag = ''
+	thread_flag = ''
 
     conf.FindBoost(BOOST_SEARCH_PREFIXES,thread_flag)
 
     has_boost_devel = True
     if not env['HOST']:
-        if not conf.CheckHeader(header='boost/version.hpp',language='C++'):
-            env['MISSING_DEPS'].append('boost development headers')
-            has_boost_devel = False
+	if not conf.CheckHeader(header='boost/version.hpp',language='C++'):
+	    env['MISSING_DEPS'].append('boost development headers')
+	    has_boost_devel = False
 
     if has_boost_devel:
-        if not env['HOST']:
-            env['BOOST_LIB_VERSION_FROM_HEADER'] = conf.GetBoostLibVersion()
-
-        # The other required boost headers.
-        BOOST_LIBSHEADERS = [
-            ['system', 'boost/system/system_error.hpp', True],
-            ['filesystem', 'boost/filesystem/operations.hpp', True],
-            ['regex', 'boost/regex.hpp', True],
-            ['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)
-
-        if not env['HOST']:
-            # if the user is not setting custom boost configuration
-            # enforce boost version greater than or equal to BOOST_MIN_VERSION
-            if not conf.CheckBoost(BOOST_MIN_VERSION):
-                color_print(4,'Found boost lib version... %s' % env.get('BOOST_LIB_VERSION_FROM_HEADER') )
-                color_print(1,'Boost version %s or greater is required' % BOOST_MIN_VERSION)
-                if not env['BOOST_VERSION']:
-                    env['MISSING_DEPS'].append('boost version >= %s' % BOOST_MIN_VERSION)
-            else:
-                color_print(4,'Found boost lib version... %s' % env.get('BOOST_LIB_VERSION_FROM_HEADER') )
-
-        if not env['HOST']:
-            for count, libinfo in enumerate(BOOST_LIBSHEADERS):
-                if not conf.CheckLibWithHeader('boost_%s%s' % (libinfo[0],env['BOOST_APPEND']), libinfo[1], 'C++'):
-                    if libinfo[2]:
-                        color_print(1,'Could not find required header or shared library for boost %s' % libinfo[0])
-                        env['MISSING_DEPS'].append('boost ' + libinfo[0])
-                    else:
-                        color_print(4,'Could not find optional header or shared library for boost %s' % libinfo[0])
-                        env['SKIPPED_DEPS'].append('boost ' + libinfo[0])
-
-        # Boost versions before 1.57 are broken when the system package and
-        # Mapnik are compiled against different standards. On Ubuntu 14.04
-        # using boost 1.54, it breaks scoped enums. It's a bit of a hack to
-        # 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']:
+	    env['BOOST_LIB_VERSION_FROM_HEADER'] = conf.GetBoostLibVersion()
+
+	# The other required boost headers.
+	BOOST_LIBSHEADERS = [
+	    ['system', 'boost/system/system_error.hpp', True],
+	    ['filesystem', 'boost/filesystem/operations.hpp', True],
+	    ['regex', 'boost/regex.hpp', True],
+	    ['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)
+
+	if not env['HOST']:
+	    # if the user is not setting custom boost configuration
+	    # enforce boost version greater than or equal to BOOST_MIN_VERSION
+	    if not conf.CheckBoost(BOOST_MIN_VERSION):
+		color_print(4,'Found boost lib version... %s' % env.get('BOOST_LIB_VERSION_FROM_HEADER') )
+		color_print(1,'Boost version %s or greater is required' % BOOST_MIN_VERSION)
+		if not env['BOOST_VERSION']:
+		    env['MISSING_DEPS'].append('boost version >= %s' % BOOST_MIN_VERSION)
+	    else:
+		color_print(4,'Found boost lib version... %s' % env.get('BOOST_LIB_VERSION_FROM_HEADER') )
+
+	if not env['HOST']:
+	    for count, libinfo in enumerate(BOOST_LIBSHEADERS):
+		if not conf.CheckLibWithHeader('boost_%s%s' % (libinfo[0],env['BOOST_APPEND']), libinfo[1], 'C++'):
+		    if libinfo[2]:
+			color_print(1,'Could not find required header or shared library for boost %s' % libinfo[0])
+			env['MISSING_DEPS'].append('boost ' + libinfo[0])
+		    else:
+			color_print(4,'Could not find optional header or shared library for boost %s' % libinfo[0])
+			env['SKIPPED_DEPS'].append('boost ' + libinfo[0])
+
+	# Boost versions before 1.57 are broken when the system package and
+	# Mapnik are compiled against different standards. On Ubuntu 14.04
+	# using boost 1.54, it breaks scoped enums. It's a bit of a hack to
+	# 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'] and env['ICU_LIB_NAME'] not in env['MISSING_DEPS']:
-        # http://lists.boost.org/Archives/boost/2009/03/150076.php
-        # we need libicui18n if using static boost libraries, so it is
-        # important to try this check with the library linked
-        if conf.boost_regex_has_icu():
-            # TODO - should avoid having this be globally defined...
-            env.Append(CPPDEFINES = '-DBOOST_REGEX_HAS_ICU')
-        else:
-            env['SKIPPED_DEPS'].append('boost_regex_icu')
-
-        for libname, headers, required, lang, define in OPTIONAL_LIBSHEADERS:
-            if not env['HOST']:
-                if not conf.CheckLibWithHeader(libname, headers, lang):
-                    if required:
-                        color_print(1, 'Could not find required header or shared library for %s' % libname)
-                        env['MISSING_DEPS'].append(libname)
-                    else:
-                        color_print(4, 'Could not find optional header or shared library for %s' % libname)
-                        env['SKIPPED_DEPS'].append(libname)
-                else:
-                    env.Append(CPPDEFINES = define)
-            else:
-                env.Append(CPPDEFINES = define)
+	# http://lists.boost.org/Archives/boost/2009/03/150076.php
+	# we need libicui18n if using static boost libraries, so it is
+	# important to try this check with the library linked
+	if conf.boost_regex_has_icu():
+	    # TODO - should avoid having this be globally defined...
+	    env.Append(CPPDEFINES = '-DBOOST_REGEX_HAS_ICU')
+	else:
+	    env['SKIPPED_DEPS'].append('boost_regex_icu')
+
+	for libname, headers, required, lang, define in OPTIONAL_LIBSHEADERS:
+	    if not env['HOST']:
+		if not conf.CheckLibWithHeader(libname, headers, lang):
+		    if required:
+			color_print(1, 'Could not find required header or shared library for %s' % libname)
+			env['MISSING_DEPS'].append(libname)
+		    else:
+			color_print(4, 'Could not find optional header or shared library for %s' % libname)
+			env['SKIPPED_DEPS'].append(libname)
+		else:
+		    env.Append(CPPDEFINES = define)
+	    else:
+		env.Append(CPPDEFINES = define)
 
     env['REQUESTED_PLUGINS'] = [ driver.strip() for driver in Split(env['INPUT_PLUGINS'])]
 
     SQLITE_HAS_RTREE = None
     if env['HOST']:
-        SQLITE_HAS_RTREE = True
+	SQLITE_HAS_RTREE = True
 
     CHECK_PKG_CONFIG = conf.CheckPKGConfig('0.15.0')
 
     if len(env['REQUESTED_PLUGINS']):
-        if env['HOST']:
-            for plugin in env['REQUESTED_PLUGINS']:
-                details = env['PLUGINS'][plugin]
-                if details['lib']:
-                    env.AppendUnique(LIBS=details['lib'])
-        else:
-            color_print(4,'Checking for requested plugins dependencies...')
-            for plugin in env['REQUESTED_PLUGINS']:
-                details = env['PLUGINS'][plugin]
-                if plugin == 'gdal':
-                    if conf.parse_config('GDAL_CONFIG',checks='--libs'):
-                        conf.parse_config('GDAL_CONFIG',checks='--cflags')
-                        libname = conf.get_pkg_lib('GDAL_CONFIG','gdal')
-                        if libname:
-                            if not conf.CheckLibWithHeader(libname, details['inc'], details['lang']):
-                                env['SKIPPED_DEPS'].append('gdal')
-                                if libname in env['LIBS']:
-                                     env['LIBS'].remove(libname)
-                            else:
-                                details['lib'] = libname
-                elif plugin == 'postgis' or plugin == 'pgraster':
-                    if env.get('PG_LIBS') or env.get('PG_INCLUDES'):
-                        libname = details['lib']
-                        if env.get('PG_INCLUDES'):
-                            inc_path = env['PG_INCLUDES']
-                            env.AppendUnique(CPPPATH = fix_path(inc_path))
-                        if env.get('PG_LIBS'):
-                            lib_path = env['PG_LIBS']
-                            env.AppendUnique(LIBPATH = fix_path(lib_path))
-                        if not conf.CheckLibWithHeader(libname, details['inc'], details['lang']):
-                            env['SKIPPED_DEPS'].append(libname)
-                            if libname in env['LIBS']:
-                                 env['LIBS'].remove(libname)
-                        else:
-                            details['lib'] = libname
-                    else:
-                        conf.parse_pg_config('PG_CONFIG')
-                elif plugin == 'ogr':
-                    if conf.ogr_enabled():
-                        if conf.parse_config('GDAL_CONFIG',checks='--libs'):
-                            conf.parse_config('GDAL_CONFIG',checks='--cflags')
-                            libname = conf.get_pkg_lib('GDAL_CONFIG','ogr')
-                            if libname:
-                                if not conf.CheckLibWithHeader(libname, details['inc'], details['lang']):
-                                    if 'gdal' not in env['SKIPPED_DEPS']:
-                                        env['SKIPPED_DEPS'].append('gdal')
-                                    if libname in env['LIBS']:
-                                         env['LIBS'].remove(libname)
-                                else:
-                                    details['lib'] = libname
-                elif details['path'] and details['lib'] and details['inc']:
-                    backup = env.Clone().Dictionary()
-                    # Note, the 'delete_existing' keyword makes sure that these paths are prepended
-                    # to the beginning of the path list even if they already exist
-                    incpath = env['%s_INCLUDES' % details['path']]
-                    libpath = env['%s_LIBS' % details['path']]
-                    env.PrependUnique(CPPPATH = fix_path(incpath),delete_existing=True)
-                    env.PrependUnique(LIBPATH = fix_path(libpath),delete_existing=True)
-                    if not conf.CheckLibWithHeader(details['lib'], details['inc'], details['lang']):
-                        env.Replace(**backup)
-                        env['SKIPPED_DEPS'].append(details['lib'])
-                    if plugin == 'sqlite':
-                        sqlite_backup = env.Clone().Dictionary()
-                        # if statically linking, on linux we likely
-                        # need to link sqlite to pthreads and dl
-                        if env['RUNTIME_LINK'] == 'static' and not env['PLATFORM'] == 'Darwin':
-                            if CHECK_PKG_CONFIG and conf.CheckPKG('sqlite3'):
-                                sqlite_env = env.Clone()
-                                try:
-                                    sqlite_env.ParseConfig('pkg-config --static --libs sqlite3')
-                                    for lib in sqlite_env['LIBS']:
-                                        if not lib in env['LIBS']:
-                                            env["SQLITE_LINKFLAGS"].append(lib)
-                                            env.Append(LIBS=lib)
-                                except OSError,e:
-                                    for lib in ["sqlite3","dl","pthread"]:
-                                        if not lib in env['LIBS']:
-                                            env["SQLITE_LINKFLAGS"].append("lib")
-                                            env.Append(LIBS=lib)
-                            else:
-                                for lib in ["sqlite3","dl","pthread"]:
-                                    if not lib in env['LIBS']:
-                                        env["SQLITE_LINKFLAGS"].append("lib")
-                                        env.Append(LIBS=lib)
-                        SQLITE_HAS_RTREE = conf.sqlite_has_rtree()
-                        if not SQLITE_HAS_RTREE:
-                            env.Replace(**sqlite_backup)
-                            if details['lib'] in env['LIBS']:
-                                env['LIBS'].remove(details['lib'])
-                            env['SKIPPED_DEPS'].append('sqlite_rtree')
-                        else:
-                            env.Replace(**sqlite_backup)
-                elif details['lib'] and details['inc']:
-                    if not conf.CheckLibWithHeader(details['lib'], details['inc'], details['lang']):
-                        env['SKIPPED_DEPS'].append(details['lib'])
-
-            # re-append the local paths for mapnik sources to the beginning of the list
-            # to make sure they come before any plugins that were 'prepended'
-            env.PrependUnique(CPPPATH = '#include', delete_existing=True)
-            env.PrependUnique(LIBPATH = '#src', delete_existing=True)
+	if env['HOST']:
+	    for plugin in env['REQUESTED_PLUGINS']:
+		details = env['PLUGINS'][plugin]
+		if details['lib']:
+		    env.AppendUnique(LIBS=details['lib'])
+	else:
+	    color_print(4,'Checking for requested plugins dependencies...')
+	    for plugin in env['REQUESTED_PLUGINS']:
+		details = env['PLUGINS'][plugin]
+		if plugin == 'gdal':
+		    if conf.parse_config('GDAL_CONFIG',checks='--libs'):
+			conf.parse_config('GDAL_CONFIG',checks='--cflags')
+			libname = conf.get_pkg_lib('GDAL_CONFIG','gdal')
+			if libname:
+			    if not conf.CheckLibWithHeader(libname, details['inc'], details['lang']):
+				env['SKIPPED_DEPS'].append('gdal')
+				if libname in env['LIBS']:
+				     env['LIBS'].remove(libname)
+			    else:
+				details['lib'] = libname
+		elif plugin == 'postgis' or plugin == 'pgraster':
+		    if env.get('PG_LIBS') or env.get('PG_INCLUDES'):
+			libname = details['lib']
+			if env.get('PG_INCLUDES'):
+			    inc_path = env['PG_INCLUDES']
+			    env.AppendUnique(CPPPATH = fix_path(inc_path))
+			if env.get('PG_LIBS'):
+			    lib_path = env['PG_LIBS']
+			    env.AppendUnique(LIBPATH = fix_path(lib_path))
+			if not conf.CheckLibWithHeader(libname, details['inc'], details['lang']):
+			    env['SKIPPED_DEPS'].append(libname)
+			    if libname in env['LIBS']:
+				 env['LIBS'].remove(libname)
+			else:
+			    details['lib'] = libname
+		    else:
+			conf.parse_pg_config('PG_CONFIG')
+		elif plugin == 'ogr':
+		    if conf.ogr_enabled():
+			if conf.parse_config('GDAL_CONFIG',checks='--libs'):
+			    conf.parse_config('GDAL_CONFIG',checks='--cflags')
+			    libname = conf.get_pkg_lib('GDAL_CONFIG','ogr')
+			    if libname:
+				if not conf.CheckLibWithHeader(libname, details['inc'], details['lang']):
+				    if 'gdal' not in env['SKIPPED_DEPS']:
+					env['SKIPPED_DEPS'].append('gdal')
+				    if libname in env['LIBS']:
+					 env['LIBS'].remove(libname)
+				else:
+				    details['lib'] = libname
+		elif details['path'] and details['lib'] and details['inc']:
+		    backup = env.Clone().Dictionary()
+		    # Note, the 'delete_existing' keyword makes sure that these paths are prepended
+		    # to the beginning of the path list even if they already exist
+		    incpath = env['%s_INCLUDES' % details['path']]
+		    libpath = env['%s_LIBS' % details['path']]
+		    env.PrependUnique(CPPPATH = fix_path(incpath),delete_existing=True)
+		    env.PrependUnique(LIBPATH = fix_path(libpath),delete_existing=True)
+		    if not conf.CheckLibWithHeader(details['lib'], details['inc'], details['lang']):
+			env.Replace(**backup)
+			env['SKIPPED_DEPS'].append(details['lib'])
+		    if plugin == 'sqlite':
+			sqlite_backup = env.Clone().Dictionary()
+			# if statically linking, on linux we likely
+			# need to link sqlite to pthreads and dl
+			if env['RUNTIME_LINK'] == 'static' and not env['PLATFORM'] == 'Darwin':
+			    if CHECK_PKG_CONFIG and conf.CheckPKG('sqlite3'):
+				sqlite_env = env.Clone()
+				try:
+				    sqlite_env.ParseConfig('pkg-config --static --libs sqlite3')
+				    for lib in sqlite_env['LIBS']:
+					if not lib in env['LIBS']:
+					    env["SQLITE_LINKFLAGS"].append(lib)
+					    env.Append(LIBS=lib)
+				except OSError,e:
+				    for lib in ["sqlite3","dl","pthread"]:
+					if not lib in env['LIBS']:
+					    env["SQLITE_LINKFLAGS"].append("lib")
+					    env.Append(LIBS=lib)
+			    else:
+				for lib in ["sqlite3","dl","pthread"]:
+				    if not lib in env['LIBS']:
+					env["SQLITE_LINKFLAGS"].append("lib")
+					env.Append(LIBS=lib)
+			SQLITE_HAS_RTREE = conf.sqlite_has_rtree()
+			if not SQLITE_HAS_RTREE:
+			    env.Replace(**sqlite_backup)
+			    if details['lib'] in env['LIBS']:
+				env['LIBS'].remove(details['lib'])
+			    env['SKIPPED_DEPS'].append('sqlite_rtree')
+			else:
+			    env.Replace(**sqlite_backup)
+		elif details['lib'] and details['inc']:
+		    if not conf.CheckLibWithHeader(details['lib'], details['inc'], details['lang']):
+			env['SKIPPED_DEPS'].append(details['lib'])
+
+	    # re-append the local paths for mapnik sources to the beginning of the list
+	    # to make sure they come before any plugins that were 'prepended'
+	    env.PrependUnique(CPPPATH = '#include', delete_existing=True)
+	    env.PrependUnique(LIBPATH = '#src', delete_existing=True)
 
     if not env['HOST']:
-        if env['PGSQL2SQLITE']:
-            if 'sqlite3' not in env['LIBS']:
-                env.AppendUnique(LIBS='sqlite3')
-                env.AppendUnique(CPPPATH = fix_path(env['SQLITE_INCLUDES']))
-                env.AppendUnique(LIBPATH = fix_path(env['SQLITE_LIBS']))
-            if 'pq' not in env['LIBS']:
-                if not conf.parse_pg_config('PG_CONFIG'):
-                    env['PGSQL2SQLITE'] = False
-            if not SQLITE_HAS_RTREE:
-                env['SKIPPED_DEPS'].append('pgsql2sqlite_rtree')
-                env['PGSQL2SQLITE'] = False
+	if env['PGSQL2SQLITE']:
+	    if 'sqlite3' not in env['LIBS']:
+		env.AppendUnique(LIBS='sqlite3')
+		env.AppendUnique(CPPPATH = fix_path(env['SQLITE_INCLUDES']))
+		env.AppendUnique(LIBPATH = fix_path(env['SQLITE_LIBS']))
+	    if 'pq' not in env['LIBS']:
+		if not conf.parse_pg_config('PG_CONFIG'):
+		    env['PGSQL2SQLITE'] = False
+	    if not SQLITE_HAS_RTREE:
+		env['SKIPPED_DEPS'].append('pgsql2sqlite_rtree')
+		env['PGSQL2SQLITE'] = False
 
     # we rely on an internal, patched copy of agg with critical fixes
     # prepend to make sure we link locally
@@ -1592,236 +1593,236 @@ if not preconfigured:
     env.Prepend(CPPPATH = '#deps')
 
     if env['CAIRO']:
-        if env['CAIRO_LIBS'] or env['CAIRO_INCLUDES']:
-            c_inc = env['CAIRO_INCLUDES']
-            if env['CAIRO_LIBS']:
-                env["CAIRO_LIBPATHS"].append(fix_path(env['CAIRO_LIBS']))
-                if not env['CAIRO_INCLUDES']:
-                    c_inc = env['CAIRO_LIBS'].replace('lib','',1)
-            if c_inc:
-                c_inc = os.path.normpath(fix_path(env['CAIRO_INCLUDES']))
-                if c_inc.endswith('include'):
-                    c_inc = os.path.dirname(c_inc)
-                env["CAIRO_CPPPATHS"].extend(
-                    [
-                      os.path.join(c_inc,'include/cairo'),
-                      os.path.join(c_inc,'include/pixman-1'),
-                      #os.path.join(c_inc,'include/freetype2'),
-                      #os.path.join(c_inc,'include/libpng'),
-                    ]
-                )
-                env["CAIRO_ALL_LIBS"] = ['cairo']
-                if env['RUNTIME_LINK'] == 'static':
-                    env["CAIRO_ALL_LIBS"].extend(
-                        ['pixman-1','expat']
-                    )
-                # todo - run actual checkLib?
-                env['HAS_CAIRO'] = True
-        else:
-            if not CHECK_PKG_CONFIG:
-                env['HAS_CAIRO'] = False
-                env['SKIPPED_DEPS'].append('pkg-config')
-                env['SKIPPED_DEPS'].append('cairo')
-            elif not conf.CheckPKG('cairo'):
-                env['HAS_CAIRO'] = False
-                env['SKIPPED_DEPS'].append('cairo')
-            else:
-                print 'Checking for cairo lib and include paths... ',
-                cmd = 'pkg-config --libs --cflags cairo'
-                if env['RUNTIME_LINK'] == 'static':
-                    cmd += ' --static'
-                cairo_env = env.Clone()
-                try:
-                    cairo_env.ParseConfig(cmd)
-                    for lib in cairo_env['LIBS']:
-                        if not lib in env['LIBS']:
-                            env["CAIRO_ALL_LIBS"].append(lib)
-                    for lpath in cairo_env['LIBPATH']:
-                        if not lpath in env['LIBPATH']:
-                            env["CAIRO_LIBPATHS"].append(lpath)
-                    for inc in cairo_env['CPPPATH']:
-                        if not inc in env['CPPPATH']:
-                            env["CAIRO_CPPPATHS"].append(inc)
-                    env['HAS_CAIRO'] = True
-                    print 'yes'
-                except OSError,e:
-                    color_print(1,'no')
-                    env['SKIPPED_DEPS'].append('cairo')
-                    color_print(1,'pkg-config reported: %s' % e)
+	if env['CAIRO_LIBS'] or env['CAIRO_INCLUDES']:
+	    c_inc = env['CAIRO_INCLUDES']
+	    if env['CAIRO_LIBS']:
+		env["CAIRO_LIBPATHS"].append(fix_path(env['CAIRO_LIBS']))
+		if not env['CAIRO_INCLUDES']:
+		    c_inc = env['CAIRO_LIBS'].replace('lib','',1)
+	    if c_inc:
+		c_inc = os.path.normpath(fix_path(env['CAIRO_INCLUDES']))
+		if c_inc.endswith('include'):
+		    c_inc = os.path.dirname(c_inc)
+		env["CAIRO_CPPPATHS"].extend(
+		    [
+		      os.path.join(c_inc,'include/cairo'),
+		      os.path.join(c_inc,'include/pixman-1'),
+		      #os.path.join(c_inc,'include/freetype2'),
+		      #os.path.join(c_inc,'include/libpng'),
+		    ]
+		)
+		env["CAIRO_ALL_LIBS"] = ['cairo']
+		if env['RUNTIME_LINK'] == 'static':
+		    env["CAIRO_ALL_LIBS"].extend(
+			['pixman-1','expat']
+		    )
+		# todo - run actual checkLib?
+		env['HAS_CAIRO'] = True
+	else:
+	    if not CHECK_PKG_CONFIG:
+		env['HAS_CAIRO'] = False
+		env['SKIPPED_DEPS'].append('pkg-config')
+		env['SKIPPED_DEPS'].append('cairo')
+	    elif not conf.CheckPKG('cairo'):
+		env['HAS_CAIRO'] = False
+		env['SKIPPED_DEPS'].append('cairo')
+	    else:
+		print 'Checking for cairo lib and include paths... ',
+		cmd = 'pkg-config --libs --cflags cairo'
+		if env['RUNTIME_LINK'] == 'static':
+		    cmd += ' --static'
+		cairo_env = env.Clone()
+		try:
+		    cairo_env.ParseConfig(cmd)
+		    for lib in cairo_env['LIBS']:
+			if not lib in env['LIBS']:
+			    env["CAIRO_ALL_LIBS"].append(lib)
+		    for lpath in cairo_env['LIBPATH']:
+			if not lpath in env['LIBPATH']:
+			    env["CAIRO_LIBPATHS"].append(lpath)
+		    for inc in cairo_env['CPPPATH']:
+			if not inc in env['CPPPATH']:
+			    env["CAIRO_CPPPATHS"].append(inc)
+		    env['HAS_CAIRO'] = True
+		    print 'yes'
+		except OSError,e:
+		    color_print(1,'no')
+		    env['SKIPPED_DEPS'].append('cairo')
+		    color_print(1,'pkg-config reported: %s' % e)
 
     else:
-        color_print(4,'Not building with cairo support, pass CAIRO=True to enable')
+	color_print(4,'Not building with cairo support, pass CAIRO=True to enable')
 
     if not env['HOST'] and env['HAS_CAIRO']:
-        if not conf.CheckCairoHasFreetype():
-            env['SKIPPED_DEPS'].append('cairo')
-            env['HAS_CAIRO'] = False
+	if not conf.CheckCairoHasFreetype():
+	    env['SKIPPED_DEPS'].append('cairo')
+	    env['HAS_CAIRO'] = False
 
     #### End Config Stage for Required Dependencies ####
 
     if env['MISSING_DEPS']:
-        # if required dependencies are missing, print warnings and then let SCons finish without building or saving local config
-        color_print(1,'\nExiting... the following required dependencies were not found:\n   - %s' % '\n   - '.join([pretty_dep(dep) for dep in env['MISSING_DEPS']]))
-        color_print(1,"\nSee '%s' for details on possible problems." % (fix_path(SCONS_LOCAL_LOG)))
-        if env['SKIPPED_DEPS']:
-            color_print(4,'\nAlso, these OPTIONAL dependencies were not found:\n   - %s' % '\n   - '.join([pretty_dep(dep) for dep in env['SKIPPED_DEPS']]))
-        color_print(4,"\nSet custom paths to these libraries and header files on the command-line or in a file called '%s'" % SCONS_LOCAL_CONFIG)
-        color_print(4,"    ie. $ python scons/scons.py BOOST_INCLUDES=/usr/local/include BOOST_LIBS=/usr/local/lib")
-        color_print(4, "\nOnce all required dependencies are found a local '%s' will be saved and then install:" % SCONS_LOCAL_CONFIG)
-        color_print(4,"    $ sudo python scons/scons.py install")
-        color_print(4,"\nTo view available path variables:\n    $ python scons/scons.py --help or -h")
-        color_print(4,'\nTo view overall SCons help options:\n    $ python scons/scons.py --help-options or -H\n')
-        color_print(4,'More info: https://github.com/mapnik/mapnik/wiki/Mapnik-Installation')
-        if not HELP_REQUESTED:
-            Exit(1)
+	# if required dependencies are missing, print warnings and then let SCons finish without building or saving local config
+	color_print(1,'\nExiting... the following required dependencies were not found:\n   - %s' % '\n   - '.join([pretty_dep(dep) for dep in env['MISSING_DEPS']]))
+	color_print(1,"\nSee '%s' for details on possible problems." % (fix_path(SCONS_LOCAL_LOG)))
+	if env['SKIPPED_DEPS']:
+	    color_print(4,'\nAlso, these OPTIONAL dependencies were not found:\n   - %s' % '\n   - '.join([pretty_dep(dep) for dep in env['SKIPPED_DEPS']]))
+	color_print(4,"\nSet custom paths to these libraries and header files on the command-line or in a file called '%s'" % SCONS_LOCAL_CONFIG)
+	color_print(4,"    ie. $ python scons/scons.py BOOST_INCLUDES=/usr/local/include BOOST_LIBS=/usr/local/lib")
+	color_print(4, "\nOnce all required dependencies are found a local '%s' will be saved and then install:" % SCONS_LOCAL_CONFIG)
+	color_print(4,"    $ sudo python scons/scons.py install")
+	color_print(4,"\nTo view available path variables:\n    $ python scons/scons.py --help or -h")
+	color_print(4,'\nTo view overall SCons help options:\n    $ python scons/scons.py --help-options or -H\n')
+	color_print(4,'More info: https://github.com/mapnik/mapnik/wiki/Mapnik-Installation')
+	if not HELP_REQUESTED:
+	    Exit(1)
     else:
-        # Save the custom variables in a SCONS_LOCAL_CONFIG
-        # that will be reloaded to allow for `install` without re-specifying custom variables
-        color_print(4,"\nAll Required dependencies found!\n")
-        if env['USE_CONFIG']:
-            if os.path.exists(SCONS_LOCAL_CONFIG):
-                action = 'Overwriting and re-saving'
-                os.unlink(SCONS_LOCAL_CONFIG)
-            else:
-                action = 'Saving new'
-            color_print(4,"%s file '%s'..." % (action,SCONS_LOCAL_CONFIG))
-            color_print(4,"Will hold custom path variables from commandline and python config file(s)...")
-            opts.Save(SCONS_LOCAL_CONFIG,env)
-        else:
-          color_print(4,"Did not use user config file, no custom path variables will be saved...")
-
-        if env['SKIPPED_DEPS']:
-            color_print(4,'\nNote: will build without these OPTIONAL dependencies:\n   - %s' % '\n   - '.join([pretty_dep(dep) for dep in env['SKIPPED_DEPS']]))
-            print
-
-        # fetch the mapnik version header in order to set the
-        # ABI version used to build libmapnik.so on linux in src/build.py
-        abi = GetMapnikLibVersion()
-        abi_no_pre = abi.replace('-pre','').split('.')
-        env['ABI_VERSION'] = abi_no_pre
-        env['MAPNIK_VERSION_STRING'] = abi
-        env['MAPNIK_VERSION'] = str(int(abi_no_pre[0])*100000+int(abi_no_pre[1])*100+int(abi_no_pre[2]))
-
-        # Common DEFINES.
-        env.Append(CPPDEFINES = '-D%s' % env['PLATFORM'].upper())
-        if env['THREADING'] == 'multi':
-            env.Append(CPPDEFINES = '-DMAPNIK_THREADSAFE')
-
-        if env['NO_ATEXIT']:
-            env.Append(CPPDEFINES = '-DMAPNIK_NO_ATEXIT')
-
-        # Mac OSX (Darwin) special settings
-        if env['PLATFORM'] == 'Darwin':
-            pthread = ''
-        else:
-            pthread = '-pthread'
-
-        # Common debugging flags.
-        # http://lists.fedoraproject.org/pipermail/devel/2010-November/144952.html
-        debug_flags  = ['-g', '-fno-omit-frame-pointer']
-        debug_defines = ['-DDEBUG', '-DMAPNIK_DEBUG']
-        ndebug_defines = ['-DNDEBUG']
-
-        # faster compile
-        # http://www.boost.org/doc/libs/1_47_0/libs/spirit/doc/html/spirit/what_s_new/spirit_2_5.html#spirit.what_s_new.spirit_2_5.breaking_changes
-        env.Append(CPPDEFINES = '-DBOOST_SPIRIT_NO_PREDEFINED_TERMINALS=1')
-        env.Append(CPPDEFINES = '-DBOOST_PHOENIX_NO_PREDEFINED_TERMINALS=1')
-        # c++11 support / https://github.com/mapnik/mapnik/issues/1683
-        #  - upgrade to PHOENIX_V3 since that is needed for c++11 compile
-        env.Append(CPPDEFINES = '-DBOOST_SPIRIT_USE_PHOENIX_V3=1')
-
-        # Enable logging in debug mode (always) and release mode (when specified)
-        if env['DEFAULT_LOG_SEVERITY']:
-            if env['DEFAULT_LOG_SEVERITY'] not in severities:
-                severities_list = ', '.join(["'%s'" % s for s in severities])
-                color_print(1,"Cannot set default logger severity to '%s', available options are %s." % (env['DEFAULT_LOG_SEVERITY'], severities_list))
-                Exit(1)
-            else:
-                log_severity = severities.index(env['DEFAULT_LOG_SEVERITY'])
-        else:
-            severities_list = ', '.join(["'%s'" % s for s in severities])
-            color_print(1,"No logger severity specified, available options are %s." % severities_list)
-            Exit(1)
-
-        log_enabled = ['-DMAPNIK_LOG', '-DMAPNIK_DEFAULT_LOG_SEVERITY=%d' % log_severity]
-
-        if env['DEBUG']:
-            debug_defines += log_enabled
-        else:
-            if env['ENABLE_LOG']:
-                ndebug_defines += log_enabled
-
-        # Enable statistics reporting
-        if env['ENABLE_STATS']:
-            debug_defines.append('-DMAPNIK_STATS')
-            ndebug_defines.append('-DMAPNIK_STATS')
-
-        # Add rdynamic to allow using statics between application and plugins
-        # http://stackoverflow.com/questions/8623657/multiple-instances-of-singleton-across-shared-libraries-on-linux
-        if env['PLATFORM'] != 'Darwin' and env['CXX'] == 'g++':
-            env.MergeFlags('-rdynamic')
-
-        if env['DEBUG']:
-            env.Append(CXXFLAGS = debug_flags)
-            env.Append(CPPDEFINES = debug_defines)
-        else:
-            env.Append(CPPDEFINES = ndebug_defines)
-
-        # Common flags for g++/clang++ CXX compiler.
-        # TODO: clean up code more to make -Wextra -Wsign-compare -Wsign-conversion -Wconversion viable
-        common_cxx_flags = '-Wall %s %s -ftemplate-depth-300 -Wsign-compare -Wshadow ' % (env['WARNING_CXXFLAGS'], pthread)
-
-        if 'clang++' in env['CXX']:
-            common_cxx_flags += ' -Wno-unknown-pragmas -Wno-unsequenced '
-        elif 'g++' in env['CXX']:
-            common_cxx_flags += ' -Wno-pragmas '
-
-        if env['DEBUG']:
-            env.Append(CXXFLAGS = common_cxx_flags + '-O0')
-        else:
-            # TODO - add back -fvisibility-inlines-hidden
-            # https://github.com/mapnik/mapnik/issues/1863
-            env.Append(CXXFLAGS = common_cxx_flags + '-O%s' % (env['OPTIMIZATION']))
-        if env['DEBUG_UNDEFINED']:
-            env.Append(CXXFLAGS = '-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -ftrapv -fwrapv')
-
-        # if requested, sort LIBPATH and CPPPATH one last time before saving...
-        if env['PRIORITIZE_LINKING']:
-            conf.prioritize_paths(silent=True)
-
-        # finish config stage and pickle results
-        env = conf.Finish()
-        env_cache = open(SCONS_CONFIGURE_CACHE, 'w')
-        pickle_dict = {}
-        for i in pickle_store:
-            pickle_dict[i] = env.get(i)
-        pickle.dump(pickle_dict,env_cache)
-        env_cache.close()
-        # fix up permissions on configure outputs
-        # this is hackish but avoids potential problems
-        # with a non-root configure following a root install
-        # that also triggered a re-configure
-        try:
-            os.chmod(SCONS_CONFIGURE_CACHE,0666)
-        except: pass
-        try:
-            os.chmod(SCONS_LOCAL_CONFIG,0666)
-        except: pass
-        try:
-            os.chmod('.sconsign.dblite',0666)
-        except: pass
-        try:
-            os.chmod(SCONS_LOCAL_LOG,0666)
-        except: pass
-        try:
-            for item in glob('%s/*' % SCONF_TEMP_DIR):
-                os.chmod(item,0666)
-        except: pass
-
-        if 'configure' in command_line_args:
-            color_print(4,'\nConfigure completed: run `make` to build or `make install`')
-            if not HELP_REQUESTED:
-                Exit(0)
+	# Save the custom variables in a SCONS_LOCAL_CONFIG
+	# that will be reloaded to allow for `install` without re-specifying custom variables
+	color_print(4,"\nAll Required dependencies found!\n")
+	if env['USE_CONFIG']:
+	    if os.path.exists(SCONS_LOCAL_CONFIG):
+		action = 'Overwriting and re-saving'
+		os.unlink(SCONS_LOCAL_CONFIG)
+	    else:
+		action = 'Saving new'
+	    color_print(4,"%s file '%s'..." % (action,SCONS_LOCAL_CONFIG))
+	    color_print(4,"Will hold custom path variables from commandline and python config file(s)...")
+	    opts.Save(SCONS_LOCAL_CONFIG,env)
+	else:
+	  color_print(4,"Did not use user config file, no custom path variables will be saved...")
+
+	if env['SKIPPED_DEPS']:
+	    color_print(4,'\nNote: will build without these OPTIONAL dependencies:\n   - %s' % '\n   - '.join([pretty_dep(dep) for dep in env['SKIPPED_DEPS']]))
+	    print
+
+	# fetch the mapnik version header in order to set the
+	# ABI version used to build libmapnik.so on linux in src/build.py
+	abi = GetMapnikLibVersion()
+	abi_no_pre = abi.replace('-pre','').split('.')
+	env['ABI_VERSION'] = abi_no_pre
+	env['MAPNIK_VERSION_STRING'] = abi
+	env['MAPNIK_VERSION'] = str(int(abi_no_pre[0])*100000+int(abi_no_pre[1])*100+int(abi_no_pre[2]))
+
+	# Common DEFINES.
+	env.Append(CPPDEFINES = '-D%s' % env['PLATFORM'].upper())
+	if env['THREADING'] == 'multi':
+	    env.Append(CPPDEFINES = '-DMAPNIK_THREADSAFE')
+
+	if env['NO_ATEXIT']:
+	    env.Append(CPPDEFINES = '-DMAPNIK_NO_ATEXIT')
+
+	# Mac OSX (Darwin) special settings
+	if env['PLATFORM'] == 'Darwin':
+	    pthread = ''
+	else:
+	    pthread = '-pthread'
+
+	# Common debugging flags.
+	# http://lists.fedoraproject.org/pipermail/devel/2010-November/144952.html
+	debug_flags  = ['-g', '-fno-omit-frame-pointer']
+	debug_defines = ['-DDEBUG', '-DMAPNIK_DEBUG']
+	ndebug_defines = ['-DNDEBUG']
+
+	# faster compile
+	# http://www.boost.org/doc/libs/1_47_0/libs/spirit/doc/html/spirit/what_s_new/spirit_2_5.html#spirit.what_s_new.spirit_2_5.breaking_changes
+	env.Append(CPPDEFINES = '-DBOOST_SPIRIT_NO_PREDEFINED_TERMINALS=1')
+	env.Append(CPPDEFINES = '-DBOOST_PHOENIX_NO_PREDEFINED_TERMINALS=1')
+	# c++11 support / https://github.com/mapnik/mapnik/issues/1683
+	#  - upgrade to PHOENIX_V3 since that is needed for c++11 compile
+	env.Append(CPPDEFINES = '-DBOOST_SPIRIT_USE_PHOENIX_V3=1')
+
+	# Enable logging in debug mode (always) and release mode (when specified)
+	if env['DEFAULT_LOG_SEVERITY']:
+	    if env['DEFAULT_LOG_SEVERITY'] not in severities:
+		severities_list = ', '.join(["'%s'" % s for s in severities])
+		color_print(1,"Cannot set default logger severity to '%s', available options are %s." % (env['DEFAULT_LOG_SEVERITY'], severities_list))
+		Exit(1)
+	    else:
+		log_severity = severities.index(env['DEFAULT_LOG_SEVERITY'])
+	else:
+	    severities_list = ', '.join(["'%s'" % s for s in severities])
+	    color_print(1,"No logger severity specified, available options are %s." % severities_list)
+	    Exit(1)
+
+	log_enabled = ['-DMAPNIK_LOG', '-DMAPNIK_DEFAULT_LOG_SEVERITY=%d' % log_severity]
+
+	if env['DEBUG']:
+	    debug_defines += log_enabled
+	else:
+	    if env['ENABLE_LOG']:
+		ndebug_defines += log_enabled
+
+	# Enable statistics reporting
+	if env['ENABLE_STATS']:
+	    debug_defines.append('-DMAPNIK_STATS')
+	    ndebug_defines.append('-DMAPNIK_STATS')
+
+	# Add rdynamic to allow using statics between application and plugins
+	# http://stackoverflow.com/questions/8623657/multiple-instances-of-singleton-across-shared-libraries-on-linux
+	if env['PLATFORM'] != 'Darwin' and env['CXX'] == 'g++':
+	    env.MergeFlags('-rdynamic')
+
+	if env['DEBUG']:
+	    env.Append(CXXFLAGS = debug_flags)
+	    env.Append(CPPDEFINES = debug_defines)
+	else:
+	    env.Append(CPPDEFINES = ndebug_defines)
+
+	# Common flags for g++/clang++ CXX compiler.
+	# TODO: clean up code more to make -Wextra -Wsign-compare -Wsign-conversion -Wconversion viable
+	common_cxx_flags = '-Wall %s %s -ftemplate-depth-300 -Wsign-compare -Wshadow ' % (env['WARNING_CXXFLAGS'], pthread)
+
+	if 'clang++' in env['CXX']:
+	    common_cxx_flags += ' -Wno-unknown-pragmas -Wno-unsequenced '
+	elif 'g++' in env['CXX']:
+	    common_cxx_flags += ' -Wno-pragmas '
+
+	if env['DEBUG']:
+	    env.Append(CXXFLAGS = common_cxx_flags + '-O0')
+	else:
+	    # TODO - add back -fvisibility-inlines-hidden
+	    # https://github.com/mapnik/mapnik/issues/1863
+	    env.Append(CXXFLAGS = common_cxx_flags + '-O%s' % (env['OPTIMIZATION']))
+	if env['DEBUG_UNDEFINED']:
+	    env.Append(CXXFLAGS = '-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -ftrapv -fwrapv')
+
+	# if requested, sort LIBPATH and CPPPATH one last time before saving...
+	if env['PRIORITIZE_LINKING']:
+	    conf.prioritize_paths(silent=True)
+
+	# finish config stage and pickle results
+	env = conf.Finish()
+	env_cache = open(SCONS_CONFIGURE_CACHE, 'w')
+	pickle_dict = {}
+	for i in pickle_store:
+	    pickle_dict[i] = env.get(i)
+	pickle.dump(pickle_dict,env_cache)
+	env_cache.close()
+	# fix up permissions on configure outputs
+	# this is hackish but avoids potential problems
+	# with a non-root configure following a root install
+	# that also triggered a re-configure
+	try:
+	    os.chmod(SCONS_CONFIGURE_CACHE,0666)
+	except: pass
+	try:
+	    os.chmod(SCONS_LOCAL_CONFIG,0666)
+	except: pass
+	try:
+	    os.chmod('.sconsign.dblite',0666)
+	except: pass
+	try:
+	    os.chmod(SCONS_LOCAL_LOG,0666)
+	except: pass
+	try:
+	    for item in glob('%s/*' % SCONF_TEMP_DIR):
+		os.chmod(item,0666)
+	except: pass
+
+	if 'configure' in command_line_args:
+	    color_print(4,'\nConfigure completed: run `make` to build or `make install`')
+	    if not HELP_REQUESTED:
+		Exit(0)
 
 # autogenerate help on default/current SCons options
 Help(opts.GenerateHelpText(env))
@@ -1830,43 +1831,43 @@ Help(opts.GenerateHelpText(env))
 if not HELP_REQUESTED:
 
     if 'uninstall' in COMMAND_LINE_TARGETS:
-        # dummy action in case there is nothing to uninstall, to avoid phony error..
-        env.Alias("uninstall", "")
+	# dummy action in case there is nothing to uninstall, to avoid phony error..
+	env.Alias("uninstall", "")
     env['create_uninstall_target'] = create_uninstall_target
 
     if env['PKG_CONFIG_PATH']:
-        env['ENV']['PKG_CONFIG_PATH'] = fix_path(env['PKG_CONFIG_PATH'])
-        # otherwise this variable == os.environ["PKG_CONFIG_PATH"]
+	env['ENV']['PKG_CONFIG_PATH'] = fix_path(env['PKG_CONFIG_PATH'])
+	# otherwise this variable == os.environ["PKG_CONFIG_PATH"]
 
     if env['PATH']:
-        env['ENV']['PATH'] = fix_path(env['PATH']) + ':' + env['ENV']['PATH']
+	env['ENV']['PATH'] = fix_path(env['PATH']) + ':' + env['ENV']['PATH']
 
     if env['PATH_REMOVE']:
-        for p in env['PATH_REMOVE'].split(':'):
-            if p in env['ENV']['PATH']:
-                env['ENV']['PATH'].replace(p,'')
-            rm_path(p,'LIBPATH',env)
-            rm_path(p,'CPPPATH',env)
-            rm_path(p,'CXXFLAGS',env)
-            rm_path(p,'CAIRO_LIBPATHS',env)
-            rm_path(p,'CAIRO_CPPPATHS',env)
+	for p in env['PATH_REMOVE'].split(':'):
+	    if p in env['ENV']['PATH']:
+		env['ENV']['PATH'].replace(p,'')
+	    rm_path(p,'LIBPATH',env)
+	    rm_path(p,'CPPPATH',env)
+	    rm_path(p,'CXXFLAGS',env)
+	    rm_path(p,'CAIRO_LIBPATHS',env)
+	    rm_path(p,'CAIRO_CPPPATHS',env)
 
     if env['PATH_REPLACE']:
-        searches,replace = env['PATH_REPLACE'].split(':')
-        for search in searches.split(','):
-            if search in env['ENV']['PATH']:
-                env['ENV']['PATH'] = os.path.abspath(env['ENV']['PATH'].replace(search,replace))
-            def replace_path(set,s,r):
-                idx = 0
-                for i in env[set]:
-                    if s in i:
-                        env[set][idx] = os.path.abspath(env[set][idx].replace(s,r))
-                    idx +=1
-            replace_path('LIBPATH',search,replace)
-            replace_path('CPPPATH',search,replace)
-            replace_path('CXXFLAGS',search,replace)
-            replace_path('CAIRO_LIBPATHS',search,replace)
-            replace_path('CAIRO_CPPPATHS',search,replace)
+	searches,replace = env['PATH_REPLACE'].split(':')
+	for search in searches.split(','):
+	    if search in env['ENV']['PATH']:
+		env['ENV']['PATH'] = os.path.abspath(env['ENV']['PATH'].replace(search,replace))
+	    def replace_path(set,s,r):
+		idx = 0
+		for i in env[set]:
+		    if s in i:
+			env[set][idx] = os.path.abspath(env[set][idx].replace(s,r))
+		    idx +=1
+	    replace_path('LIBPATH',search,replace)
+	    replace_path('CPPPATH',search,replace)
+	    replace_path('CXXFLAGS',search,replace)
+	    replace_path('CAIRO_LIBPATHS',search,replace)
+	    replace_path('CAIRO_CPPPATHS',search,replace)
 
     # export env so it is available in build.py files
     Export('env')
@@ -1876,15 +1877,15 @@ if not HELP_REQUESTED:
     Export('plugin_base')
 
     if env['FAST']:
-        # caching is 'auto' by default in SCons
-        # But let's also cache implicit deps...
-        EnsureSConsVersion(0,98)
-        SetOption('implicit_cache', 1)
-        SetOption('max_drift', 1)
+	# caching is 'auto' by default in SCons
+	# But let's also cache implicit deps...
+	EnsureSConsVersion(0,98)
+	SetOption('implicit_cache', 1)
+	SetOption('max_drift', 1)
 
     # Build agg first, doesn't need anything special
     if env['RUNTIME_LINK'] == 'shared':
-        SConscript('deps/agg/build.py')
+	SConscript('deps/agg/build.py')
 
     # Build spirit grammars
     SConscript('src/json/build.py')
@@ -1905,76 +1906,78 @@ if not HELP_REQUESTED:
     POSTGIS_BUILT = False
     PGRASTER_BUILT = False
     for plugin in env['PLUGINS']:
-        if env['PLUGIN_LINKING'] == 'static' or plugin not in env['REQUESTED_PLUGINS']:
-            if os.path.exists('plugins/input/%s.input' % plugin):
-                os.unlink('plugins/input/%s.input' % plugin)
-        elif plugin in env['REQUESTED_PLUGINS']:
-            details = env['PLUGINS'][plugin]
-            if details['lib'] in env['LIBS']:
-                if env['PLUGIN_LINKING'] == 'shared':
-                    SConscript('plugins/input/%s/build.py' % plugin)
-                # hack to avoid breaking on plugins with the same dep
-                if plugin == 'ogr': OGR_BUILT = True
-                if plugin == 'gdal': GDAL_BUILT = True
-                if plugin == 'postgis': POSTGIS_BUILT = True
-                if plugin == 'pgraster': PGRASTER_BUILT = True
-                if plugin == 'ogr' or plugin == 'gdal':
-                    if GDAL_BUILT and OGR_BUILT:
-                        env['LIBS'].remove(details['lib'])
-                elif plugin == 'postgis' or plugin == 'pgraster':
-                    if POSTGIS_BUILT and PGRASTER_BUILT:
-                        env['LIBS'].remove(details['lib'])
-                else:
-                    env['LIBS'].remove(details['lib'])
-            elif not details['lib']:
-                if env['PLUGIN_LINKING'] == 'shared':
-                    # build internal datasource input plugins
-                    SConscript('plugins/input/%s/build.py' % plugin)
-            else:
-                color_print(1,"Notice: dependencies not met for plugin '%s', not building..." % plugin)
-                if os.path.exists('plugins/input/%s.input' % plugin):
-                    os.unlink('plugins/input/%s.input' % plugin)
+	if env['PLUGIN_LINKING'] == 'static' or plugin not in env['REQUESTED_PLUGINS']:
+	    if os.path.exists('plugins/input/%s.input' % plugin):
+		os.unlink('plugins/input/%s.input' % plugin)
+	elif plugin in env['REQUESTED_PLUGINS']:
+	    details = env['PLUGINS'][plugin]
+	    if details['lib'] in env['LIBS']:
+		if env['PLUGIN_LINKING'] == 'shared':
+		    SConscript('plugins/input/%s/build.py' % plugin)
+		# hack to avoid breaking on plugins with the same dep
+		if plugin == 'ogr': OGR_BUILT = True
+		if plugin == 'gdal': GDAL_BUILT = True
+		if plugin == 'postgis': POSTGIS_BUILT = True
+		if plugin == 'pgraster': PGRASTER_BUILT = True
+		if plugin == 'ogr' or plugin == 'gdal':
+		    if GDAL_BUILT and OGR_BUILT:
+			env['LIBS'].remove(details['lib'])
+		elif plugin == 'postgis' or plugin == 'pgraster':
+		    if POSTGIS_BUILT and PGRASTER_BUILT:
+			env['LIBS'].remove(details['lib'])
+		else:
+		    env['LIBS'].remove(details['lib'])
+	    elif not details['lib']:
+		if env['PLUGIN_LINKING'] == 'shared':
+		    # build internal datasource input plugins
+		    SConscript('plugins/input/%s/build.py' % plugin)
+	    else:
+		color_print(1,"Notice: dependencies not met for plugin '%s', not building..." % plugin)
+		if os.path.exists('plugins/input/%s.input' % plugin):
+		    os.unlink('plugins/input/%s.input' % plugin)
 
     create_uninstall_target(env, env['MAPNIK_LIB_DIR_DEST'], False)
     create_uninstall_target(env, env['MAPNIK_INPUT_PLUGINS_DEST'] , False)
 
     if 'install' in COMMAND_LINE_TARGETS:
-        # if statically linking plugins still make sure
-        # to create the dynamic plugins directory
-        if env['PLUGIN_LINKING'] == 'static':
-            if not os.path.exists(env['MAPNIK_INPUT_PLUGINS_DEST']):
-                os.makedirs(env['MAPNIK_INPUT_PLUGINS_DEST'])
-        # before installing plugins, wipe out any previously
-        # installed plugins that we are no longer building
-        for plugin in PLUGINS.keys():
-            plugin_path = os.path.join(env['MAPNIK_INPUT_PLUGINS_DEST'],'%s.input' % plugin)
-            if os.path.exists(plugin_path):
-                if plugin not in env['REQUESTED_PLUGINS'] or env['PLUGIN_LINKING'] == 'static':
-                    color_print(4,"Notice: removing out of date plugin: '%s'" % plugin_path)
-                    os.unlink(plugin_path)
+	# if statically linking plugins still make sure
+	# to create the dynamic plugins directory
+	if env['PLUGIN_LINKING'] == 'static':
+	    if not os.path.exists(env['MAPNIK_INPUT_PLUGINS_DEST']):
+		os.makedirs(env['MAPNIK_INPUT_PLUGINS_DEST'])
+	# before installing plugins, wipe out any previously
+	# installed plugins that we are no longer building
+	for plugin in PLUGINS.keys():
+	    plugin_path = os.path.join(env['MAPNIK_INPUT_PLUGINS_DEST'],'%s.input' % plugin)
+	    if os.path.exists(plugin_path):
+		if plugin not in env['REQUESTED_PLUGINS'] or env['PLUGIN_LINKING'] == 'static':
+		    color_print(4,"Notice: removing out of date plugin: '%s'" % plugin_path)
+		    os.unlink(plugin_path)
 
     # Build the c++ rundemo app if requested
     if not env['HOST']:
-        if env['DEMO']:
-            SConscript('demo/c++/build.py')
+	if env['DEMO']:
+	    SConscript('demo/c++/build.py')
 
     # Build shapeindex and remove its dependency from the LIBS
     if not env['HOST']:
-        if 'boost_program_options%s' % env['BOOST_APPEND'] in env['LIBS']:
-            if env['SHAPEINDEX']:
-                SConscript('utils/shapeindex/build.py')
-            # Build the pgsql2psqlite app if requested
-            if env['PGSQL2SQLITE']:
-                SConscript('utils/pgsql2sqlite/build.py')
-            if env['SVG2PNG']:
-                SConscript('utils/svg2png/build.py')
-            if env['NIK2IMG']:
-                SConscript('utils/nik2img/build.py')
-            # devtools not ready for public
-            #SConscript('utils/ogrindex/build.py')
-            env['LIBS'].remove('boost_program_options%s' % env['BOOST_APPEND'])
-        else :
-            color_print(1,"WARNING: Cannot find boost_program_options. 'shapeindex' and other command line programs will not be available")
+	if 'boost_program_options%s' % env['BOOST_APPEND'] in env['LIBS']:
+	    if env['SHAPEINDEX']:
+		SConscript('utils/shapeindex/build.py')
+	    if env['MAPNIK_INDEX']:
+		SConscript('utils/mapnik-index/build.py')
+	    # Build the pgsql2psqlite app if requested
+	    if env['PGSQL2SQLITE']:
+		SConscript('utils/pgsql2sqlite/build.py')
+	    if env['SVG2PNG']:
+		SConscript('utils/svg2png/build.py')
+	    if env['NIK2IMG']:
+		SConscript('utils/nik2img/build.py')
+	    # devtools not ready for public
+	    #SConscript('utils/ogrindex/build.py')
+	    env['LIBS'].remove('boost_program_options%s' % env['BOOST_APPEND'])
+	else :
+	    color_print(1,"WARNING: Cannot find boost_program_options. 'shapeindex' and other command line programs will not be available")
 
     # Configure fonts and if requested install the bundled DejaVu fonts
     SConscript('fonts/build.py')
@@ -1983,10 +1986,10 @@ if not HELP_REQUESTED:
     SConscript('test/build.py')
 
     if env['BENCHMARK']:
-        SConscript('benchmark/build.py')
+	SConscript('benchmark/build.py')
 
     if os.path.exists('./bindings/python/build.py'):
-        SConscript('./bindings/python/build.py')
+	SConscript('./bindings/python/build.py')
 
     # install mapnik-config script
     SConscript('utils/mapnik-config/build.py')
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..b227544
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,26 @@
+environment:
+  msvs_toolset: 14
+  BOOST_VERSION: 58
+  FASTBUILD: 1
+  matrix:
+    - platform: x64
+      configuration: Release
+
+os: Visual Studio 2015
+
+shallow_clone: true
+
+install:
+  - scripts\build-appveyor.bat
+
+artifacts:
+  - path: mapnik-gyp\msbuild-summary.txt
+    name: msbuild-summary.txt
+  - path: mapnik-gyp\msbuild-errors.txt
+    name: msbuild-errors.txt
+  - path: mapnik-gyp\msbuild-warnings.txt
+    name: msbuild-warnings.txt
+
+build: off
+test: off
+deploy: off
diff --git a/benchmark/build.py b/benchmark/build.py
index 800a9ab..37ba867 100644
--- a/benchmark/build.py
+++ b/benchmark/build.py
@@ -47,6 +47,7 @@ benchmarks = [
     "test_marker_cache.cpp",
     "test_quad_tree.cpp",
     "test_noop_rendering.cpp",
+    "test_getline.cpp",
 #    "test_numeric_cast_vs_static_cast.cpp",
 ]
 for cpp_test in benchmarks:
diff --git a/benchmark/run b/benchmark/run
index f918539..00285b2 100755
--- a/benchmark/run
+++ b/benchmark/run
@@ -9,7 +9,7 @@ function run {
     ${BASE}/$1 --threads 0 --iterations $3;
     ${BASE}/$1 --threads $2 --iterations $(expr $3 / $2);
 }
-
+run test_getline 30 10000000
 #run test_array_allocation 20 100000
 #run test_png_encoding1 10 1000
 #run test_png_encoding2 10 50
diff --git a/benchmark/test_getline.cpp b/benchmark/test_getline.cpp
new file mode 100644
index 0000000..1791b06
--- /dev/null
+++ b/benchmark/test_getline.cpp
@@ -0,0 +1,125 @@
+#include "bench_framework.hpp"
+#include <cstring>
+#include <cstdlib>
+#include "../plugins/input/csv/csv_utils.hpp"
+
+class test : public benchmark::test_case
+{
+public:
+    std::string line_data_;
+    test(mapnik::parameters const& params)
+     : test_case(params),
+       line_data_("this is one line\nand this is a second line\nand a third line")
+       {
+          boost::optional<std::string> line_data = params.get<std::string>("line");
+          if (line_data)
+          {
+              line_data_ = *line_data;
+          }
+       }
+
+    bool validate() const
+    {
+        std::string first = line_data_.substr(line_data_.find_first_not_of('\n'));
+        char newline = '\n';
+        std::string csv_line;
+        std::stringstream s;
+        s << line_data_;
+        std::getline(s,csv_line,newline);
+        if (csv_line != first)
+        {
+            return true;
+        }
+        else
+        {
+            std::clog << "Error: the parsed line (" << csv_line << ") should be a subset of the original line (" << line_data_ << ") (ensure you pass a line with a \\n)\n";
+        }
+        return true;
+    }
+    bool operator()() const
+    {
+        char newline = '\n';
+        std::string csv_line;
+        std::stringstream s;
+        s << line_data_;
+        for (unsigned i=0;i<iterations_;++i)
+        {
+            std::getline(s,csv_line,newline);
+        }
+        return true;
+    }
+};
+
+
+class test2 : public benchmark::test_case
+{
+public:
+    std::string line_data_;
+    test2(mapnik::parameters const& params)
+     : test_case(params),
+       line_data_("this is one line\nand this is a second line\nand a third line")
+       {
+          boost::optional<std::string> line_data = params.get<std::string>("line");
+          if (line_data)
+          {
+              line_data_ = *line_data;
+          }
+       }
+
+    bool validate() const
+    {
+        std::string first = line_data_.substr(line_data_.find_first_not_of('\n'));
+        char newline = '\n';
+        char quote = '"';
+        std::string csv_line;
+        std::stringstream s;
+        s << line_data_;
+        csv_utils::getline_csv(s,csv_line,newline,quote);
+        if (csv_line != first)
+        {
+            return true;
+        }
+        else
+        {
+            std::clog << "Error: the parsed line (" << csv_line << ") should be a subset of the original line (" << line_data_ << ") (ensure you pass a line with a \\n)\n";
+        }
+        return true;
+    }
+    bool operator()() const
+    {
+        char newline = '\n';
+        char quote = '"';
+        std::string csv_line;
+        std::stringstream s;
+        s << line_data_;
+        for (unsigned i=0;i<iterations_;++i)
+        {
+            csv_utils::getline_csv(s,csv_line,newline,quote);
+        }
+        return true;
+    }
+};
+
+int main(int argc, char** argv)
+{
+    int return_value = 0;
+    try
+    {
+        mapnik::parameters params;
+        benchmark::handle_args(argc,argv,params);
+        {
+            test test_runner(params);
+            return_value = return_value | run(test_runner,"std::getline");
+        }
+        {
+            test2 test_runner2(params);
+            return_value = return_value | run(test_runner2,"csv_utils::getline_csv");
+        }
+    }
+    catch (std::exception const& ex)
+    {
+        std::clog << ex.what() << "\n";
+        return -1;
+    }
+    return return_value;
+}
diff --git a/benchmark/test_rendering.cpp b/benchmark/test_rendering.cpp
index 593307b..beea289 100644
--- a/benchmark/test_rendering.cpp
+++ b/benchmark/test_rendering.cpp
@@ -28,7 +28,7 @@ public:
         boost::optional<std::string> map = params.get<std::string>("map");
         if (!map)
         {
-            throw std::runtime_error("please provide a --map=<path to xml> arg");
+            throw std::runtime_error("please provide a --map <path to xml> arg");
         }
         xml_ = *map;
 
diff --git a/bootstrap.sh b/bootstrap.sh
index 5d38b16..b148139 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -48,29 +48,29 @@ function install() {
     fi
 }
 
+ICU_VERSION="55.1"
+
 function install_mason_deps() {
     install gdal 1.11.2 libgdal &
-    install boost 1.57.0 &
-    install boost_libsystem 1.57.0 &
-    install boost_libthread 1.57.0 &
-    install boost_libfilesystem 1.57.0 &
-    install boost_libprogram_options 1.57.0 &
-    install boost_libregex 1.57.0 &
-    install boost_libpython 1.57.0 &
-    install freetype 2.5.5 libfreetype &
+    install boost 1.59.0 &
+    install boost_liball 1.59.0 &
+    install freetype 2.6 libfreetype &
     install harfbuzz 0.9.40 libharfbuzz &
     install jpeg_turbo 1.4.0 libjpeg &
     install libpng 1.6.17 libpng &
     install webp 0.4.2 libwebp &
-    install icu 54.1 &
+    install icu ${ICU_VERSION} &
     install proj 4.8.0 libproj &
     install libtiff 4.0.4beta libtiff &
     install libpq 9.4.0 &
     install sqlite 3.8.8.1 libsqlite3 &
     install expat 2.1.0 libexpat &
     install pixman 0.32.6 libpixman-1 &
-    install cairo 1.12.18 libcairo &
+    install cairo 1.14.2 libcairo &
+    install protobuf 2.6.1 &
     wait
+    # technically protobuf is not a mapnik core dep, but installing
+    # here by default helps make mapnik-vector-tile builds easier
 }
 
 MASON_LINKED_ABS=$(pwd)/mason_packages/.link
@@ -133,7 +133,7 @@ SAMPLE_INPUT_PLUGINS = True
 # NOTE: the `mapnik-settings.env` is used by test/run (which is run by `make test`)
 function setup_runtime_settings() {
     echo "export PROJ_LIB=${MASON_LINKED_ABS}/share/proj" > mapnik-settings.env
-    echo "export ICU_DATA=${MASON_LINKED_ABS}/share/icu/54.1" >> mapnik-settings.env
+    echo "export ICU_DATA=${MASON_LINKED_ABS}/share/icu/${ICU_VERSION}" >> mapnik-settings.env
     echo "export GDAL_DATA=${MASON_LINKED_ABS}/share/gdal" >> mapnik-settings.env
     source mapnik-settings.env
 }
diff --git a/demo/viewer/mapwidget.cpp b/demo/viewer/mapwidget.cpp
index efcab89..e35d131 100644
--- a/demo/viewer/mapwidget.cpp
+++ b/demo/viewer/mapwidget.cpp
@@ -31,10 +31,10 @@
 #include <mapnik/feature_kv_iterator.hpp>
 #include <mapnik/image_util.hpp>
 #include <mapnik/util/timer.hpp>
-#include <mapnik/cairo/cairo_image_util.hpp>
 
 #ifdef HAVE_CAIRO
 // cairo
+#include <mapnik/cairo/cairo_image_util.hpp>
 #include <mapnik/cairo/cairo_renderer.hpp>
 #endif
 
diff --git a/demo/viewer/viewer.pro b/demo/viewer/viewer.pro
index aad00b4..8a97477 100644
--- a/demo/viewer/viewer.pro
+++ b/demo/viewer/viewer.pro
@@ -1,6 +1,7 @@
 ######################################################################
 # Mapnik viewer - Copyright (C) 2007 Artem Pavlenko
 ######################################################################
+QMAKE_MAC_SDK = macosx10.11
 TEMPLATE = app
 QT += core gui widgets
 QMAKE_CXX = $$system(mapnik-config --cxx)
diff --git a/include/mapnik/css_color_grammar.hpp b/include/mapnik/css_color_grammar.hpp
index 4403d34..04ff02d 100644
--- a/include/mapnik/css_color_grammar.hpp
+++ b/include/mapnik/css_color_grammar.hpp
@@ -37,6 +37,7 @@
 #pragma GCC diagnostic ignored "-Wconversion"
 #include <boost/spirit/include/qi.hpp>
 #include <boost/spirit/include/qi_action.hpp>
+#include <boost/spirit/include/qi_lexeme.hpp>
 #include <boost/spirit/include/phoenix_core.hpp>
 #include <boost/spirit/include/phoenix_operator.hpp>
 #include <boost/spirit/include/phoenix_fusion.hpp>
diff --git a/include/mapnik/css_color_grammar_impl.hpp b/include/mapnik/css_color_grammar_impl.hpp
index 4ce3134..c978194 100644
--- a/include/mapnik/css_color_grammar_impl.hpp
+++ b/include/mapnik/css_color_grammar_impl.hpp
@@ -39,6 +39,7 @@ css_color_grammar<Iterator>::css_color_grammar()
     qi::_a_type _a;
     qi::_b_type _b;
     qi::_c_type _c;
+    qi::lexeme_type lexeme;
     ascii::no_case_type no_case;
     using phoenix::at_c;
 
@@ -49,18 +50,18 @@ css_color_grammar<Iterator>::css_color_grammar()
         | hex_color_small
         | no_case[named];
 
-    hex_color = lit('#')
+    hex_color = lexeme[ lit('#')
         >> hex2 [ at_c<0>(_val) = _1 ]
         >> hex2 [ at_c<1>(_val) = _1 ]
         >> hex2 [ at_c<2>(_val) = _1 ]
-        >>-hex2 [ at_c<3>(_val) = _1 ]
+        >>-hex2 [ at_c<3>(_val) = _1 ] ]
         ;
 
-    hex_color_small = lit('#')
+    hex_color_small = lexeme[ lit('#')
         >> hex1 [ at_c<0>(_val) = _1 | _1 << 4 ]
         >> hex1 [ at_c<1>(_val) = _1 | _1 << 4 ]
         >> hex1 [ at_c<2>(_val) = _1 | _1 << 4 ]
-        >>-hex1 [ at_c<3>(_val) = _1 | _1 << 4 ]
+        >>-hex1 [ at_c<3>(_val) = _1 | _1 << 4 ] ]
         ;
 
     rgba_color = lit("rgb") >> -lit('a')
diff --git a/include/mapnik/csv/csv_grammar.hpp b/include/mapnik/csv/csv_grammar.hpp
index 2bd0f52..f665dae 100644
--- a/include/mapnik/csv/csv_grammar.hpp
+++ b/include/mapnik/csv/csv_grammar.hpp
@@ -36,15 +36,30 @@ using csv_line = std::vector<csv_value>;
 using csv_data = std::vector<csv_line>;
 
 template <typename Iterator>
-struct csv_line_grammar : qi::grammar<Iterator, csv_line(std::string const&), qi::blank_type>
+struct csv_white_space_skipper : qi::grammar<Iterator>
 {
-    csv_line_grammar() : csv_line_grammar::base_type(line)
+    csv_white_space_skipper()
+        : csv_white_space_skipper::base_type(skip)
+    {
+        using namespace qi;
+        qi::lit_type lit;
+        skip = +lit(' ')
+            ;
+    }
+    qi::rule<Iterator> skip;
+};
+
+template <typename Iterator, typename Skipper = csv_white_space_skipper<Iterator> >
+struct csv_line_grammar : qi::grammar<Iterator, csv_line(char, char), Skipper>
+{
+    csv_line_grammar()
+        : csv_line_grammar::base_type(line)
     {
         using namespace qi;
         qi::_a_type _a;
         qi::_r1_type _r1;
+        qi::_r2_type _r2;
         qi::lit_type lit;
-        //qi::eol_type eol;
         qi::_1_type _1;
         qi::char_type char_;
         qi::omit_type omit;
@@ -61,43 +76,24 @@ struct csv_line_grammar : qi::grammar<Iterator, csv_line(std::string const&), qi
             ("\\\"", '\"')
             ("\"\"", '\"') // double quote
             ;
-
-        line = column(_r1)  % char_(_r1)
+        line = -omit[char_("\n\r")] >> column(_r1, _r2) % lit(_r1)
             ;
-        column = -omit[char_("\n\r")] >> quoted | *(char_ - (lit(_r1) /*| eol*/))
+        column = quoted(_r2) | *(char_ - (lit(_r1)))
             ;
-        quoted = omit[char_("\"'")[_a = _1]] > text(_a) > -lit(_a)
+        quoted = omit[char_(_r1)[_a = _1]] > text(_a) > -lit(_a) // support unmatched quotes or not (??)
             ;
-        text = *(unesc_char | (char_ - char_(_r1)))
+        text = *(unesc_char | (char_ - lit(_r1)))
             ;
         BOOST_SPIRIT_DEBUG_NODES((line)(column)(quoted));
     }
-  private:
-    qi::rule<Iterator, csv_line(std::string const&), qi::blank_type> line;
-    qi::rule<Iterator, csv_value(std::string const&)> column; // no-skip
-    qi::rule<Iterator, csv_value(char)> text;
-    qi::rule<Iterator, qi::locals<char>, csv_value()> quoted;
+private:
+    qi::rule<Iterator, csv_line(char, char),  Skipper> line;
+    qi::rule<Iterator, csv_value(char, char)> column; // no-skip
+    qi::rule<Iterator, csv_value(char)> text; // no-skip
+    qi::rule<Iterator, qi::locals<char>, csv_value(char)> quoted; //no-skip
     qi::symbols<char const, char const> unesc_char;
 };
 
-template <typename Iterator>
-struct csv_file_grammar : qi::grammar<Iterator, csv_data(std::string const&), qi::blank_type>
-{
-    csv_file_grammar() : csv_file_grammar::base_type(start)
-    {
-        using namespace qi;
-        qi::eol_type eol;
-        qi::_r1_type _r1;
-        start  = -line(_r1) % eol
-            ;
-        BOOST_SPIRIT_DEBUG_NODES((start));
-    }
-  private:
-    qi::rule<Iterator, csv_data(std::string const&), qi::blank_type> start;
-    csv_line_grammar<Iterator> line;
-};
-
-
 }
 
 #endif // MAPNIK_CVS_GRAMMAR_HPP
diff --git a/include/mapnik/expression_grammar.hpp b/include/mapnik/expression_grammar.hpp
index 7a71976..5bb132f 100644
--- a/include/mapnik/expression_grammar.hpp
+++ b/include/mapnik/expression_grammar.hpp
@@ -36,6 +36,7 @@
 #pragma GCC diagnostic ignored "-Wunused-local-typedef"
 #pragma GCC diagnostic ignored "-Wshadow"
 #pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wsign-compare"
 #pragma GCC diagnostic ignored "-Wconversion"
 #include <boost/spirit/include/qi.hpp>
 #include <boost/spirit/include/support_locals.hpp>
diff --git a/include/mapnik/expression_grammar_impl.hpp b/include/mapnik/expression_grammar_impl.hpp
index d123b3b..93b6d86 100644
--- a/include/mapnik/expression_grammar_impl.hpp
+++ b/include/mapnik/expression_grammar_impl.hpp
@@ -31,9 +31,17 @@
 #include <mapnik/function_call.hpp>
 
 // boost
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wunused-local-typedef"
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wconversion"
 #include <boost/spirit/include/qi.hpp>
 #include <boost/spirit/include/phoenix_operator.hpp>
 #include <boost/spirit/include/phoenix_object.hpp>
+#pragma GCC diagnostic pop
 
 // fwd declare
 namespace mapnik {
diff --git a/include/mapnik/feature.hpp b/include/mapnik/feature.hpp
index eb084bc..fff06a2 100644
--- a/include/mapnik/feature.hpp
+++ b/include/mapnik/feature.hpp
@@ -273,9 +273,6 @@ inline std::ostream& operator<< (std::ostream & out,feature_impl const& f)
     return out;
 }
 
-// TODO - remove at Mapnik 3.x
-using Feature = feature_impl;
-
 using feature_ptr = std::shared_ptr<feature_impl>;
 
 }
diff --git a/include/mapnik/feature_style_processor.hpp b/include/mapnik/feature_style_processor.hpp
index 062c9d6..c0ba4f3 100644
--- a/include/mapnik/feature_style_processor.hpp
+++ b/include/mapnik/feature_style_processor.hpp
@@ -110,7 +110,7 @@ private:
     /*!
      * \brief render features list queued when they are available.
      */
-    void render_material(layer_rendering_material & mat, Processor & p );
+    void render_material(layer_rendering_material const & mat, Processor & p );
 
     Map const& m_;
 };
diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp
index 0c77821..89e0c3a 100644
--- a/include/mapnik/feature_style_processor_impl.hpp
+++ b/include/mapnik/feature_style_processor_impl.hpp
@@ -69,10 +69,9 @@ struct layer_rendering_material
         lay_(lay),
         proj0_(dest),
         proj1_(lay.srs(),true) {}
-};
-
-using layer_rendering_material_ptr = std::shared_ptr<layer_rendering_material>;
 
+    layer_rendering_material(layer_rendering_material && rhs) = default;
+};
 
 template <typename Processor>
 feature_style_processor<Processor>::feature_style_processor(Map const& m, double scale_factor)
@@ -102,7 +101,7 @@ void feature_style_processor<Processor>::apply(double scale_denom)
     // in a second time, we fetch the results and
     // do the actual rendering
 
-    std::vector<layer_rendering_material_ptr> mat_list;
+    std::vector<layer_rendering_material> mat_list;
 
     // Define processing context map used by datasources
     // implementing asynchronous queries
@@ -113,9 +112,9 @@ void feature_style_processor<Processor>::apply(double scale_denom)
         if (lyr.visible(scale_denom))
         {
             std::set<std::string> names;
-            layer_rendering_material_ptr mat = std::make_shared<layer_rendering_material>(lyr, proj);
+            layer_rendering_material mat(lyr, proj);
 
-            prepare_layer(*mat,
+            prepare_layer(mat,
                           ctx_map,
                           p,
                           m_.scale(),
@@ -127,18 +126,18 @@ void feature_style_processor<Processor>::apply(double scale_denom)
                           names);
 
             // Store active material
-            if (!mat->active_styles_.empty())
+            if (!mat.active_styles_.empty())
             {
-                mat_list.push_back(mat);
+                mat_list.emplace_back(std::move(mat));
             }
         }
     }
 
-    for ( layer_rendering_material_ptr mat : mat_list )
+    for ( layer_rendering_material const & mat : mat_list )
     {
-        if (!mat->active_styles_.empty())
+        if (!mat.active_styles_.empty())
         {
-            render_material(*mat,p);
+            render_material(mat, p);
         }
     }
 
@@ -443,11 +442,11 @@ void feature_style_processor<Processor>::prepare_layer(layer_rendering_material
 
 
 template <typename Processor>
-void feature_style_processor<Processor>::render_material(layer_rendering_material & mat,
+void feature_style_processor<Processor>::render_material(layer_rendering_material const & mat,
                                                          Processor & p )
 {
-    std::vector<feature_type_style const*> & active_styles = mat.active_styles_;
-    std::vector<featureset_ptr> & featureset_ptr_list = mat.featureset_ptr_list_;
+    std::vector<feature_type_style const*> const & active_styles = mat.active_styles_;
+    std::vector<featureset_ptr> const & featureset_ptr_list = mat.featureset_ptr_list_;
     if (featureset_ptr_list.empty())
     {
         // The datasource wasn't queried because of early return
@@ -464,7 +463,7 @@ void feature_style_processor<Processor>::render_material(layer_rendering_materia
 
     layer const& lay = mat.lay_;
 
-    std::vector<rule_cache> & rule_caches = mat.rule_caches_;
+    std::vector<rule_cache> const & rule_caches = mat.rule_caches_;
 
     proj_transform prj_trans(mat.proj0_,mat.proj1_);
 
@@ -544,7 +543,7 @@ void feature_style_processor<Processor>::render_material(layer_rendering_materia
     else
     {
         std::size_t i = 0;
-        std::vector<featureset_ptr>::iterator featuresets = featureset_ptr_list.begin();
+        std::vector<featureset_ptr>::const_iterator featuresets = featureset_ptr_list.cbegin();
         for (feature_type_style const* style : active_styles)
         {
             featureset_ptr features = *featuresets++;
diff --git a/include/mapnik/geometry_is_simple.hpp b/include/mapnik/geometry_is_simple.hpp
index 30fff6d..868b41a 100644
--- a/include/mapnik/geometry_is_simple.hpp
+++ b/include/mapnik/geometry_is_simple.hpp
@@ -48,7 +48,9 @@ struct geometry_is_simple
 
     result_type operator() (geometry_empty const& ) const
     {
-        return false;
+        // An empty geometry has no anomalous geometric points, such as self intersection or self tangency.
+        // Therefore, we will return true
+        return true;
     }
 
     template <typename T>
@@ -69,6 +71,13 @@ struct geometry_is_simple
     template <typename T>
     result_type operator() (line_string<T> const& line) const
     {
+        if (line.empty())
+        {
+            // Prevent an empty line_string from segfaulting in boost geometry 1.58
+            // once it is fixed this can be removed
+            // https://svn.boost.org/trac/boost/ticket/11709
+            return true;
+        }
         return boost::geometry::is_simple(line);
     }
     template <typename T>
@@ -79,17 +88,43 @@ struct geometry_is_simple
     template <typename T>
     result_type operator() (multi_point<T> const& multi_pt) const
     {
+        if (multi_pt.empty())
+        {
+            // This return is due to bug in boost geometry once it is fixed it can be removed
+            // https://svn.boost.org/trac/boost/ticket/11710
+            return true;
+        }
         return boost::geometry::is_simple(multi_pt);
     }
     template <typename T>
     result_type operator() (multi_line_string<T> const& multi_line) const
     {
-        return boost::geometry::is_simple(multi_line);
+        if (multi_line.empty())
+        {
+            // This return is due to bug in boost geometry once it is fixed it can be removed
+            // https://svn.boost.org/trac/boost/ticket/11710
+            return true;
+        }
+        for (auto const& line : multi_line)
+        {
+            if (!(*this)(line)) return false;
+        }
+        return true;
     }
     template <typename T>
     result_type operator() (multi_polygon<T> const& multi_poly) const
     {
-        return boost::geometry::is_simple(multi_poly);
+        if (multi_poly.empty())
+        {
+            // This return is due to bug in boost geometry once it is fixed it can be removed
+            // https://svn.boost.org/trac/boost/ticket/11710
+            return true;
+        }
+        for (auto const& poly : multi_poly)
+        {
+            if (!(*this)(poly)) return false;
+        }
+        return true;
     }
 
 };
@@ -102,6 +137,12 @@ inline bool is_simple(T const& geom)
     return detail::geometry_is_simple() (geom);
 }
 
+template <typename T>
+inline bool is_simple(mapnik::geometry::geometry<T> const& geom)
+{
+    return util::apply_visitor(detail::geometry_is_simple(), geom);
+}
+
 }}
 
 #endif // BOOST_VERSION >= 1.56
diff --git a/include/mapnik/geometry_is_valid.hpp b/include/mapnik/geometry_is_valid.hpp
index aa7f139..95319cc 100644
--- a/include/mapnik/geometry_is_valid.hpp
+++ b/include/mapnik/geometry_is_valid.hpp
@@ -25,12 +25,13 @@
 
 #include <boost/version.hpp>
 
-// only Boost >= 1.56 contains the is_valid function
-#if BOOST_VERSION >= 105600
+// only Boost >= 1.58 contains the is_valid function
+#if BOOST_VERSION >= 105800
 
 #include <mapnik/geometry.hpp>
 #include <mapnik/geometry_adapters.hpp>
 #include <boost/geometry/algorithms/is_valid.hpp>
+#include <boost/geometry/algorithms/validity_failure_type.hpp>
 
 namespace mapnik { namespace geometry {
 
@@ -48,7 +49,7 @@ struct geometry_is_valid
 
     result_type operator() (geometry_empty const& ) const
     {
-        return false;
+        return true;
     }
 
     template <typename T>
@@ -98,6 +99,143 @@ struct geometry_is_valid
     }
 };
 
+struct geometry_is_valid_reason
+{
+    using result_type = bool;
+    
+    boost::geometry::validity_failure_type & failure_;
+
+    geometry_is_valid_reason(boost::geometry::validity_failure_type & failure):
+        failure_(failure) {}
+
+    template <typename T>
+    result_type operator() (geometry<T> const& geom) const
+    {
+        return mapnik::util::apply_visitor(*this, geom);
+    }
+
+    result_type operator() (geometry_empty const& ) const
+    {
+        failure_ = boost::geometry::no_failure;
+        return true;
+    }
+
+    template <typename T>
+    result_type operator() (geometry_collection<T> const& collection) const
+    {
+        for (auto const& geom : collection)
+        {
+            if ( !(*this)(geom)) return false;
+        }
+        return true;
+    }
+
+    template <typename T>
+    result_type operator() (point<T> const& pt) const
+    {
+        return boost::geometry::is_valid(pt, failure_);
+    }
+
+    template <typename T>
+    result_type operator() (line_string<T> const& line) const
+    {
+        return boost::geometry::is_valid(line, failure_);
+    }
+
+    template <typename T>
+    result_type operator() (polygon<T> const& poly) const
+    {
+        return boost::geometry::is_valid(poly, failure_);
+    }
+
+    template <typename T>
+    result_type operator() (multi_point<T> const& multi_pt) const
+    {
+        return boost::geometry::is_valid(multi_pt, failure_);
+    }
+
+    template <typename T>
+    result_type operator() (multi_line_string<T> const& multi_line) const
+    {
+        return boost::geometry::is_valid(multi_line, failure_);
+    }
+
+    template <typename T>
+    result_type operator() (multi_polygon<T> const& multi_poly) const
+    {
+        return boost::geometry::is_valid(multi_poly, failure_);
+    }
+};
+
+struct geometry_is_valid_string
+{
+    using result_type = bool;
+    
+    std::string & message_;
+
+    geometry_is_valid_string(std::string & message):
+        message_(message) {}
+
+    template <typename T>
+    result_type operator() (geometry<T> const& geom) const
+    {
+        return mapnik::util::apply_visitor(*this, geom);
+    }
+
+    result_type operator() (geometry_empty const& ) const
+    {
+        message_ = "Geometry is valid";
+        return true;
+    }
+
+    template <typename T>
+    result_type operator() (geometry_collection<T> const& collection) const
+    {
+        for (auto const& geom : collection)
+        {
+            if ( !(*this)(geom)) return false;
+        }
+        return true;
+    }
+
+    template <typename T>
+    result_type operator() (point<T> const& pt) const
+    {
+        return boost::geometry::is_valid(pt, message_);
+    }
+
+    template <typename T>
+    result_type operator() (line_string<T> const& line) const
+    {
+        return boost::geometry::is_valid(line, message_);
+    }
+
+    template <typename T>
+    result_type operator() (polygon<T> const& poly) const
+    {
+        return boost::geometry::is_valid(poly, message_);
+    }
+
+    template <typename T>
+    result_type operator() (multi_point<T> const& multi_pt) const
+    {
+        return boost::geometry::is_valid(multi_pt, message_);
+    }
+
+    template <typename T>
+    result_type operator() (multi_line_string<T> const& multi_line) const
+    {
+        return boost::geometry::is_valid(multi_line, message_);
+    }
+
+    template <typename T>
+    result_type operator() (multi_polygon<T> const& multi_poly) const
+    {
+        return boost::geometry::is_valid(multi_poly, message_);
+    }
+};
+
+
 }
 
 template <typename T>
@@ -106,7 +244,39 @@ inline bool is_valid(T const& geom)
     return detail::geometry_is_valid() (geom);
 }
 
+template <typename T>
+inline bool is_valid(mapnik::geometry::geometry<T> const& geom)
+{
+    return util::apply_visitor(detail::geometry_is_valid(), geom);
+}
+
+template <typename T>
+inline bool is_valid(T const& geom, boost::geometry::validity_failure_type & failure)
+{
+    return detail::geometry_is_valid_reason(failure) (geom);
+}
+
+template <typename T>
+inline bool is_valid(mapnik::geometry::geometry<T> const& geom, 
+                     boost::geometry::validity_failure_type & failure)
+{
+    return util::apply_visitor(detail::geometry_is_valid_reason(failure), geom);
+}
+
+template <typename T>
+inline bool is_valid(T const& geom, std::string & message)
+{
+    return detail::geometry_is_valid_string(message) (geom);
+}
+
+template <typename T>
+inline bool is_valid(mapnik::geometry::geometry<T> const& geom, 
+                     std::string & message)
+{
+    return util::apply_visitor(detail::geometry_is_valid_string(message), geom);
+}
+
 }}
 
-#endif // BOOST_VERSION >= 1.56
+#endif // BOOST_VERSION >= 1.58
 #endif // MAPNIK_GEOMETRY_IS_VALID_HPP
diff --git a/include/mapnik/image_any.hpp b/include/mapnik/image_any.hpp
index c14d5dd..735ad82 100644
--- a/include/mapnik/image_any.hpp
+++ b/include/mapnik/image_any.hpp
@@ -55,8 +55,8 @@ struct MAPNIK_DECL image_any : image_base
               bool painted = false);
 
     template <typename T>
-        image_any(T && data) noexcept
-        : image_base(std::move(data)) {}
+        image_any(T && _data) noexcept
+        : image_base(std::move(_data)) {}
 
     unsigned char const* bytes() const;
     unsigned char* bytes();
diff --git a/include/mapnik/jpeg_io.hpp b/include/mapnik/jpeg_io.hpp
index dcfd446..cc64dac 100644
--- a/include/mapnik/jpeg_io.hpp
+++ b/include/mapnik/jpeg_io.hpp
@@ -27,10 +27,10 @@
 
 #include <new>
 #include <ostream>
+#include <cstdio>
 
 extern "C"
 {
-#include <stdio.h>
 #include <jpeglib.h>
 }
 
@@ -58,10 +58,10 @@ inline boolean empty_output_buffer (j_compress_ptr cinfo)
 {
     dest_mgr * dest = reinterpret_cast<dest_mgr*>(cinfo->dest);
     dest->out->write((char*)dest->buffer, BUFFER_SIZE);
-    if (!*(dest->out)) return false;
+    if (!*(dest->out)) return boolean(0);
     dest->pub.next_output_byte = dest->buffer;
     dest->pub.free_in_buffer = BUFFER_SIZE;
-    return true;
+    return boolean(1);
 }
 
 inline void term_destination( j_compress_ptr cinfo)
@@ -105,8 +105,8 @@ void save_as_jpeg(T1 & file,int quality, T2 const& image)
     cinfo.input_components = 3;
     cinfo.in_color_space = JCS_RGB;
     jpeg_set_defaults(&cinfo);
-    jpeg_set_quality(&cinfo, quality,1);
-    jpeg_start_compress(&cinfo, 1);
+    jpeg_set_quality(&cinfo, quality, boolean(1));
+    jpeg_start_compress(&cinfo, boolean(1));
     JSAMPROW row_pointer[1];
     JSAMPLE* row=reinterpret_cast<JSAMPLE*>( ::operator new (sizeof(JSAMPLE) * width*3));
     while (cinfo.next_scanline < cinfo.image_height)
diff --git a/include/mapnik/json/error_handler.hpp b/include/mapnik/json/error_handler.hpp
index 2f38f62..90d64af 100644
--- a/include/mapnik/json/error_handler.hpp
+++ b/include/mapnik/json/error_handler.hpp
@@ -44,7 +44,7 @@ struct error_handler
         auto start = err_pos;
         std::advance(err_pos,16);
         auto end = err_pos;
-        s << what << " expected but got: " << std::string(start, end);
+        s << "Mapnik geojson parsing error:" << what << " expected but got: " << std::string(start, end);
         throw std::runtime_error(s.str());
     }
 };
diff --git a/include/mapnik/json/extract_bounding_box_grammar.hpp b/include/mapnik/json/extract_bounding_box_grammar.hpp
index 2183fe5..bfe9185 100644
--- a/include/mapnik/json/extract_bounding_box_grammar.hpp
+++ b/include/mapnik/json/extract_bounding_box_grammar.hpp
@@ -45,8 +45,8 @@
 
 namespace mapnik { namespace json {
 
-using position = mapnik::geometry::point<double>;
-using boxes = std::vector<std::pair<box2d<double>, std::pair<std::size_t, std::size_t>>>;
+using position_type = mapnik::geometry::point<double>;
+using boxes_type = std::vector<std::pair<box2d<double>, std::pair<std::size_t, std::size_t>>>;
 
 namespace qi = boost::spirit::qi;
 
@@ -84,15 +84,15 @@ struct push_box_impl
 
 template <typename Iterator, typename ErrorHandler = error_handler<Iterator> >
 struct extract_bounding_box_grammar :
-        qi::grammar<Iterator, void(boxes&), space_type>
+        qi::grammar<Iterator, void(boxes_type&), space_type>
 {
     extract_bounding_box_grammar();
     // rules
-    qi::rule<Iterator, void(boxes&), space_type> start;
-    qi::rule<Iterator, qi::locals<Iterator>, void(boxes&), space_type> features;
-    qi::rule<Iterator, qi::locals<int, box2d<double>>, void(boxes&, Iterator const&), space_type> feature;
+    qi::rule<Iterator, void(boxes_type&), space_type> start;
+    qi::rule<Iterator, qi::locals<Iterator>, void(boxes_type&), space_type> features;
+    qi::rule<Iterator, qi::locals<int, box2d<double>>, void(boxes_type&, Iterator const&), space_type> feature;
     qi::rule<Iterator, qi::locals<box2d<double>>, box2d<double>(), space_type> coords;
-    qi::rule<Iterator, boost::optional<position>(), space_type> pos;
+    qi::rule<Iterator, boost::optional<position_type>(), space_type> pos;
     qi::rule<Iterator, void(box2d<double>&), space_type> ring;
     qi::rule<Iterator, void(box2d<double>&), space_type> rings;
     qi::rule<Iterator, void(box2d<double>&), space_type> rings_array;
diff --git a/include/mapnik/quad_tree.hpp b/include/mapnik/quad_tree.hpp
index d46faa2..092bf4d 100644
--- a/include/mapnik/quad_tree.hpp
+++ b/include/mapnik/quad_tree.hpp
@@ -30,20 +30,23 @@
 // stl
 #include <algorithm>
 #include <vector>
+#include <type_traits>
+
+#include <cstring>
 
 namespace mapnik
 {
 template <typename T>
 class quad_tree : util::noncopyable
 {
+    using value_type = T;
     struct node
     {
-        using value_t = T;
-        using cont_t = std::vector<T>;
-        using iterator = typename cont_t::iterator;
-        using const_iterator = typename cont_t::const_iterator;
+        using cont_type = std::vector<T>;
+        using iterator = typename cont_type::iterator;
+        using const_iterator = typename cont_type::const_iterator;
         box2d<double> extent_;
-        cont_t cont_;
+        cont_type cont_;
         node * children_[4];
 
         explicit node(box2d<double> const& ext)
@@ -76,18 +79,28 @@ class quad_tree : util::noncopyable
         {
             return cont_.end();
         }
+
+        int num_subnodes() const
+        {
+            int count = 0;
+            for (int i = 0; i < 4; ++i)
+            {
+                if (children_[i]) ++count;
+            }
+            return count;
+        }
         ~node () {}
     };
 
-    using nodes_t = std::vector<std::unique_ptr<node> >;
-    using cont_t = typename node::cont_t;
-    using node_data_iterator = typename cont_t::iterator;
+    using nodes_type = std::vector<std::unique_ptr<node> >;
+    using cont_type = typename node::cont_type;
+    using node_data_iterator = typename cont_type::iterator;
 
 public:
-    using iterator = typename nodes_t::iterator;
-    using const_iterator = typename nodes_t::const_iterator;
-    using result_t = typename std::vector<std::reference_wrapper<T> >;
-    using query_iterator = typename result_t::iterator;
+    using iterator = typename nodes_type::iterator;
+    using const_iterator = typename nodes_type::const_iterator;
+    using result_type = typename std::vector<std::reference_wrapper<T> >;
+    using query_iterator = typename result_type::iterator;
 
     explicit quad_tree(box2d<double> const& ext,
                        unsigned int max_depth = 8,
@@ -143,9 +156,41 @@ public:
         return root_->extent_;
     }
 
+    int count() const
+    {
+        return count_nodes(root_);
+    }
+
+    int count_items() const
+    {
+        int count = 0;
+        count_items(root_, count);
+        return count;
+    }
+    void trim()
+    {
+        trim_tree(root_);
+    }
+
+    template <typename OutputStream>
+    void write(OutputStream & out)
+    {
+        static_assert(std::is_standard_layout<value_type>::value,
+                      "Values stored in quad-tree must be standard layout types to allow serialisation");
+        char header[16];
+        std::memset(header,0,16);
+        header[0]='m';
+        header[1]='a';
+        header[2]='p';
+        header[3]='n';
+        header[4]='i';
+        header[5]='k';
+        out.write(header,16);
+        write_node(out,root_);
+    }
 private:
 
-    void query_node(box2d<double> const& box, result_t & result, node * node_) const
+    void query_node(box2d<double> const& box, result_type & result, node * node_) const
     {
         if (node_)
         {
@@ -208,10 +253,107 @@ private:
         ext[3]=box2d<double>(hix - width * ratio_,hiy - height*ratio_,hix,hiy);
     }
 
+    void trim_tree(node *& n)
+    {
+        if (n)
+        {
+            for (int i = 0; i < 4; ++i)
+            {
+                trim_tree(n->children_[i]);
+            }
+            if (n->num_subnodes() == 1 && n->cont_.size() == 0)
+            {
+                for (int i = 0; i < 4; ++i)
+                {
+                    if (n->children_[i])
+                    {
+                        n = n->children_[i];
+                        break;
+                    }
+                }
+            }
+        }
+
+    }
+
+    int count_nodes(node const* n) const
+    {
+        if (!n) return 0;
+        else
+        {
+            int count = 1;
+            for (int i = 0; i < 4; ++i)
+            {
+                count += count_nodes(n->children_[i]);
+            }
+            return count;
+        }
+    }
+
+    void count_items(node const* n,int& count) const
+    {
+        if (n)
+        {
+            count += n->cont_.size();
+            for (int i = 0; i < 4; ++i)
+            {
+                count_items(n->children_[i],count);
+            }
+        }
+    }
+
+    int subnode_offset(node const* n) const
+    {
+        int offset = 0;
+        for (int i = 0; i < 4; i++)
+        {
+            if (n->children_[i])
+            {
+                offset +=sizeof(box2d<double>) + (n->children_[i]->cont_.size() * sizeof(value_type)) + 3 * sizeof(int);
+                offset +=subnode_offset(n->children_[i]);
+            }
+        }
+        return offset;
+    }
+
+    template <typename OutputStream>
+    void write_node(OutputStream & out, node const* n) const
+    {
+        if (n)
+        {
+            int offset=subnode_offset(n);
+            int shape_count=n->cont_.size();
+            int recsize=sizeof(box2d<double>) + 3 * sizeof(int) + shape_count * sizeof(value_type);
+            std::unique_ptr<char[]> node_record(new char[recsize]);
+            std::memset(node_record.get(), 0, recsize);
+            std::memcpy(node_record.get(), &offset, 4);
+            std::memcpy(node_record.get() + 4, &n->extent_, sizeof(box2d<double>));
+            std::memcpy(node_record.get() + 36, &shape_count, 4);
+            for (int i=0; i < shape_count; ++i)
+            {
+                memcpy(node_record.get() + 40 + i * sizeof(value_type), &(n->cont_[i]),sizeof(value_type));
+            }
+            int num_subnodes=0;
+            for (int i = 0; i < 4; ++i)
+            {
+                if (n->children_[i])
+                {
+                    ++num_subnodes;
+                }
+            }
+            std::memcpy(node_record.get() + 40 + shape_count * sizeof(value_type),&num_subnodes,4);
+            out.write(node_record.get(),recsize);
+            for (int i = 0; i < 4; ++i)
+            {
+                write_node(out, n->children_[i]);
+            }
+        }
+    }
+
     const unsigned int max_depth_;
     const double ratio_;
-    result_t query_result_;
-    nodes_t nodes_;
+    result_type query_result_;
+    nodes_type nodes_;
     node * root_;
 
 };
diff --git a/include/mapnik/renderer_common/process_group_symbolizer.hpp b/include/mapnik/renderer_common/process_group_symbolizer.hpp
index d593b8e..edec308 100644
--- a/include/mapnik/renderer_common/process_group_symbolizer.hpp
+++ b/include/mapnik/renderer_common/process_group_symbolizer.hpp
@@ -329,7 +329,7 @@ void render_group_symbolizer(group_symbolizer const& sym,
         // get the layout for this set of properties
         for (auto const& rule : props->get_rules())
         {
-             if (util::apply_visitor(evaluate<Feature,value_type,attributes>(*sub_feature,common.vars_),
+             if (util::apply_visitor(evaluate<feature_impl,value_type,attributes>(*sub_feature,common.vars_),
                                                *(rule->get_filter())).to_bool())
              {
                 // add matched rule and feature to the list of things to draw
@@ -380,7 +380,7 @@ void render_group_symbolizer(group_symbolizer const& sym,
         // evaluate the repeat key with the matched sub feature if we have one
         if (rpt_key_expr)
         {
-            rpt_key_value = util::apply_visitor(evaluate<Feature,value_type,attributes>(*match_feature,common.vars_),
+            rpt_key_value = util::apply_visitor(evaluate<feature_impl,value_type,attributes>(*match_feature,common.vars_),
                                                 *rpt_key_expr).to_unicode();
         }
         helper.add_box_element(layout_manager.offset_box_at(i), rpt_key_value);
diff --git a/include/mapnik/simplify_converter.hpp b/include/mapnik/simplify_converter.hpp
index e217eba..f107a8d 100644
--- a/include/mapnik/simplify_converter.hpp
+++ b/include/mapnik/simplify_converter.hpp
@@ -268,11 +268,11 @@ private:
     }
 
     template <typename Iterator>
-    bool fit_sleeve(Iterator itr,Iterator end, vertex2d const& v)
+    bool fit_sleeve(Iterator itr, Iterator itr_end, vertex2d const& v)
     {
         sleeve s(*itr,v,tolerance_);
         ++itr; // skip first vertex
-        for (; itr!=end; ++itr)
+        for (; itr!=itr_end; ++itr)
         {
             if (!s.inside(*itr))
             {
diff --git a/include/mapnik/text/placements/base.hpp b/include/mapnik/text/placements/base.hpp
index f83a547..0b93fb8 100644
--- a/include/mapnik/text/placements/base.hpp
+++ b/include/mapnik/text/placements/base.hpp
@@ -45,7 +45,7 @@ class MAPNIK_DECL text_placement_info : util::noncopyable
 public:
     // Constructor. Takes the parent text_placements object as a parameter
     // to read defaults from it.
-    text_placement_info(text_placements const* parent, double scale_factor_);
+    text_placement_info(text_placements const* parent, double _scale_factor);
     // Get next placement.
     // This function is also called before the first placement is tried.
     // Each class has to return at least one position!
@@ -87,7 +87,7 @@ public:
     //     return text_placement_info_ptr(new text_placement_info_XXX(this));
     // }
 
-    virtual text_placement_info_ptr get_placement_info(double scale_factor, feature_impl const& feature, attributes const& vars) const = 0;
+    virtual text_placement_info_ptr get_placement_info(double _scale_factor, feature_impl const& feature, attributes const& vars) const = 0;
     // Get a list of all expressions used in any placement.
     // This function is used to collect attributes.
 
diff --git a/include/mapnik/text/placements/dummy.hpp b/include/mapnik/text/placements/dummy.hpp
index 036c5bd..e83808f 100644
--- a/include/mapnik/text/placements/dummy.hpp
+++ b/include/mapnik/text/placements/dummy.hpp
@@ -37,7 +37,7 @@ struct attribute;
 class MAPNIK_DECL text_placements_dummy: public text_placements
 {
 public:
-    text_placement_info_ptr get_placement_info(double scale_factor, feature_impl const& feature, attributes const& vars) const;
+    text_placement_info_ptr get_placement_info(double _scale_factor, feature_impl const& feature, attributes const& vars) const;
     friend class text_placement_info_dummy;
 };
 
@@ -45,8 +45,8 @@ public:
 class MAPNIK_DECL text_placement_info_dummy : public text_placement_info
 {
 public:
-    text_placement_info_dummy(text_placements_dummy const* parent, double scale_factor)
-        : text_placement_info(parent, scale_factor),
+    text_placement_info_dummy(text_placements_dummy const* parent, double _scale_factor)
+        : text_placement_info(parent, _scale_factor),
         state(0) {}
     bool next() const;
 private:
diff --git a/include/mapnik/text/placements/simple.hpp b/include/mapnik/text/placements/simple.hpp
index a489bea..ea0d73c 100644
--- a/include/mapnik/text/placements/simple.hpp
+++ b/include/mapnik/text/placements/simple.hpp
@@ -40,7 +40,7 @@ public:
     text_placements_simple(symbolizer_base::value_type const& positions,
                            std::vector<directions_e> && direction,
                            std::vector<int> && text_sizes);
-    text_placement_info_ptr get_placement_info(double scale_factor, feature_impl const& feature, attributes const& vars) const;
+    text_placement_info_ptr get_placement_info(double _scale_factor, feature_impl const& feature, attributes const& vars) const;
     std::string get_positions() const;
     static text_placements_ptr from_xml(xml_node const& xml, fontset_map const& fontsets, bool is_shield);
     void init_positions(std::string const& positions) const;
@@ -58,7 +58,7 @@ class text_placement_info_simple : public text_placement_info
 public:
     text_placement_info_simple(text_placements_simple const* parent,
                                std::string const& evaluated_positions,
-                               double scale_factor);
+                               double _scale_factor);
     bool next() const;
 protected:
     bool next_position_only() const;
diff --git a/include/mapnik/util/singleton.hpp b/include/mapnik/util/singleton.hpp
index 94f8bf1..fcb0275 100644
--- a/include/mapnik/util/singleton.hpp
+++ b/include/mapnik/util/singleton.hpp
@@ -31,7 +31,10 @@
 #include <new> // operator new
 #include <type_traits>
 #include <atomic>
+
+#ifdef MAPNIK_THREADSAFE
 #include <mutex>
+#endif
 
 namespace mapnik
 {
@@ -97,7 +100,9 @@ template <typename T,
         }
 
     protected:
+#ifdef MAPNIK_THREADSAFE
         static std::mutex mutex_;
+#endif
         singleton() {}
 
     public:
@@ -106,7 +111,9 @@ template <typename T,
             T * tmp = pInstance_.load(std::memory_order_acquire);
             if (tmp == nullptr)
             {
+#ifdef MAPNIK_THREADSAFE
                 std::lock_guard<std::mutex> lock(mutex_);
+#endif
                 tmp = pInstance_.load(std::memory_order_relaxed);
                 if (tmp == nullptr)
                 {
@@ -130,8 +137,10 @@ template <typename T,
         }
     };
 
+#ifdef MAPNIK_THREADSAFE
     template <typename T,
               template <typename U> class CreatePolicy> std::mutex singleton<T,CreatePolicy>::mutex_;
+#endif
     template <typename T,
               template <typename U> class CreatePolicy> std::atomic<T*> singleton<T,CreatePolicy>::pInstance_;
     template <typename T,
diff --git a/include/mapnik/util/spatial_index.hpp b/include/mapnik/util/spatial_index.hpp
new file mode 100644
index 0000000..021bed5
--- /dev/null
+++ b/include/mapnik/util/spatial_index.hpp
@@ -0,0 +1,155 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2015 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_SPATIAL_INDEX_HPP
+#define MAPNIK_UTIL_SPATIAL_INDEX_HPP
+
+//mapnik
+#include <mapnik/coord.hpp>
+#include <mapnik/box2d.hpp>
+#include <mapnik/query.hpp>
+#include <mapnik/geom_util.hpp>
+// stl
+#include <type_traits>
+
+using mapnik::box2d;
+using mapnik::query;
+
+namespace mapnik { namespace util {
+
+template <typename Value, typename Filter, typename InputStream>
+class spatial_index
+{
+public:
+    static void query(Filter const& filter, InputStream& in,std::vector<Value>& pos);
+    static box2d<double> bounding_box( InputStream& in );
+    static void query_first_n(Filter const& filter, InputStream & in, std::vector<Value>& pos, std::size_t count);
+private:
+
+    spatial_index();
+    ~spatial_index();
+    spatial_index(spatial_index const&);
+    spatial_index& operator=(spatial_index const&);
+    static int read_ndr_integer(InputStream& in);
+    static void read_envelope(InputStream& in, box2d<double>& envelope);
+    static void query_node(Filter const& filter, InputStream& in, std::vector<Value> & results);
+    static void query_first_n_impl(Filter const& filter, InputStream& in, std::vector<Value> & results, std::size_t count);
+};
+
+template <typename Value, typename Filter, typename InputStream>
+box2d<double> spatial_index<Value, Filter, InputStream>::bounding_box(InputStream& in)
+{
+    static_assert(std::is_standard_layout<Value>::value, "Values stored in quad-tree must be standard layout type");
+    in.seekg(16 + 4, std::ios::beg);
+    box2d<double> box;
+    read_envelope(in, box);
+    in.seekg(0, std::ios::beg);
+    return box;
+}
+
+template <typename Value, typename Filter, typename InputStream>
+void spatial_index<Value, Filter, InputStream>::query(Filter const& filter, InputStream& in, std::vector<Value>& results)
+{
+    static_assert(std::is_standard_layout<Value>::value, "Values stored in quad-tree must be standard layout types");
+    in.seekg(16, std::ios::beg);
+    query_node(filter, in, results);
+}
+
+template <typename Value, typename Filter, typename InputStream>
+void spatial_index<Value, Filter, InputStream>::query_node(Filter const& filter, InputStream& in, std::vector<Value>& results)
+{
+    int offset = read_ndr_integer(in);
+    box2d<double> node_ext;
+    read_envelope(in, node_ext);
+    int num_shapes = read_ndr_integer(in);
+    if (!filter.pass(node_ext))
+    {
+        in.seekg(offset + num_shapes * sizeof(Value) + 4, std::ios::cur);
+        return;
+    }
+
+    for (int i = 0; i < num_shapes; ++i)
+    {
+        Value item;
+        in.read(reinterpret_cast<char*>(&item), sizeof(Value));
+        results.push_back(std::move(item));
+    }
+
+    int children = read_ndr_integer(in);
+    for (int j = 0; j < children; ++j)
+    {
+        query_node(filter, in, results);
+    }
+}
+
+template <typename Value, typename Filter, typename InputStream>
+void spatial_index<Value, Filter, InputStream>::query_first_n(Filter const& filter, InputStream& in, std::vector<Value>& results, std::size_t count)
+{
+    static_assert(std::is_standard_layout<Value>::value, "Values stored in quad-tree must be standard layout types");
+    in.seekg(16, std::ios::beg);
+    query_first_n_impl(filter, in, results, count);
+}
+
+template <typename Value, typename Filter, typename InputStream>
+void spatial_index<Value, Filter, InputStream>::query_first_n_impl(Filter const& filter, InputStream& in, std::vector<Value>& results, std::size_t count)
+{
+    if (results.size() == count) return;
+    int offset = read_ndr_integer(in);
+    box2d<double> node_ext;
+    read_envelope(in, node_ext);
+    int num_shapes = read_ndr_integer(in);
+    if (!filter.pass(node_ext))
+    {
+        in.seekg(offset + num_shapes * sizeof(Value) + 4, std::ios::cur);
+        return;
+    }
+
+    for (int i = 0; i < num_shapes; ++i)
+    {
+        Value item;
+        in.read(reinterpret_cast<char*>(&item), sizeof(Value));
+        if (results.size() < count) results.push_back(std::move(item));
+    }
+    int children = read_ndr_integer(in);
+    for (int j = 0; j < children; ++j)
+    {
+        query_first_n_impl(filter, in, results, count);
+    }
+}
+
+template <typename Value, typename Filter, typename InputStream>
+int spatial_index<Value, Filter, InputStream>::read_ndr_integer(InputStream& in)
+{
+    char b[4];
+    in.read(b, 4);
+    return (b[0] & 0xff) | (b[1] & 0xff) << 8 | (b[2] & 0xff) << 16 | (b[3] & 0xff) << 24;
+}
+
+template <typename Value, typename Filter, typename InputStream>
+void spatial_index<Value, Filter, InputStream>::read_envelope(InputStream& in, box2d<double>& envelope)
+{
+    in.read(reinterpret_cast<char*>(&envelope), sizeof(envelope));
+}
+
+}} // mapnik/util
+
+#endif // MAPNIK_UTIL_SPATIAL_INDEX_HPP
diff --git a/include/mapnik/value.hpp b/include/mapnik/value.hpp
index a56d76f..1041d94 100644
--- a/include/mapnik/value.hpp
+++ b/include/mapnik/value.hpp
@@ -72,42 +72,41 @@ using value_base = util::variant<value_null, value_bool, value_integer,value_dou
 namespace impl {
 
 struct equals
-
 {
     bool operator() (value_integer lhs, value_double rhs) const
     {
-        return  lhs == rhs;
+        return static_cast<value_double>(lhs) == rhs;
     }
 
     bool operator() (value_bool lhs, value_double rhs) const
     {
-        return  lhs == rhs;
+        return static_cast<value_double>(lhs) == rhs;
     }
 
     bool operator() (value_double lhs, value_integer rhs) const
     {
-        return  lhs == rhs;
+        return lhs == static_cast<value_double>(rhs);
     }
 
     bool operator() (value_bool lhs, value_integer rhs) const
     {
-        return  lhs == rhs;
+        return static_cast<value_integer>(lhs) == rhs;
     }
 
     bool operator() (value_integer lhs, value_bool rhs) const
     {
-        return  lhs == rhs;
+        return lhs == static_cast<value_integer>(rhs);
     }
 
     bool operator() (value_double lhs, value_bool rhs) const
     {
-        return  lhs == rhs;
+        return static_cast<value_double>(lhs) == rhs;
     }
 
     bool operator() (value_unicode_string const& lhs,
                      value_unicode_string const& rhs) const
     {
-        return  (lhs == rhs) ? true: false;
+        return (lhs == rhs) ? true: false;
     }
 
     template <typename T>
@@ -140,32 +139,32 @@ struct not_equals
 
     bool operator() (value_integer lhs, value_double rhs) const
     {
-        return  lhs != rhs;
+        return static_cast<value_double>(lhs) != rhs;
     }
 
     bool operator() (value_bool lhs, value_double rhs) const
     {
-        return  lhs != rhs;
+        return static_cast<value_double>(lhs) != rhs;
     }
 
     bool operator() (value_double lhs, value_integer rhs) const
     {
-        return  lhs != rhs;
+        return  lhs != static_cast<value_double>(rhs);
     }
 
     bool operator() (value_bool lhs, value_integer rhs) const
     {
-        return  lhs != rhs;
+        return static_cast<value_integer>(lhs) != rhs;
     }
 
     bool operator() (value_integer lhs, value_bool rhs) const
     {
-        return  lhs != rhs;
+        return lhs != static_cast<value_integer>(rhs);
     }
 
     bool operator() (value_double lhs, value_bool rhs) const
     {
-        return  lhs != rhs;
+        return  lhs != static_cast<value_double>(rhs);
     }
 
     bool operator() (value_unicode_string const& lhs,
@@ -993,8 +992,8 @@ inline bool value::is_null() const
 namespace std
 {
 
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmismatched-tags"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmismatched-tags"
 
 template <>
 struct hash<mapnik::value>
@@ -1005,7 +1004,7 @@ struct hash<mapnik::value>
     }
 };
 
-#pragma clang diagnostic pop
+#pragma GCC diagnostic pop
 
 }
 
diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp
index 161b708..ad784d0 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 5
+#define MAPNIK_PATCH_VERSION 6
 
 #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
 
diff --git a/include/mapnik/webp_io.hpp b/include/mapnik/webp_io.hpp
index 978e6cf..b16a891 100644
--- a/include/mapnik/webp_io.hpp
+++ b/include/mapnik/webp_io.hpp
@@ -28,13 +28,13 @@
 #include <mapnik/util/conversions.hpp>
 
 // webp
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
 extern "C"
 {
 #include <webp/encode.h>
 }
-#pragma clang diagnostic pop
+#pragma GCC diagnostic pop
 
 // stl
 #include <algorithm>
diff --git a/plugins/input/csv/build.py b/plugins/input/csv/build.py
index 2694109..f21c93f 100644
--- a/plugins/input/csv/build.py
+++ b/plugins/input/csv/build.py
@@ -42,18 +42,19 @@ else:
         %(PLUGIN_NAME)s_datasource.cpp
         %(PLUGIN_NAME)s_featureset.cpp
         %(PLUGIN_NAME)s_inline_featureset.cpp
+        %(PLUGIN_NAME)s_index_featureset.cpp
         """ % locals()
     )
 
     # Link Library to Dependencies
     libraries = []
-    libraries.append('boost_system%s' % env['BOOST_APPEND'])
-    libraries.append(env['ICU_LIB_NAME'])
     libraries.append('mapnik-json')
     libraries.append('mapnik-wkt')
 
     if env['PLUGIN_LINKING'] == 'shared':
-        libraries.append(env['MAPNIK_NAME'])
+        libraries.append('boost_system%s' % env['BOOST_APPEND'])
+        libraries.insert(0,env['MAPNIK_NAME'])
+        libraries.append(env['ICU_LIB_NAME'])
 
         TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                           SHLIBPREFIX='',
diff --git a/plugins/input/csv/csv_datasource.cpp b/plugins/input/csv/csv_datasource.cpp
index 8b15fc9..8da4691 100644
--- a/plugins/input/csv/csv_datasource.cpp
+++ b/plugins/input/csv/csv_datasource.cpp
@@ -24,6 +24,7 @@
 #include "csv_datasource.hpp"
 #include "csv_featureset.hpp"
 #include "csv_inline_featureset.hpp"
+#include "csv_index_featureset.hpp"
 // boost
 #include <boost/algorithm/string.hpp>
 // mapnik
@@ -37,10 +38,16 @@
 #include <mapnik/util/trim.hpp>
 #include <mapnik/util/geometry_to_ds_type.hpp>
 #include <mapnik/value_types.hpp>
-
+#include <mapnik/util/fs.hpp>
+#include <mapnik/util/spatial_index.hpp>
+#include <mapnik/geom_util.hpp>
 #ifdef CSV_MEMORY_MAPPED_FILE
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
 #include <boost/interprocess/mapped_region.hpp>
 #include <boost/interprocess/streams/bufferstream.hpp>
+#pragma GCC diagnostic pop
 #include <mapnik/mapped_memory_cache.hpp>
 #endif
 
@@ -56,13 +63,6 @@ using mapnik::parameters;
 
 DATASOURCE_PLUGIN(csv_datasource)
 
-
-namespace {
-
-using cvs_value = mapnik::util::variant<std::string, mapnik::value_integer, mapnik::value_double, mapnik::value_bool>;
-
-}
-
 csv_datasource::csv_datasource(parameters const& params)
 : datasource(params),
     desc_(csv_datasource::name(), *params.get<std::string>("encoding", "utf-8")),
@@ -70,17 +70,31 @@ csv_datasource::csv_datasource(parameters const& params)
     filename_(),
     row_limit_(*params.get<mapnik::value_integer>("row_limit", 0)),
     inline_string_(),
-    escape_(*params.get<std::string>("escape", "")),
-    separator_(*params.get<std::string>("separator", "")),
-    quote_(*params.get<std::string>("quote", "")),
+    separator_(0),
+    quote_(0),
     headers_(),
     manual_headers_(mapnik::util::trim_copy(*params.get<std::string>("headers", ""))),
     strict_(*params.get<mapnik::boolean_type>("strict", false)),
     ctx_(std::make_shared<mapnik::context_type>()),
     extent_initialized_(false),
     tree_(nullptr),
-    locator_()
+    locator_(),
+    has_disk_index_(false)
 {
+    auto quote_param = params.get<std::string>("quote");
+    if (quote_param)
+    {
+        auto val = mapnik::util::trim_copy(*quote_param);
+        if (!val.empty()) quote_ = val.front(); // we pick pick first non-space char
+    }
+
+    auto separator_param = params.get<std::string>("separator");
+    if (separator_param)
+    {
+        auto val = mapnik::util::trim_copy(*separator_param);
+        if (!val.empty()) separator_ = val.front();
+    }
+
     boost::optional<std::string> ext = params.get<std::string>("extent");
     if (ext && !ext->empty())
     {
@@ -101,11 +115,13 @@ csv_datasource::csv_datasource(parameters const& params)
             filename_ = *base + "/" + *file;
         else
             filename_ = *file;
+
+        has_disk_index_ = mapnik::util::exists(filename_ + ".index");
     }
     if (!inline_string_.empty())
     {
         std::istringstream in(inline_string_);
-        parse_csv(in, escape_, separator_, quote_);
+        parse_csv(in);
     }
     else
     {
@@ -137,7 +153,18 @@ csv_datasource::csv_datasource(parameters const& params)
             throw mapnik::datasource_exception("CSV Plugin: could not open: '" + filename_ + "'");
         }
 #endif
-        parse_csv(in, escape_, separator_, quote_);
+        parse_csv(in);
+
+        if (has_disk_index_ && !extent_initialized_)
+        {
+            // read bounding box from *.index
+            using value_type = std::pair<std::size_t, std::size_t>;
+            std::ifstream index(filename_ + ".index", std::ios::binary);
+            if (!index) throw mapnik::datasource_exception("CSV Plugin: could not open: '" + filename_ + ".index'");
+            extent_ = mapnik::util::spatial_index<value_type,
+                                                  mapnik::filter_in_box,
+                                                  std::ifstream>::bounding_box(index);
+        }
         //in.close(); no need to call close, rely on dtor
     }
 }
@@ -145,60 +172,47 @@ csv_datasource::csv_datasource(parameters const& params)
 csv_datasource::~csv_datasource() {}
 
 template <typename T>
-void csv_datasource::parse_csv(T & stream,
-                               std::string const& escape,
-                               std::string const& separator,
-                               std::string const& quote)
+void csv_datasource::parse_csv(T & stream)
 {
     auto file_length = detail::file_length(stream);
     // set back to start
     stream.seekg(0, std::ios::beg);
     char newline;
     bool has_newline;
-    std::tie(newline, has_newline) = detail::autodect_newline(stream, file_length);
+    char detected_quote;
+    std::tie(newline, has_newline, detected_quote) = detail::autodect_newline_and_quote(stream, file_length);
+    if (quote_ == 0) quote_ = detected_quote;
     // set back to start
     stream.seekg(0, std::ios::beg);
-    // get first line
     std::string csv_line;
-    std::getline(stream,csv_line,stream.widen(newline));
-    // if user has not passed a separator manually
-    // then attempt to detect by reading first line
-
-    std::string sep = mapnik::util::trim_copy(separator);
-    if (sep.empty())  sep = detail::detect_separator(csv_line);
-    separator_ = sep;
+    csv_utils::getline_csv(stream, csv_line, newline, quote_);
+    if (separator_ == 0)
+    {
+        separator_ = detail::detect_separator(csv_line);
+    }
 
-    // set back to start
+    MAPNIK_LOG_DEBUG(csv) << "csv_datasource: separator: '" << separator_
+                          << "' quote: '" << quote_ << "'";
     stream.seekg(0, std::ios::beg);
 
-    std::string esc = mapnik::util::trim_copy(escape);
-    if (esc.empty()) esc = "\\";
-
-    std::string quo = mapnik::util::trim_copy(quote);
-    if (quo.empty()) quo = "\"";
-
-    MAPNIK_LOG_DEBUG(csv) << "csv_datasource: csv grammar: sep: '" << sep
-                          << "' quo: '" << quo << "' esc: '" << esc << "'";
-
     int line_number = 1;
     if (!manual_headers_.empty())
     {
         std::size_t index = 0;
-        auto headers = csv_utils::parse_line(manual_headers_, sep);
+        auto headers = csv_utils::parse_line(manual_headers_, separator_, quote_);
         for (auto const& header : headers)
         {
-            std::string val = mapnik::util::trim_copy(header);
-            detail::locate_geometry_column(val, index++, locator_);
-            headers_.push_back(val);
+            detail::locate_geometry_column(header, index++, locator_);
+            headers_.push_back(header);
         }
     }
     else // parse first line as headers
     {
-        while (std::getline(stream,csv_line,stream.widen(newline)))
+        while (csv_utils::getline_csv(stream, csv_line, newline, quote_))
         {
             try
             {
-                auto headers = csv_utils::parse_line(csv_line, sep);
+                auto headers = csv_utils::parse_line(csv_line, separator_, quote_);
                 // skip blank lines
                 std::string val;
                 if (headers.size() > 0 && headers[0].empty()) ++line_number;
@@ -261,11 +275,12 @@ void csv_datasource::parse_csv(T & stream,
                   [ & ](std::string const& header){ ctx_->push(header); });
 
     mapnik::transcoder tr(desc_.get_encoding());
-    auto pos = stream.tellg();
 
+    auto pos = stream.tellg();
     // handle rare case of a single line of data and user-provided headers
-    // where a lack of a newline will mean that std::getline returns false
+    // where a lack of a newline will mean that csv_utils::getline_csv returns false
     bool is_first_row = false;
+
     if (!has_newline)
     {
         stream.setstate(std::ios::failbit);
@@ -275,9 +290,11 @@ void csv_datasource::parse_csv(T & stream,
             is_first_row = true;
         }
     }
+
     std::vector<item_type> boxes;
-    while (is_first_row || std::getline(stream, csv_line, stream.widen(newline)))
+    while (is_first_row || csv_utils::getline_csv(stream, csv_line, newline, quote_))
     {
+
         if ((row_limit_ > 0) && (line_number++ > row_limit_))
         {
             MAPNIK_LOG_DEBUG(csv) << "csv_datasource: row limit hit, exiting at feature: " << feature_count;
@@ -287,9 +304,9 @@ void csv_datasource::parse_csv(T & stream,
         auto record_size = csv_line.length();
         pos = stream.tellg();
         is_first_row = false;
+
         // skip blank lines
-        unsigned line_length = csv_line.length();
-        if (line_length <= 10)
+        if (record_size <= 10)
         {
             std::string trimmed = csv_line;
             boost::trim_if(trimmed,boost::algorithm::is_any_of("\",'\r\n "));
@@ -302,9 +319,9 @@ void csv_datasource::parse_csv(T & stream,
 
         try
         {
-            auto values = csv_utils::parse_line(csv_line, sep);
+            auto values = csv_utils::parse_line(csv_line, separator_, quote_);
             unsigned num_fields = values.size();
-            if (num_fields > num_headers)
+            if (num_fields > num_headers || num_fields < num_headers)
             {
                 std::ostringstream s;
                 s << "CSV Plugin: # of columns("
@@ -312,21 +329,6 @@ void csv_datasource::parse_csv(T & stream,
                   << num_headers << ") parsed for row " << line_number << "\n";
                 throw mapnik::datasource_exception(s.str());
             }
-            else if (num_fields < num_headers)
-            {
-                std::ostringstream s;
-                s << "CSV Plugin: # of headers("
-                  << num_headers << ") > # of columns("
-                  << num_fields << ") parsed for row " << line_number << "\n";
-                if (strict_)
-                {
-                    throw mapnik::datasource_exception(s.str());
-                }
-                else
-                {
-                    MAPNIK_LOG_WARN(csv) << s.str();
-                }
-            }
 
             auto geom = detail::extract_geometry(values, locator_);
             if (!geom.is<mapnik::geometry::geometry_empty>())
@@ -430,14 +432,7 @@ void csv_datasource::parse_csv(T & stream,
                 s << "CSV Plugin: expected geometry column: could not parse row "
                   << line_number << " "
                   << values[locator_.index] << "'";
-                if (strict_)
-                {
-                    throw mapnik::datasource_exception(s.str());
-                }
-                else
-                {
-                    MAPNIK_LOG_ERROR(csv) << s.str();
-                }
+                throw mapnik::datasource_exception(s.str());
             }
         }
         catch (mapnik::datasource_exception const& ex )
@@ -463,6 +458,8 @@ void csv_datasource::parse_csv(T & stream,
                 MAPNIK_LOG_ERROR(csv) << s.str();
             }
         }
+        // return early if *.index is present
+        if (has_disk_index_) return;
     }
     // bulk insert initialise r-tree
     tree_ = std::make_unique<spatial_index_type>(boxes);
@@ -492,40 +489,87 @@ template <typename T>
 boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type_impl(T & stream) const
 {
     boost::optional<mapnik::datasource_geometry_t> result;
-    int multi_type = 0;
-    auto itr = tree_->qbegin(boost::geometry::index::intersects(extent_));
-    auto end = tree_->qend();
-    for (std::size_t count = 0; itr !=end &&  count < 5; ++itr, ++count)
+    if (tree_)
     {
-        csv_datasource::item_type const& item = *itr;
-        std::size_t file_offset = item.second.first;
-        std::size_t size = item.second.second;
-        stream.seekg(file_offset);
-        std::vector<char> record;
-        record.resize(size);
-        stream.read(record.data(), size);
-        std::string str(record.begin(), record.end());
-        try
+        int multi_type = 0;
+        auto itr = tree_->qbegin(boost::geometry::index::intersects(extent_));
+        auto end = tree_->qend();
+        for (std::size_t count = 0; itr !=end &&  count < 5; ++itr, ++count)
         {
-            auto values = csv_utils::parse_line(str, separator_);
-            auto geom = detail::extract_geometry(values, locator_);
-            result = mapnik::util::to_ds_type(geom);
-            if (result)
+            csv_datasource::item_type const& item = *itr;
+            std::size_t file_offset = item.second.first;
+            std::size_t size = item.second.second;
+            stream.seekg(file_offset);
+            std::vector<char> record;
+            record.resize(size);
+            stream.read(record.data(), size);
+            std::string str(record.begin(), record.end());
+            try
             {
-                int type = static_cast<int>(*result);
-                if (multi_type > 0 && multi_type != type)
+                auto values = csv_utils::parse_line(str, separator_, quote_);
+                auto geom = detail::extract_geometry(values, locator_);
+                result = mapnik::util::to_ds_type(geom);
+                if (result)
                 {
-                    result.reset(mapnik::datasource_geometry_t::Collection);
-                    return result;
+                    int type = static_cast<int>(*result);
+                    if (multi_type > 0 && multi_type != type)
+                    {
+                        result.reset(mapnik::datasource_geometry_t::Collection);
+                        return result;
+                    }
+                    multi_type = type;
                 }
-                multi_type = type;
+            }
+            catch (std::exception const& ex)
+            {
+                if (strict_) throw ex;
+                else MAPNIK_LOG_ERROR(csv) << ex.what();
             }
         }
-        catch (std::exception const& ex)
+    }
+    else
+    {
+        // try reading *.index
+        using value_type = std::pair<std::size_t, std::size_t>;
+        std::ifstream index(filename_ + ".index", std::ios::binary);
+        if (!index) throw mapnik::datasource_exception("CSV Plugin: could not open: '" + filename_ + ".index'");
+
+        mapnik::filter_in_box filter(extent_);
+        std::vector<value_type> positions;
+        mapnik::util::spatial_index<value_type,
+                                    mapnik::filter_in_box,
+                                    std::ifstream>::query_first_n(filter, index, positions, 5);
+        int multi_type = 0;
+        for (auto const& val : positions)
         {
-            if (strict_) throw ex;
-            else MAPNIK_LOG_ERROR(csv) << ex.what();
+            stream.seekg(val.first);
+            std::vector<char> record;
+            record.resize(val.second);
+            stream.read(record.data(), val.second);
+            std::string str(record.begin(), record.end());
+            try
+            {
+                auto values = csv_utils::parse_line(str, separator_, quote_);
+                auto geom = detail::extract_geometry(values, locator_);
+                result = mapnik::util::to_ds_type(geom);
+                if (result)
+                {
+                    int type = static_cast<int>(*result);
+                    if (multi_type > 0 && multi_type != type)
+                    {
+                        result.reset(mapnik::datasource_geometry_t::Collection);
+                        return result;
+                    }
+                    multi_type = type;
+                }
+            }
+            catch (std::exception const& ex)
+            {
+                if (strict_) throw ex;
+                else MAPNIK_LOG_ERROR(csv) << ex.what();
+            }
         }
+
     }
     return result;
 }
@@ -554,7 +598,6 @@ boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type
 
 mapnik::featureset_ptr csv_datasource::features(mapnik::query const& q) const
 {
-
     for (auto const& name : q.property_names())
     {
         bool found_name = false;
@@ -589,13 +632,18 @@ mapnik::featureset_ptr csv_datasource::features(mapnik::query const& q) const
                       });
             if (inline_string_.empty())
             {
-                return std::make_shared<csv_featureset>(filename_, locator_, separator_, headers_, ctx_, std::move(index_array));
+                return std::make_shared<csv_featureset>(filename_, locator_, separator_, quote_, headers_, ctx_, std::move(index_array));
             }
             else
             {
-                return std::make_shared<csv_inline_featureset>(inline_string_, locator_, separator_, headers_, ctx_, std::move(index_array));
+                return std::make_shared<csv_inline_featureset>(inline_string_, locator_, separator_, quote_, headers_, ctx_, std::move(index_array));
             }
         }
+        else if (has_disk_index_)
+        {
+            mapnik::filter_in_box filter(q.get_bbox());
+            return std::make_shared<csv_index_featureset>(filename_, filter, locator_, separator_, quote_, headers_, ctx_);
+        }
     }
     return mapnik::featureset_ptr();
 }
diff --git a/plugins/input/csv/csv_datasource.hpp b/plugins/input/csv/csv_datasource.hpp
index 06b5daf..3faf0cf 100644
--- a/plugins/input/csv/csv_datasource.hpp
+++ b/plugins/input/csv/csv_datasource.hpp
@@ -88,14 +88,10 @@ public:
     mapnik::box2d<double> envelope() const;
     mapnik::layer_descriptor get_descriptor() const;
     boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
-    template <typename T>
-    void parse_csv(T & stream,
-                   std::string const& escape,
-                   std::string const& separator,
-                   std::string const& quote);
-
 private:
     template <typename T>
+    void parse_csv(T & stream);
+    template <typename T>
     boost::optional<mapnik::datasource_geometry_t> get_geometry_type_impl(T & stream) const;
 
     mapnik::layer_descriptor desc_;
@@ -103,9 +99,8 @@ private:
     std::string filename_;
     mapnik::value_integer row_limit_;
     std::string inline_string_;
-    std::string escape_;
-    std::string separator_;
-    std::string quote_;
+    char separator_;
+    char quote_;
     std::vector<std::string> headers_;
     std::string manual_headers_;
     bool strict_;
@@ -113,6 +108,7 @@ private:
     bool extent_initialized_;
     std::unique_ptr<spatial_index_type> tree_;
     detail::geometry_column_locator locator_;
+    bool has_disk_index_;
 };
 
 #endif // MAPNIK_CSV_DATASOURCE_HPP
diff --git a/plugins/input/csv/csv_featureset.cpp b/plugins/input/csv/csv_featureset.cpp
index acda58e..8a94875 100644
--- a/plugins/input/csv/csv_featureset.cpp
+++ b/plugins/input/csv/csv_featureset.cpp
@@ -31,7 +31,7 @@
 #include <vector>
 #include <deque>
 
-csv_featureset::csv_featureset(std::string const& filename, detail::geometry_column_locator const& locator, std::string const& separator,
+csv_featureset::csv_featureset(std::string const& filename, detail::geometry_column_locator const& locator, char separator, char quote,
                                std::vector<std::string> const& headers, mapnik::context_ptr const& ctx, array_type && index_array)
     :
 #if defined(CSV_MEMORY_MAPPED_FILE)
@@ -42,6 +42,7 @@ csv_featureset::csv_featureset(std::string const& filename, detail::geometry_col
     file_(std::fopen(filename.c_str(),"rb"), std::fclose),
 #endif
     separator_(separator),
+    quote_(quote),
     headers_(headers),
     index_array_(std::move(index_array)),
     index_itr_(index_array_.begin()),
@@ -70,7 +71,7 @@ csv_featureset::~csv_featureset() {}
 
 mapnik::feature_ptr csv_featureset::parse_feature(char const* beg, char const* end)
 {
-    auto values = csv_utils::parse_line(beg, end, separator_, headers_.size());
+    auto values = csv_utils::parse_line(beg, end, separator_, quote_, headers_.size());
     auto geom = detail::extract_geometry(values, locator_);
     if (!geom.is<mapnik::geometry::geometry_empty>())
     {
diff --git a/plugins/input/csv/csv_featureset.hpp b/plugins/input/csv/csv_featureset.hpp
index 59afa1d..3f05c08 100644
--- a/plugins/input/csv/csv_featureset.hpp
+++ b/plugins/input/csv/csv_featureset.hpp
@@ -31,20 +31,24 @@
 #include <cstdio>
 
 #ifdef CSV_MEMORY_MAPPED_FILE
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
 #include <boost/interprocess/mapped_region.hpp>
 #include <boost/interprocess/streams/bufferstream.hpp>
+#pragma GCC diagnostic pop
 #include <mapnik/mapped_memory_cache.hpp>
 #endif
 
 class csv_featureset : public mapnik::Featureset
 {
-
     using locator_type = detail::geometry_column_locator;
 public:
     using array_type = std::deque<csv_datasource::item_type>;
     csv_featureset(std::string const& filename,
                    locator_type const& locator,
-                   std::string const& separator,
+                   char separator,
+                   char quote,
                    std::vector<std::string> const& headers,
                    mapnik::context_ptr const& ctx,
                    array_type && index_array);
@@ -59,7 +63,8 @@ private:
     using file_ptr = std::unique_ptr<std::FILE, int (*)(std::FILE *)>;
     file_ptr file_;
 #endif
-    std::string const& separator_;
+    char separator_;
+    char quote_;
     std::vector<std::string> const& headers_;
     const array_type index_array_;
     array_type::const_iterator index_itr_;
diff --git a/plugins/input/csv/csv_index_featureset.cpp b/plugins/input/csv/csv_index_featureset.cpp
new file mode 100644
index 0000000..4a13551
--- /dev/null
+++ b/plugins/input/csv/csv_index_featureset.cpp
@@ -0,0 +1,130 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2015 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
+ *
+ *****************************************************************************/
+
+// mapnik
+#include "csv_index_featureset.hpp"
+#include <mapnik/debug.hpp>
+#include <mapnik/feature.hpp>
+#include <mapnik/feature_factory.hpp>
+#include <mapnik/util/utf_conv_win.hpp>
+#include <mapnik/util/trim.hpp>
+#include <mapnik/util/spatial_index.hpp>
+#include <mapnik/geometry.hpp>
+// stl
+#include <string>
+#include <vector>
+#include <deque>
+#include <fstream>
+
+csv_index_featureset::csv_index_featureset(std::string const& filename,
+                                           mapnik::filter_in_box const& filter,
+                                           detail::geometry_column_locator const& locator,
+                                           char separator,
+                                           char quote,
+                                           std::vector<std::string> const& headers,
+                                           mapnik::context_ptr const& ctx)
+    : separator_(separator),
+      quote_(quote),
+      headers_(headers),
+      ctx_(ctx),
+      locator_(locator),
+      tr_("utf8")
+#if defined(CSV_MEMORY_MAPPED_FILE)
+      //
+#elif defined( _WINDOWS)
+    ,file_(_wfopen(mapnik::utf8_to_utf16(filename).c_str(), L"rb"), std::fclose)
+#else
+    ,file_(std::fopen(filename.c_str(),"rb"), std::fclose)
+#endif
+
+{
+#if defined (CSV_MEMORY_MAPPED_FILE)
+    boost::optional<mapnik::mapped_region_ptr> memory =
+        mapnik::mapped_memory_cache::instance().find(filename, true);
+    if (memory)
+    {
+        mapped_region_ = *memory;
+    }
+    else
+    {
+        throw std::runtime_error("could not create file mapping for " + filename);
+    }
+#else
+    if (!file_) throw mapnik::datasource_exception("CSV Plugin: can't open file " + filename);
+#endif
+
+    std::string indexname = filename + ".index";
+    std::ifstream index(indexname.c_str(), std::ios::binary);
+    if (!index) throw mapnik::datasource_exception("CSV Plugin: can't open index file " + indexname);
+    mapnik::util::spatial_index<value_type,
+                                mapnik::filter_in_box,
+                                std::ifstream>::query(filter, index, positions_);
+
+    std::sort(positions_.begin(), positions_.end(),
+              [](value_type const& lhs, value_type const& rhs) { return lhs.first < rhs.first;});
+    itr_ = positions_.begin();
+}
+
+csv_index_featureset::~csv_index_featureset() {}
+
+mapnik::feature_ptr csv_index_featureset::parse_feature(char const* beg, char const* end)
+{
+    auto values = csv_utils::parse_line(beg, end, separator_, quote_, headers_.size());
+    auto geom = detail::extract_geometry(values, locator_);
+    if (!geom.is<mapnik::geometry::geometry_empty>())
+    {
+        mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_id_));
+        feature->set_geometry(std::move(geom));
+        detail::process_properties(*feature, headers_, values, locator_, tr_);
+        return feature;
+    }
+    return mapnik::feature_ptr();
+}
+
+mapnik::feature_ptr csv_index_featureset::next()
+{
+    /*
+    if (row_limit_ && count_ >= row_limit_)
+    {
+        return feature_ptr();
+    }
+    */
+
+    while( itr_ != positions_.end())
+    {
+        auto pos = *itr_++;
+#if defined(CSV_MEMORY_MAPPED_FILE)
+        char const* start = (char const*)mapped_region_->get_address() + pos.first;
+        char const*  end = start + pos.second;
+#else
+        std::fseek(file_.get(), pos.first, SEEK_SET);
+        std::vector<char> record;
+        record.resize(pos.second);
+        std::fread(record.data(), pos.second, 1, file_.get());
+        auto const* start = record.data();
+        auto const*  end = start + record.size();
+#endif
+        auto feature = parse_feature(start, end);
+        if (feature) return feature;
+    }
+    return mapnik::feature_ptr();
+}
diff --git a/plugins/input/csv/csv_featureset.hpp b/plugins/input/csv/csv_index_featureset.hpp
similarity index 66%
copy from plugins/input/csv/csv_featureset.hpp
copy to plugins/input/csv/csv_index_featureset.hpp
index 59afa1d..5980afb 100644
--- a/plugins/input/csv/csv_featureset.hpp
+++ b/plugins/input/csv/csv_index_featureset.hpp
@@ -20,38 +20,49 @@
  *
  *****************************************************************************/
 
-#ifndef CSV_FEATURESET_HPP
-#define CSV_FEATURESET_HPP
+#ifndef CSV_INDEX_FEATURESET_HPP
+#define CSV_INDEX_FEATURESET_HPP
 
 #include <mapnik/feature.hpp>
 #include <mapnik/unicode.hpp>
+#include <mapnik/geom_util.hpp>
 #include "csv_utils.hpp"
 #include "csv_datasource.hpp"
-#include <deque>
-#include <cstdio>
 
 #ifdef CSV_MEMORY_MAPPED_FILE
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
 #include <boost/interprocess/mapped_region.hpp>
 #include <boost/interprocess/streams/bufferstream.hpp>
+#pragma GCC diagnostic pop
 #include <mapnik/mapped_memory_cache.hpp>
 #endif
 
-class csv_featureset : public mapnik::Featureset
+class csv_index_featureset : public mapnik::Featureset
 {
-
+    using value_type = std::pair<std::size_t, std::size_t>;
     using locator_type = detail::geometry_column_locator;
 public:
-    using array_type = std::deque<csv_datasource::item_type>;
-    csv_featureset(std::string const& filename,
-                   locator_type const& locator,
-                   std::string const& separator,
-                   std::vector<std::string> const& headers,
-                   mapnik::context_ptr const& ctx,
-                   array_type && index_array);
-    ~csv_featureset();
+
+    csv_index_featureset(std::string const& filename,
+                         mapnik::filter_in_box const& filter,
+                         locator_type const& locator,
+                         char separator,
+                         char quote,
+                         std::vector<std::string> const& headers,
+                         mapnik::context_ptr const& ctx);
+    ~csv_index_featureset();
     mapnik::feature_ptr next();
 private:
     mapnik::feature_ptr parse_feature(char const* beg, char const* end);
+    char separator_;
+    char quote_;
+    std::vector<std::string> headers_;
+    mapnik::context_ptr ctx_;
+    mapnik::value_integer feature_id_ = 0;
+    detail::geometry_column_locator const& locator_;
+    mapnik::transcoder tr_;
 #if defined (CSV_MEMORY_MAPPED_FILE)
     using file_source_type = boost::interprocess::ibufferstream;
     mapnik::mapped_region_ptr mapped_region_;
@@ -59,16 +70,9 @@ private:
     using file_ptr = std::unique_ptr<std::FILE, int (*)(std::FILE *)>;
     file_ptr file_;
 #endif
-    std::string const& separator_;
-    std::vector<std::string> const& headers_;
-    const array_type index_array_;
-    array_type::const_iterator index_itr_;
-    array_type::const_iterator index_end_;
-    mapnik::context_ptr ctx_;
-    mapnik::value_integer feature_id_ = 0;
-    detail::geometry_column_locator const& locator_;
-    mapnik::transcoder tr_;
+    std::vector<value_type> positions_;
+    std::vector<value_type>::iterator itr_;
 };
 
 
-#endif // CSV_FEATURESET_HPP
+#endif // CSV_INDEX_FEATURESET_HPP
diff --git a/plugins/input/csv/csv_inline_featureset.cpp b/plugins/input/csv/csv_inline_featureset.cpp
index 29b2203..b0fe420 100644
--- a/plugins/input/csv/csv_inline_featureset.cpp
+++ b/plugins/input/csv/csv_inline_featureset.cpp
@@ -34,12 +34,14 @@
 
 csv_inline_featureset::csv_inline_featureset(std::string const& inline_string,
                                              detail::geometry_column_locator const& locator,
-                                             std::string const& separator,
+                                             char separator,
+                                             char quote,
                                              std::vector<std::string> const& headers,
                                              mapnik::context_ptr const& ctx,
                                              array_type && index_array)
     : inline_string_(inline_string),
       separator_(separator),
+      quote_(quote),
       headers_(headers),
       index_array_(std::move(index_array)),
       index_itr_(index_array_.begin()),
@@ -52,7 +54,7 @@ csv_inline_featureset::~csv_inline_featureset() {}
 
 mapnik::feature_ptr csv_inline_featureset::parse_feature(std::string const& str)
 {
-    auto values = csv_utils::parse_line(str, separator_);
+    auto values = csv_utils::parse_line(str, separator_, quote_);
     auto geom = detail::extract_geometry(values, locator_);
     if (!geom.is<mapnik::geometry::geometry_empty>())
     {
diff --git a/plugins/input/csv/csv_inline_featureset.hpp b/plugins/input/csv/csv_inline_featureset.hpp
index 9e06be8..3da9f63 100644
--- a/plugins/input/csv/csv_inline_featureset.hpp
+++ b/plugins/input/csv/csv_inline_featureset.hpp
@@ -36,17 +36,19 @@ class csv_inline_featureset : public mapnik::Featureset
 public:
     using array_type = std::deque<csv_datasource::item_type>;
     csv_inline_featureset(std::string const& inline_string,
-                   locator_type const& locator,
-                   std::string const& separator,
-                   std::vector<std::string> const& headers,
-                   mapnik::context_ptr const& ctx,
-                   array_type && index_array);
+                          locator_type const& locator,
+                          char separator,
+                          char quote,
+                          std::vector<std::string> const& headers,
+                          mapnik::context_ptr const& ctx,
+                          array_type && index_array);
     ~csv_inline_featureset();
     mapnik::feature_ptr next();
 private:
     mapnik::feature_ptr parse_feature(std::string const& str);
     std::string const& inline_string_;
-    std::string const& separator_;
+    char separator_;
+    char quote_;
     std::vector<std::string> headers_;
     const array_type index_array_;
     array_type::const_iterator index_itr_;
diff --git a/plugins/input/csv/csv_utils.hpp b/plugins/input/csv/csv_utils.hpp
index b84e92d..77a2710 100644
--- a/plugins/input/csv/csv_utils.hpp
+++ b/plugins/input/csv/csv_utils.hpp
@@ -52,30 +52,30 @@ namespace csv_utils
 {
 
 static const mapnik::csv_line_grammar<char const*> line_g;
+static const mapnik::csv_white_space_skipper<char const*> skipper;
 
 template <typename Iterator>
-static mapnik::csv_line parse_line(Iterator start, Iterator end, std::string const& separator, std::size_t num_columns)
+static mapnik::csv_line parse_line(Iterator start, Iterator end, char separator, char quote, std::size_t num_columns)
 {
     mapnik::csv_line values;
     if (num_columns > 0) values.reserve(num_columns);
-    boost::spirit::standard::blank_type blank;
-    if (!boost::spirit::qi::phrase_parse(start, end, (line_g)(boost::phoenix::cref(separator)), blank, values))
+    if (!boost::spirit::qi::phrase_parse(start, end, (line_g)(separator, quote), skipper, values))
     {
         throw std::runtime_error("Failed to parse CSV line:\n" + std::string(start, end));
     }
     return values;
 }
 
-static inline mapnik::csv_line parse_line(std::string const& line_str, std::string const& separator)
+static inline mapnik::csv_line parse_line(std::string const& line_str, char separator, char quote)
 {
     auto start = line_str.c_str();
     auto end   = start + line_str.length();
-    return parse_line(start, end, separator, 0);
+    return parse_line(start, end, separator, quote, 0);
 }
 
 static inline bool is_likely_number(std::string const& value)
 {
-    return( strspn( value.c_str(), "e-.+0123456789" ) == value.size() );
+    return (std::strspn( value.c_str(), "e-.+0123456789" ) == value.size());
 }
 
 struct ignore_case_equal_pred
@@ -92,6 +92,43 @@ inline bool ignore_case_equal(std::string const& s0, std::string const& s1)
                       s1.begin(), ignore_case_equal_pred());
 }
 
+template <class CharT, class Traits, class Allocator>
+std::basic_istream<CharT, Traits>& getline_csv(std::istream& is, std::basic_string<CharT,Traits,Allocator>& s, CharT delim, CharT quote)
+{
+    typename std::basic_string<CharT,Traits,Allocator>::size_type nread = 0;
+    typename std::basic_istream<CharT, Traits>::sentry sentry(is, true);
+    if (sentry)
+    {
+        std::basic_streambuf<CharT, Traits>* buf = is.rdbuf();
+        s.clear();
+        bool has_quote = false;
+        while (nread < s.max_size())
+        {
+            int c1 = buf->sbumpc();
+            if (Traits::eq_int_type(c1, Traits::eof()))
+            {
+                is.setstate(std::ios_base::eofbit);
+                break;
+            }
+            else
+            {
+                ++nread;
+                CharT c = Traits::to_char_type(c1);
+                if (Traits::eq(c, quote))
+                    has_quote = !has_quote;
+                if (!Traits::eq(c, delim) || has_quote)
+                    s.push_back(c);
+                else
+                    break;// Character is extracted but not appended.
+            }
+        }
+    }
+    if (nread == 0 || nread >= s.max_size())
+        is.setstate(std::ios_base::failbit);
+
+    return is;
+}
+
 }
 
 
@@ -104,9 +141,9 @@ std::size_t file_length(T & stream)
     return stream.tellg();
 }
 
-static inline std::string detect_separator(std::string const& str)
+static inline char detect_separator(std::string const& str)
 {
-    std::string separator = ","; // default
+    char separator = ','; // default
     int num_commas = std::count(str.begin(), str.end(), ',');
     // detect tabs
     int num_tabs = std::count(str.begin(), str.end(), '\t');
@@ -114,7 +151,7 @@ static inline std::string detect_separator(std::string const& str)
     {
         if (num_tabs > num_commas)
         {
-            separator = "\t";
+            separator = '\t';
             MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
         }
     }
@@ -123,7 +160,7 @@ static inline std::string detect_separator(std::string const& str)
         int num_pipes = std::count(str.begin(), str.end(), '|');
         if (num_pipes > num_commas)
         {
-            separator = "|";
+            separator = '|';
             MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
         }
         else // semicolons
@@ -131,7 +168,7 @@ static inline std::string detect_separator(std::string const& str)
             int num_semicolons = std::count(str.begin(), str.end(), ';');
             if (num_semicolons > num_commas)
             {
-                separator = ";";
+                separator = ';';
                 MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
             }
         }
@@ -140,11 +177,13 @@ static inline std::string detect_separator(std::string const& str)
 }
 
 template <typename T>
-std::tuple<char,bool> autodect_newline(T & stream, std::size_t file_length)
+std::tuple<char,bool,char> autodect_newline_and_quote(T & stream, std::size_t file_length)
 {
     // autodetect newlines
     char newline = '\n';
     bool has_newline = false;
+    char quote = '"';
+    bool has_quote = false;
     static std::size_t const max_size = 4000;
     std::size_t size = std::min(file_length, max_size);
     for (std::size_t lidx = 0; lidx < size; ++lidx)
@@ -154,15 +193,20 @@ std::tuple<char,bool> autodect_newline(T & stream, std::size_t file_length)
         {
             newline = '\r';
             has_newline = true;
-            break;
+            //break;
         }
         if (c == '\n')
         {
             has_newline = true;
-            break;
+            //break;
+        }
+        else if (!has_quote && c == '\'')
+        {
+            quote = '\'';
+            has_quote = true;
         }
     }
-    return std::make_tuple(newline,has_newline);
+    return std::make_tuple(newline, has_newline, quote);
 }
 
 
diff --git a/plugins/input/gdal/build.py b/plugins/input/gdal/build.py
index 6c80de2..0db10c2 100644
--- a/plugins/input/gdal/build.py
+++ b/plugins/input/gdal/build.py
@@ -45,9 +45,9 @@ if env['RUNTIME_LINK'] == 'static':
 libraries = copy(plugin_env['LIBS'])
 
 if env['PLUGIN_LINKING'] == 'shared':
+    libraries.append('boost_system%s' % env['BOOST_APPEND'])
     libraries.insert(0,env['MAPNIK_NAME'])
     libraries.append(env['ICU_LIB_NAME'])
-    libraries.append('boost_system%s' % env['BOOST_APPEND'])
 
     TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                       SHLIBPREFIX='',
diff --git a/plugins/input/geojson/build.py b/plugins/input/geojson/build.py
index 7435be5..9001360 100644
--- a/plugins/input/geojson/build.py
+++ b/plugins/input/geojson/build.py
@@ -48,12 +48,12 @@ else:
 
     # Link Library to Dependencies
     libraries = []
-    libraries.append(env['ICU_LIB_NAME'])
-    libraries.append('boost_system%s' % env['BOOST_APPEND'])
     libraries.append('mapnik-json')
 
     if env['PLUGIN_LINKING'] == 'shared':
-        libraries.append(env['MAPNIK_NAME'])
+        libraries.append('boost_system%s' % env['BOOST_APPEND'])
+        libraries.insert(0,env['MAPNIK_NAME'])
+        libraries.append(env['ICU_LIB_NAME'])
 
         TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                           SHLIBPREFIX='',
diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp
index e1ebb47..15a9503 100644
--- a/plugins/input/geojson/geojson_datasource.cpp
+++ b/plugins/input/geojson/geojson_datasource.cpp
@@ -31,6 +31,7 @@
 #pragma GCC diagnostic ignored "-Wunused-parameter"
 #pragma GCC diagnostic ignored "-Wunused-local-typedef"
 #pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wsign-compare"
 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
 #pragma GCC diagnostic ignored "-Wsign-conversion"
 #include <boost/algorithm/string.hpp>
@@ -198,7 +199,7 @@ const mapnik::json::extract_bounding_box_grammar<base_iterator_type> geojson_dat
 template <typename Iterator>
 void geojson_datasource::initialise_index(Iterator start, Iterator end)
 {
-    mapnik::json::boxes boxes;
+    mapnik::json::boxes_type boxes;
     boost::spirit::standard::space_type space;
     Iterator itr = start;
     if (!boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_bbox_grammar)(boost::phoenix::ref(boxes)) , space))
@@ -281,11 +282,10 @@ void geojson_datasource::parse_geojson(Iterator start, Iterator end)
             {
                 extent_.expand_to_include(box);
             }
+            values.emplace_back(box, std::make_pair(geometry_index,0));
         }
-        values.emplace_back(box, std::make_pair(geometry_index,0));
         ++geometry_index;
     }
-
     // packing algorithm
     tree_ = std::make_unique<spatial_index_type>(values);
 
diff --git a/plugins/input/ogr/build.py b/plugins/input/ogr/build.py
index 908cf19..143289d 100644
--- a/plugins/input/ogr/build.py
+++ b/plugins/input/ogr/build.py
@@ -51,9 +51,9 @@ libraries = copy(plugin_env['LIBS'])
 plugin_env.Append(CXXFLAGS=cxxflags)
 
 if env['PLUGIN_LINKING'] == 'shared':
+    libraries.append('boost_system%s' % env['BOOST_APPEND'])
     libraries.insert(0,env['MAPNIK_NAME'])
     libraries.append(env['ICU_LIB_NAME'])
-    libraries.append('boost_system%s' % env['BOOST_APPEND'])
 
     TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                       SHLIBPREFIX='',
diff --git a/plugins/input/pgraster/build.py b/plugins/input/pgraster/build.py
index 9d26665..7b859ee 100644
--- a/plugins/input/pgraster/build.py
+++ b/plugins/input/pgraster/build.py
@@ -51,13 +51,10 @@ else:
 # Link Library to Dependencies
 libraries = copy(plugin_env['LIBS'])
 
-if env['THREADING'] == 'multi':
-    libraries.append('boost_thread%s' % env['BOOST_APPEND'])
-
 if env['PLUGIN_LINKING'] == 'shared':
+    libraries.append('boost_system%s' % env['BOOST_APPEND'])
     libraries.insert(0,env['MAPNIK_NAME'])
     libraries.append(env['ICU_LIB_NAME'])
-    libraries.append('boost_system%s' % env['BOOST_APPEND'])
 
     TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                       SHLIBPREFIX='',
diff --git a/plugins/input/postgis/build.py b/plugins/input/postgis/build.py
index 9b8ecd9..157952f 100644
--- a/plugins/input/postgis/build.py
+++ b/plugins/input/postgis/build.py
@@ -51,10 +51,10 @@ else:
 libraries = copy(plugin_env['LIBS'])
 
 if env['PLUGIN_LINKING'] == 'shared':
-    libraries.insert(0,env['MAPNIK_NAME'])
-    libraries.append(env['ICU_LIB_NAME'])
     libraries.append('boost_system%s' % env['BOOST_APPEND'])
     libraries.append('boost_regex%s' % env['BOOST_APPEND'])
+    libraries.insert(0,env['MAPNIK_NAME'])
+    libraries.append(env['ICU_LIB_NAME'])
 
     TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                       SHLIBPREFIX='',
diff --git a/plugins/input/postgis/postgis_datasource.cpp b/plugins/input/postgis/postgis_datasource.cpp
index 9477c04..d1bba46 100644
--- a/plugins/input/postgis/postgis_datasource.cpp
+++ b/plugins/input/postgis/postgis_datasource.cpp
@@ -94,7 +94,8 @@ postgis_datasource::postgis_datasource(parameters const& params)
       pattern_(boost::regex("(@\\w+)",boost::regex::normal | boost::regbase::icase)),
       // params below are for testing purposes only and may be removed at any time
       intersect_min_scale_(*params.get<mapnik::value_integer>("intersect_min_scale", 0)),
-      intersect_max_scale_(*params.get<mapnik::value_integer>("intersect_max_scale", 0))
+      intersect_max_scale_(*params.get<mapnik::value_integer>("intersect_max_scale", 0)),
+      key_field_as_attribute_(*params.get<mapnik::value_integer>("key_field_as_attribute", true))
 {
 #ifdef MAPNIK_STATS
     mapnik::progress_timer __stats__(std::clog, "postgis_datasource::init");
@@ -372,7 +373,10 @@ postgis_datasource::postgis_datasource(parameters const& params)
                     if (type_oid == 20 || type_oid == 21 || type_oid == 23)
                     {
                         found_key_field = true;
-                        desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::Integer));
+                        if (key_field_as_attribute_)
+                        {
+                            desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::Integer));
+                        }
                     }
                     else
                     {
@@ -819,7 +823,10 @@ featureset_ptr postgis_datasource::features_with_context(query const& q,processo
         if (! key_field_.empty())
         {
             mapnik::sql_utils::quote_attr(s, key_field_);
-            ctx->push(key_field_);
+            if (key_field_as_attribute_)
+            {
+                ctx->push(key_field_);
+            }
 
             for (; pos != end; ++pos)
             {
@@ -849,7 +856,7 @@ featureset_ptr postgis_datasource::features_with_context(query const& q,processo
         }
 
         std::shared_ptr<IResultSet> rs = get_resultset(conn, s.str(), pool, proc_ctx);
-        return std::make_shared<postgis_featureset>(rs, ctx, desc_.get_encoding(), !key_field_.empty());
+        return std::make_shared<postgis_featureset>(rs, ctx, desc_.get_encoding(), !key_field_.empty(), key_field_as_attribute_);
 
     }
 
@@ -902,7 +909,10 @@ featureset_ptr postgis_datasource::features_at_point(coord2d const& pt, double t
             if (! key_field_.empty())
             {
                 mapnik::sql_utils::quote_attr(s, key_field_);
-                ctx->push(key_field_);
+                if (key_field_as_attribute_)
+                {
+                    ctx->push(key_field_);
+                }
                 for (; itr != end; ++itr)
                 {
                     if (itr->get_name() != key_field_)
@@ -932,7 +942,7 @@ featureset_ptr postgis_datasource::features_at_point(coord2d const& pt, double t
             }
 
             std::shared_ptr<IResultSet> rs = get_resultset(conn, s.str(), pool);
-            return std::make_shared<postgis_featureset>(rs, ctx, desc_.get_encoding(), !key_field_.empty());
+            return std::make_shared<postgis_featureset>(rs, ctx, desc_.get_encoding(), !key_field_.empty(), key_field_as_attribute_);
         }
     }
 
diff --git a/plugins/input/postgis/postgis_datasource.hpp b/plugins/input/postgis/postgis_datasource.hpp
index 84bcfe5..2f5d24f 100644
--- a/plugins/input/postgis/postgis_datasource.hpp
+++ b/plugins/input/postgis/postgis_datasource.hpp
@@ -122,6 +122,7 @@ private:
     boost::regex pattern_;
     int intersect_min_scale_;
     int intersect_max_scale_;
+    bool key_field_as_attribute_;
 };
 
 #endif // POSTGIS_DATASOURCE_HPP
diff --git a/plugins/input/postgis/postgis_featureset.cpp b/plugins/input/postgis/postgis_featureset.cpp
index 9dda45d..9e0db0e 100644
--- a/plugins/input/postgis/postgis_featureset.cpp
+++ b/plugins/input/postgis/postgis_featureset.cpp
@@ -48,13 +48,15 @@ using mapnik::context_ptr;
 postgis_featureset::postgis_featureset(std::shared_ptr<IResultSet> const& rs,
                                        context_ptr const& ctx,
                                        std::string const& encoding,
-                                       bool key_field)
+                                       bool key_field,
+                                       bool key_field_as_attribute)
     : rs_(rs),
       ctx_(ctx),
       tr_(new transcoder(encoding)),
       totalGeomSize_(0),
       feature_id_(1),
-      key_field_(key_field)
+      key_field_(key_field),
+      key_field_as_attribute_(key_field_as_attribute)
 {
 }
 
@@ -97,10 +99,10 @@ feature_ptr postgis_featureset::next()
             }
 
             feature = feature_factory::create(ctx_, val);
-            // TODO - extend feature class to know
-            // that its id is also an attribute to avoid
-            // this duplication
-            feature->put<mapnik::value_integer>(name,val);
+            if (key_field_as_attribute_)
+            {
+                feature->put<mapnik::value_integer>(name,val);
+            }
             ++pos;
         }
         else
diff --git a/plugins/input/postgis/postgis_featureset.hpp b/plugins/input/postgis/postgis_featureset.hpp
index 1dc2b3f..4984ec9 100644
--- a/plugins/input/postgis/postgis_featureset.hpp
+++ b/plugins/input/postgis/postgis_featureset.hpp
@@ -43,7 +43,8 @@ public:
     postgis_featureset(std::shared_ptr<IResultSet> const& rs,
                        context_ptr const& ctx,
                        std::string const& encoding,
-                       bool key_field = false);
+                       bool key_field,
+                       bool key_field_as_attribute);
     feature_ptr next();
     ~postgis_featureset();
 
@@ -54,6 +55,7 @@ private:
     unsigned totalGeomSize_;
     mapnik::value_integer feature_id_;
     bool key_field_;
+    bool key_field_as_attribute_;
 };
 
 #endif // POSTGIS_FEATURESET_HPP
diff --git a/plugins/input/raster/build.py b/plugins/input/raster/build.py
index 324a2a9..2ca3c2e 100644
--- a/plugins/input/raster/build.py
+++ b/plugins/input/raster/build.py
@@ -36,11 +36,11 @@ plugin_sources = Split(
 
 # Link Library to Dependencies
 libraries = []
-libraries.append(env['ICU_LIB_NAME'])
-libraries.append('boost_system%s' % env['BOOST_APPEND'])
 
 if env['PLUGIN_LINKING'] == 'shared':
-    libraries.append(env['MAPNIK_NAME'])
+    libraries.append('boost_system%s' % env['BOOST_APPEND'])
+    libraries.insert(0,env['MAPNIK_NAME'])
+    libraries.append(env['ICU_LIB_NAME'])
 
     TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                       SHLIBPREFIX='',
diff --git a/plugins/input/raster/raster_datasource.cpp b/plugins/input/raster/raster_datasource.cpp
index 96f5938..4d8b5f9 100644
--- a/plugins/input/raster/raster_datasource.cpp
+++ b/plugins/input/raster/raster_datasource.cpp
@@ -87,6 +87,7 @@ raster_datasource::raster_datasource(parameters const& params)
     else //bounding box from image_reader
     {
         std::unique_ptr<image_reader> reader(mapnik::get_image_reader(*file));
+        if (!reader) throw datasource_exception("Raster Plugin: failed to create reader for " + *file);
         auto bbox = reader->bounding_box();
         if (bbox)
         {
diff --git a/plugins/input/shape/build.py b/plugins/input/shape/build.py
index 097d602..3f397d0 100644
--- a/plugins/input/shape/build.py
+++ b/plugins/input/shape/build.py
@@ -39,8 +39,6 @@ plugin_sources = Split(
 
 # Link Library to Dependencies
 libraries = []
-libraries.append(env['ICU_LIB_NAME'])
-libraries.append('boost_system%s' % env['BOOST_APPEND'])
 
 cppdefines = []
 cxxflags = []
@@ -49,7 +47,9 @@ plugin_env.Append(CXXFLAGS=cxxflags)
 plugin_env.Append(CPPDEFINES=cppdefines)
 
 if env['PLUGIN_LINKING'] == 'shared':
-    libraries.append(env['MAPNIK_NAME'])
+    libraries.append('boost_system%s' % env['BOOST_APPEND'])
+    libraries.insert(0,env['MAPNIK_NAME'])
+    libraries.append(env['ICU_LIB_NAME'])
 
     TARGET = plugin_env.SharedLibrary('../shape',
                                       SHLIBSUFFIX='.input',
diff --git a/plugins/input/shape/shape_index_featureset.cpp b/plugins/input/shape/shape_index_featureset.cpp
index fdd7513..1fa6f0b 100644
--- a/plugins/input/shape/shape_index_featureset.cpp
+++ b/plugins/input/shape/shape_index_featureset.cpp
@@ -36,10 +36,9 @@
 #include <boost/interprocess/streams/bufferstream.hpp>
 #endif
 #pragma GCC diagnostic pop
-
 #include "shape_index_featureset.hpp"
 #include "shape_utils.hpp"
-#include "shp_index.hpp"
+#include <mapnik/util/spatial_index.hpp>
 
 using mapnik::feature_factory;
 
@@ -52,11 +51,11 @@ shape_index_featureset<filterT>::shape_index_featureset(filterT const& filter,
                                                         int row_limit)
     : filter_(filter),
       ctx_(std::make_shared<mapnik::context_type>()),
-    shape_ptr_(std::move(shape_ptr)),
-    tr_(new mapnik::transcoder(encoding)),
-    row_limit_(row_limit),
-    count_(0),
-    feature_bbox_()
+      shape_ptr_(std::move(shape_ptr)),
+      tr_(new mapnik::transcoder(encoding)),
+      row_limit_(row_limit),
+      count_(0),
+      feature_bbox_()
 {
     shape_ptr_->shp().skip(100);
     setup_attributes(ctx_, attribute_names, shape_name, *shape_ptr_,attr_ids_);
@@ -65,17 +64,13 @@ shape_index_featureset<filterT>::shape_index_featureset(filterT const& filter,
     if (index)
     {
 #ifdef SHAPE_MEMORY_MAPPED_FILE
-        //shp_index<filterT,stream<mapped_file_source> >::query(filter, index->file(), offsets_);
-        shp_index<filterT,boost::interprocess::ibufferstream>::query(filter, index->file(), offsets_);
+        mapnik::util::spatial_index<int, filterT,boost::interprocess::ibufferstream>::query(filter, index->file(), offsets_);
 #else
-        shp_index<filterT,std::ifstream>::query(filter, index->file(), offsets_);
+        mapnik::util::spatial_index<int, filterT, std::ifstream>::query(filter, index->file(), offsets_);
 #endif
     }
-
     std::sort(offsets_.begin(), offsets_.end());
-
     MAPNIK_LOG_DEBUG(shape) << "shape_index_featureset: Query size=" << offsets_.size();
-
     itr_ = offsets_.begin();
 }
 
diff --git a/plugins/input/shape/shape_index_featureset.hpp b/plugins/input/shape/shape_index_featureset.hpp
index cf52aa0..80c8fa6 100644
--- a/plugins/input/shape/shape_index_featureset.hpp
+++ b/plugins/input/shape/shape_index_featureset.hpp
@@ -63,8 +63,8 @@ private:
     context_ptr ctx_;
     std::unique_ptr<shape_io> shape_ptr_;
     const std::unique_ptr<mapnik::transcoder> tr_;
-    std::vector<std::streampos> offsets_;
-    std::vector<std::streampos>::iterator itr_;
+    std::vector<int> offsets_;
+    std::vector<int>::iterator itr_;
     std::vector<int> attr_ids_;
     mapnik::value_integer row_limit_;
     mutable int count_;
diff --git a/plugins/input/shape/shp_index.hpp b/plugins/input/shape/shp_index.hpp
deleted file mode 100644
index 6c32305..0000000
--- a/plugins/input/shape/shp_index.hpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*****************************************************************************
- *
- * This file is part of Mapnik (c++ mapping toolkit)
- *
- * Copyright (C) 2015 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 SHP_INDEX_HPP
-#define SHP_INDEX_HPP
-
-// stl
-#include <fstream>
-#include <vector>
-
-// mapnik
-#include <mapnik/box2d.hpp>
-#include <mapnik/query.hpp>
-
-using mapnik::box2d;
-using mapnik::query;
-
-template <typename filterT, typename IStream = std::ifstream>
-class shp_index
-{
-public:
-    static void query(filterT const& filter, IStream& file,std::vector<std::streampos>& pos);
-private:
-    shp_index();
-    ~shp_index();
-    shp_index(const shp_index&);
-    shp_index& operator=(const shp_index&);
-    static int read_ndr_integer(IStream& in);
-    static void read_envelope(IStream& in, box2d<double>& envelope);
-    static void query_node(const filterT& filter, IStream& in, std::vector<std::streampos>& pos);
-};
-
-template <typename filterT, typename IStream>
-void shp_index<filterT, IStream>::query(const filterT& filter, IStream& file, std::vector<std::streampos>& pos)
-{
-    file.seekg(16, std::ios::beg);
-    query_node(filter, file, pos);
-}
-
-template <typename filterT, typename IStream>
-void shp_index<filterT, IStream>::query_node(const filterT& filter, IStream& file, std::vector<std::streampos>& ids)
-{
-    int offset = read_ndr_integer(file);
-
-    box2d<double> node_ext;
-    read_envelope(file, node_ext);
-
-    int num_shapes = read_ndr_integer(file);
-
-    if (! filter.pass(node_ext))
-    {
-        file.seekg(offset + num_shapes * 4 + 4, std::ios::cur);
-        return;
-    }
-
-    for (int i = 0; i < num_shapes; ++i)
-    {
-        int id = read_ndr_integer(file);
-        ids.push_back(id);
-    }
-
-    int children = read_ndr_integer(file);
-
-    for (int j = 0; j < children; ++j)
-    {
-        query_node(filter, file, ids);
-    }
-}
-
-template <typename filterT, typename IStream>
-int shp_index<filterT, IStream>::read_ndr_integer(IStream& file)
-{
-    char b[4];
-    file.read(b, 4);
-    return (b[0] & 0xff) | (b[1] & 0xff) << 8 | (b[2] & 0xff) << 16 | (b[3] & 0xff) << 24;
-}
-
-template <typename filterT, typename IStream>
-void shp_index<filterT, IStream>::read_envelope(IStream& file, box2d<double>& envelope)
-{
-    file.read(reinterpret_cast<char*>(&envelope), sizeof(envelope));
-}
-
-#endif // SHP_INDEX_HPP
diff --git a/plugins/input/sqlite/build.py b/plugins/input/sqlite/build.py
index b3a4a3e..53dfd8b 100644
--- a/plugins/input/sqlite/build.py
+++ b/plugins/input/sqlite/build.py
@@ -35,8 +35,6 @@ plugin_sources = Split(
 
 # Link Library to Dependencies
 libraries = [ 'sqlite3' ]
-libraries.append(env['ICU_LIB_NAME'])
-libraries.append('boost_system%s' % env['BOOST_APPEND'])
 
 linkflags = []
 if env['SQLITE_LINKFLAGS']:
@@ -44,7 +42,9 @@ if env['SQLITE_LINKFLAGS']:
     plugin_env.Append(LINKFLAGS=linkflags)
 
 if env['PLUGIN_LINKING'] == 'shared':
-    libraries.append(env['MAPNIK_NAME'])
+    libraries.append('boost_system%s' % env['BOOST_APPEND'])
+    libraries.insert(0,env['MAPNIK_NAME'])
+    libraries.append(env['ICU_LIB_NAME'])
 
     TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                        SHLIBPREFIX='',
diff --git a/plugins/input/topojson/build.py b/plugins/input/topojson/build.py
index 9f7b955..3a49320 100644
--- a/plugins/input/topojson/build.py
+++ b/plugins/input/topojson/build.py
@@ -46,12 +46,12 @@ else:
 
     # Link Library to Dependencies
     libraries = []
-    libraries.append(env['ICU_LIB_NAME'])
-    libraries.append('boost_system%s' % env['BOOST_APPEND'])
     libraries.append('mapnik-json')
 
     if env['PLUGIN_LINKING'] == 'shared':
-        libraries.append(env['MAPNIK_NAME'])
+        libraries.append('boost_system%s' % env['BOOST_APPEND'])
+        libraries.insert(0,env['MAPNIK_NAME'])
+        libraries.append(env['ICU_LIB_NAME'])
 
         TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
                                           SHLIBPREFIX='',
diff --git a/scripts/build-appveyor.bat b/scripts/build-appveyor.bat
new file mode 100644
index 0000000..3ed98d9
--- /dev/null
+++ b/scripts/build-appveyor.bat
@@ -0,0 +1,70 @@
+ at ECHO OFF
+SETLOCAL
+SET EL=0
+
+ECHO =========== %~f0 ===========
+
+ECHO NUMBER_OF_PROCESSORS^: %NUMBER_OF_PROCESSORS%
+ECHO RAM [MB]^:
+powershell "get-ciminstance -class 'cim_physicalmemory' | %% { $_.Capacity/1024/1024}"
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+
+::only build on AppVeyor, if explicitly stated
+ECHO APPVEYOR_REPO_COMMIT_MESSAGE^: %APPVEYOR_REPO_COMMIT_MESSAGE%
+::SET BUILD_ON_APPVEYOR=0
+::for /F "tokens=1 usebackq" %%i in (`powershell .\scripts\parse-commit-message.ps1 '[build appveyor]'`) DO SET BUILD_ON_APPVEYOR=%%i
+::IF %BUILD_ON_APPVEYOR% EQU 0 ECHO not building, commit with [build appveyor] && GOTO DONE
+
+ECHO configuration^: %configuration%
+ECHO platform^: %platform%
+ECHO msvs_toolset^: %msvs_toolset%
+SET BUILD_TYPE=%configuration%
+SET BUILDPLATFORM=%platform%
+SET TOOLS_VERSION=%msvs_toolset%.0
+IF DEFINED APPVEYOR (ECHO on AppVeyor) ELSE (ECHO NOT on AppVeyor)
+ECHO ========
+
+SET PATH=C:\Python27;%PATH%
+SET PATH=C:\Program Files\7-Zip;%PATH%
+:: *nix style find command:
+SET PATH=C:\Program Files (x86)\Git\bin;%PATH%
+
+::cloning mapnik-gyp
+if EXIST mapnik-gyp ECHO mapnik-gyp already cloned && GOTO MAPNIK_GYP_ALREADY_HERE
+CALL git clone https://github.com/mapnik/mapnik-gyp.git
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+:MAPNIK_GYP_ALREADY_HERE
+CD mapnik-gyp
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+git pull
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+
+
+SET DEPS_URL=https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/mapnik-win-sdk-binary-deps-%msvs_toolset%.0-%platform%.7z
+ECHO fetching binary deps^: %DEPS_URL%
+IF EXIST deps.7z (ECHO already downloaded) ELSE (powershell Invoke-WebRequest "${env:DEPS_URL}" -OutFile deps.7z)
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+ECHO extracting binary deps
+IF EXIST mapnik-sdk (ECHO already extracted) ELSE (7z -y x deps.7z | %windir%\system32\FIND "ing archive")
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+
+CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+
+SET AV_MAPNIK_GYP_STARTTIME=%TIME%
+ECHO calling build.bat of mapnik-gyp && CALL build.bat
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+ECHO %AV_MAPNIK_GYP_STARTTIME% started mapnik-gyp build.bat
+ECHO %TIME% finished mapnik-gyp build.bat
+
+GOTO DONE
+
+:ERROR
+ECHO =========== ERROR %~f0 ===========
+ECHO ERRORLEVEL^: %ERRORLEVEL%
+SET EL=%ERRORLEVEL%
+
+:DONE
+ECHO =========== DONE %~f0 ===========
+
+EXIT /b %EL%
diff --git a/scripts/build-local.bat b/scripts/build-local.bat
new file mode 100644
index 0000000..240e70f
--- /dev/null
+++ b/scripts/build-local.bat
@@ -0,0 +1,32 @@
+ at ECHO OFF
+SETLOCAL
+SET EL=0
+
+ECHO =========== %~f0 ===========
+
+SET APPVEYOR_REPO_COMMIT_MESSAGE=this is a [build appveyor] test
+SET APPVEYOR=true
+::comment this to get complete AppVeyor behaviour
+SET LOCAL_BUILD_DONT_SKIP_TESTS=true
+
+SET MAPNIK_GIT=3.0.5
+SET BOOST_VERSION=58
+SET FASTBUILD=1
+SET configuration=Release
+SET msvs_toolset=14
+SET platform=x64
+SET APPVEYOR_BUILD_FOLDER=%CD%
+CALL scripts\build-appveyor.bat
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+
+GOTO DONE
+
+:ERROR
+ECHO =========== ERROR %~f0 ===========
+ECHO ERRORLEVEL^: %ERRORLEVEL%
+SET EL=%ERRORLEVEL%
+
+:DONE
+ECHO =========== DONE %~f0 ===========
+
+EXIT /b %EL%
diff --git a/scripts/parse-commit-message.ps1 b/scripts/parse-commit-message.ps1
new file mode 100644
index 0000000..a22c10d
--- /dev/null
+++ b/scripts/parse-commit-message.ps1
@@ -0,0 +1,5 @@
+if($env:APPVEYOR_REPO_COMMIT_MESSAGE.ToLower().Contains($args[0].ToLower())) {
+    Write-Host '1';
+} else {
+    Write-Host '0';
+}
diff --git a/src/cairo/process_debug_symbolizer.cpp b/src/cairo/process_debug_symbolizer.cpp
index c60794b..8c78a82 100644
--- a/src/cairo/process_debug_symbolizer.cpp
+++ b/src/cairo/process_debug_symbolizer.cpp
@@ -92,7 +92,6 @@ void cairo_renderer<T>::process(debug_symbolizer const& sym,
                                   mapnik::feature_impl & feature,
                                   proj_transform const& prj_trans)
 {
-    using detector_type = label_collision_detector4;
     cairo_save_restore guard(context_);
 
     debug_symbolizer_mode_enum mode = get<debug_symbolizer_mode_enum>(sym, keys::mode, feature, common_.vars_, DEBUG_SYM_MODE_COLLISION);
diff --git a/src/debug.cpp b/src/debug.cpp
index f41f84c..169272f 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -43,22 +43,22 @@
 
 namespace mapnik {
 
-// mutexes
-
 #ifdef MAPNIK_THREADSAFE
+
 std::mutex logger::severity_mutex_;
 std::mutex logger::format_mutex_;
-#endif
-
-
-// first time checks
 
 std::atomic<bool> logger::severity_env_check_ {true};
 std::atomic<bool> logger::format_env_check_ {true};
 
-// severity
-
 std::atomic<logger::severity_type> logger::severity_level_ {
+#else
+
+bool logger::severity_env_check_ {true};
+bool logger::format_env_check_ {true};
+
+logger::severity_type logger::severity_level_ {
+#endif
 #if MAPNIK_DEFAULT_LOG_SEVERITY == 0
     logger::debug
 #elif MAPNIK_DEFAULT_LOG_SEVERITY == 1
diff --git a/src/image_util.cpp b/src/image_util.cpp
index 7f86fdb..79d7e0a 100644
--- a/src/image_util.cpp
+++ b/src/image_util.cpp
@@ -1582,7 +1582,6 @@ struct visitor_get_pixel
     template <typename T2>
     T1 operator() (T2 const& data) const
     {
-        using pixel_type = T1;
         if (check_bounds(data, x_, y_))
         {
             return safe_cast<T1>(data(x_, y_));
diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp
index ec1f4d8..af02a71 100644
--- a/src/svg/svg_parser.cpp
+++ b/src/svg/svg_parser.cpp
@@ -157,6 +157,28 @@ double parse_double_optional_percent(T & error_messages, const char* str, bool &
     return val;
 }
 
+template <typename T>
+bool parse_double_list(T & error_messages, const char* str, double* list)
+{
+    using namespace boost::spirit::qi;
+    using boost::phoenix::ref;
+    qi::_1_type _1;
+    qi::double_type double_;
+    qi::lit_type lit;
+    using skip_type = boost::spirit::ascii::space_type;
+
+    if (!phrase_parse(str, str + std::strlen(str),
+                      double_[ref(list[0])=_1] >> -lit(',') >>
+                      double_[ref(list[1])=_1] >> -lit(',') >>
+                      double_[ref(list[2])=_1] >> -lit(',') >>
+                      double_[ref(list[3])=_1], skip_type()))
+    {
+        error_messages.emplace_back("failed to parse list of doubles from " + std::string(str));
+        return false;
+    }
+    return true;
+}
+
 bool parse_style (char const* str, pairs_type & v)
 {
     using namespace boost::spirit::qi;
@@ -465,16 +487,44 @@ void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
 {
     double width = 0;
     double height = 0;
+    double aspect_ratio = 1;
+    double viewbox[4] = {0,0,0,0};
+    bool has_viewbox = false;
+    bool has_percent_height = true;
+    bool has_percent_width = true;
+
     auto const* width_attr = node->first_attribute("width");
     if (width_attr)
     {
-        width = parse_double(parser.error_messages_, width_attr->value());
+        width = parse_double_optional_percent(parser.error_messages_, width_attr->value(), has_percent_width);
     }
     auto const* height_attr = node->first_attribute("height");
     if (height_attr)
     {
-        height = parse_double(parser.error_messages_, height_attr->value());
+        height = parse_double_optional_percent(parser.error_messages_, height_attr->value(), has_percent_height);
     }
+    auto const* viewbox_attr = node->first_attribute("viewBox");
+    if (viewbox_attr)
+    {
+        has_viewbox = parse_double_list(parser.error_messages_, viewbox_attr->value(), viewbox);
+    }
+
+    if (has_percent_width && !has_percent_height && has_viewbox)
+    {
+        aspect_ratio = viewbox[2] / viewbox[3];
+        width = aspect_ratio * height;
+    }
+    else if (!has_percent_width && has_percent_height && has_viewbox)
+    {
+        aspect_ratio = viewbox[2] / viewbox[3];
+        height = height / aspect_ratio;
+    }
+    else if (has_percent_width && has_percent_height && has_viewbox)
+    {
+        width = viewbox[2];
+        height = viewbox[3];
+    }
+
     parser.path_.set_dimensions(width, height);
 }
 
diff --git a/src/text/placements/simple.cpp b/src/text/placements/simple.cpp
index 59e4407..493da3f 100644
--- a/src/text/placements/simple.cpp
+++ b/src/text/placements/simple.cpp
@@ -36,6 +36,7 @@
 #pragma GCC diagnostic ignored "-Wunused-local-typedef"
 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
 #pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wsign-compare"
 #pragma GCC diagnostic ignored "-Wconversion"
 #include <boost/spirit/include/qi.hpp>
 #include <boost/spirit/include/phoenix_core.hpp>
diff --git a/test/standalone/csv_test.cpp b/test/standalone/csv_test.cpp
index 6535e82..7d08bb5 100644
--- a/test/standalone/csv_test.cpp
+++ b/test/standalone/csv_test.cpp
@@ -191,6 +191,7 @@ TEST_CASE("csv") {
 
                 for (auto const &path : broken)
                 {
+                    INFO(path);
                     REQUIRE_THROWS(get_csv_ds(path.native()));
                 }
             }
@@ -555,20 +556,6 @@ TEST_CASE("csv") {
             }
         } // END SECTION
 
-        SECTION("blank undelimited rows are still parsed") {
-            using ustring = mapnik::value_unicode_string;
-
-            // TODO: does this mean this CSV file should be in the warnings
-            // subdirectory, since it doesn't work in strict mode?
-            auto ds = get_csv_ds("test/data/csv/more_headers_than_column_values.csv", false);
-            auto fields = ds->get_descriptor().get_descriptors();
-            require_field_names(fields, {"x", "y", "one", "two", "three"});
-            require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::String});
-
-            require_attributes(all_features(ds)->next(), {
-                    attr{"x", 0}, attr{"y", 0}, attr{"one", ustring("")}, attr{"two", ustring("")}, attr{"three", ustring("")} });
-        } // END SECTION
-
         SECTION("fewer headers than rows throws") {
             REQUIRE_THROWS(get_csv_ds("test/data/csv/more_column_values_than_headers.csv"));
         } // END SECTION
@@ -670,6 +657,7 @@ TEST_CASE("csv") {
             mapnik::parameters params;
             params["type"] = std::string("csv");
             params["inline"] = csv_string;
+            params["quote"] = "'";
             auto ds = mapnik::datasource_cache::instance().create(params);
             REQUIRE(bool(ds));
 
diff --git a/test/standalone/datasource_registration_test.cpp b/test/standalone/datasource_registration_test.cpp
index 2edbcd7..9d6266c 100644
--- a/test/standalone/datasource_registration_test.cpp
+++ b/test/standalone/datasource_registration_test.cpp
@@ -3,6 +3,7 @@
 
 #include <mapnik/datasource_cache.hpp>
 #include <mapnik/debug.hpp>
+#include <mapnik/util/fs.hpp>
 
 #include <iostream>
 #include <vector>
@@ -22,19 +23,24 @@ SECTION("registration") {
         success = cache.register_datasources("test/data/vrt");
         CHECK(success == false);
 
-        // registering a directory for the first time should return true
-        success = cache.register_datasources("plugins/input");
-        REQUIRE(success == true);
-
-        // registering the same directory again should now return false
-        success = cache.register_datasources("plugins/input");
-        CHECK(success == false);
-
-        // registering the same directory again, but recursively should
-        // still return false - even though there are subdirectories, they
-        // do not contain any more plugins.
-        success = cache.register_datasources("plugins/input", true);
-        CHECK(success == false);
+        // use existence of shape.input as proxy for whether any datasource plugins are available
+        std::string shape_plugin("./plugins/input/shape.input");
+        if (mapnik::util::exists(shape_plugin))
+        {
+            // registering a directory for the first time should return true
+            success = cache.register_datasources("plugins/input");
+            REQUIRE(success == true);
+
+            // registering the same directory again should now return false
+            success = cache.register_datasources("plugins/input");
+            CHECK(success == false);
+
+            // registering the same directory again, but recursively should
+            // still return false - even though there are subdirectories, they
+            // do not contain any more plugins.
+            success = cache.register_datasources("plugins/input", true);
+            CHECK(success == false);
+        }
     }
     catch (std::exception const & ex)
     {
diff --git a/test/unit/color/css_color.cpp b/test/unit/color/css_color.cpp
index 38e1518..7ff6e3f 100644
--- a/test/unit/color/css_color.cpp
+++ b/test/unit/color/css_color.cpp
@@ -1,5 +1,6 @@
 #include "catch.hpp"
 #include <mapnik/css_color_grammar.hpp>
+#include <mapnik/css_color_grammar_impl.hpp>
 #include <mapnik/safe_cast.hpp>
 
 TEST_CASE("css color") {
@@ -35,4 +36,132 @@ TEST_CASE("css color") {
         CHECK( c.green() == 0 );
         CHECK( c.blue() == 0 );
     }
-}
\ No newline at end of file
+
+    SECTION("hex colors")
+    {
+        mapnik::css_color_grammar<std::string::const_iterator> color_grammar;
+        boost::spirit::qi::ascii::space_type space;
+
+        {
+            std::string s("#abcdef");
+            mapnik::color c;
+            CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space, c) );
+            CHECK( c.alpha() == 0xff );
+            CHECK( c.red() == 0xab );
+            CHECK( c.green() == 0xcd );
+            CHECK( c.blue() == 0xef );
+        }
+
+        {
+            std::string s("#abcdef12");
+            mapnik::color c;
+            CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space, c) );
+            CHECK( c.alpha() == 0x12 );
+            CHECK( c.red() == 0xab );
+            CHECK( c.green() == 0xcd );
+            CHECK( c.blue() == 0xef );
+        }
+
+        {
+            std::string s("  #abcdef");
+            mapnik::color c;
+            CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space, c) );
+            CHECK( c.alpha() == 0xff );
+            CHECK( c.red() == 0xab );
+            CHECK( c.green() == 0xcd );
+            CHECK( c.blue() == 0xef );
+        }
+
+        {
+            std::string s("   #abcdef12");
+            mapnik::color c;
+            CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space, c) );
+            CHECK( c.alpha() == 0x12 );
+            CHECK( c.red() == 0xab );
+            CHECK( c.green() == 0xcd );
+            CHECK( c.blue() == 0xef );
+        }
+
+        {
+            std::string s("# abcdef");
+            CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
+        }
+
+        {
+            std::string s("# abcdef12");
+            CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
+        }
+
+        {
+            std::string s("#ab cdef");
+            CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
+        }
+
+        {
+            std::string s("#ab cdef12");
+            CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
+        }
+
+        // hex_color_small
+
+        {
+            std::string s("#abc");
+            mapnik::color c;
+            CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space, c) );
+            CHECK( c.alpha() == 0xff );
+            CHECK( c.red() == 0xaa );
+            CHECK( c.green() == 0xbb );
+            CHECK( c.blue() == 0xcc );
+        }
+
+        {
+            std::string s("#abcd");
+            mapnik::color c;
+            CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space, c) );
+            CHECK( c.alpha() == 0xdd );
+            CHECK( c.red() == 0xaa );
+            CHECK( c.green() == 0xbb );
+            CHECK( c.blue() == 0xcc );
+        }
+
+        {
+            std::string s("   #abc");
+            mapnik::color c;
+            CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space, c) );
+            CHECK( c.alpha() == 0xff );
+            CHECK( c.red() == 0xaa );
+            CHECK( c.green() == 0xbb );
+            CHECK( c.blue() == 0xcc );
+        }
+
+        {
+            std::string s("   #abcd");
+            mapnik::color c;
+            CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space, c) );
+            CHECK( c.alpha() == 0xdd );
+            CHECK( c.red() == 0xaa );
+            CHECK( c.green() == 0xbb );
+            CHECK( c.blue() == 0xcc );
+        }
+
+        {
+            std::string s("# abc");
+            CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
+        }
+
+        {
+            std::string s("# abcd");
+            CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
+        }
+
+        {
+            std::string s("#a bc");
+            CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
+        }
+
+        {
+            std::string s("#a bcd");
+            CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
+        }
+    }
+}
diff --git a/test/unit/core/comparison_test.cpp b/test/unit/core/comparison_test.cpp
new file mode 100644
index 0000000..2cfbc08
--- /dev/null
+++ b/test/unit/core/comparison_test.cpp
@@ -0,0 +1,49 @@
+#include "catch.hpp"
+
+#include <mapnik/value_types.hpp>
+#include <mapnik/value.hpp>
+#include <mapnik/unicode.hpp>
+
+TEST_CASE("comparison")
+{
+    SECTION("operator==()")
+    {
+        mapnik::value v0 = 1; // mapnik::value_integer
+        mapnik::value v1 = 1.001; // mapnik::value_double
+        mapnik::value v2 = true; // mapnik::value_boolean
+
+        REQUIRE(!(v0 == v1));
+        REQUIRE(!(v1 == v0));
+
+        REQUIRE(!(v1 == v2));
+        REQUIRE(!(v2 == v1));
+
+        REQUIRE(v2 == v0);
+        REQUIRE(v0 == v2);
+    }
+
+    SECTION("operator!=()")
+    {
+        mapnik::value v0 = 1; // mapnik::value_integer
+        mapnik::value v1 = 1.001; // mapnik::value_double
+        mapnik::value v2 = true; // mapnik::value_boolean
+        mapnik::value v3 = mapnik::value_null(); //
+
+        REQUIRE(v0 != v1);
+        REQUIRE(v1 != v0);
+
+        REQUIRE(v1 != v2);
+        REQUIRE(v2 != v1);
+
+        REQUIRE(!(v2 != v0));
+        REQUIRE(!(v0 != v2));
+
+        REQUIRE(v3 != v0);
+        REQUIRE(v3 != v1);
+        REQUIRE(v3 != v2);
+        REQUIRE(v0 != v3);
+        REQUIRE(v1 != v3);
+        REQUIRE(v2 != v3);
+
+    }
+}
diff --git a/test/unit/datasource/spatial_index.cpp b/test/unit/datasource/spatial_index.cpp
new file mode 100644
index 0000000..62fde03
--- /dev/null
+++ b/test/unit/datasource/spatial_index.cpp
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2015 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
+ *
+ *****************************************************************************/
+
+#include <iostream>
+#include <fstream>
+
+#include "catch.hpp"
+
+#include <mapnik/quad_tree.hpp>
+#include <mapnik/util/spatial_index.hpp>
+
+TEST_CASE("spatial_index")
+{
+    SECTION("mapnik::quad_tree<T>")
+    {
+        // value_type must have standard layout (http://en.cppreference.com/w/cpp/types/is_standard_layout)
+        using value_type = std::int32_t;
+        using mapnik::filter_in_box;
+        mapnik::box2d<double> extent(0,0,100,100);
+        mapnik::quad_tree<value_type> tree(extent);
+        REQUIRE(tree.extent() == extent);
+        // insert some items
+        tree.insert(1, mapnik::box2d<double>(10,10,20,20));
+        tree.insert(2, mapnik::box2d<double>(30,30,40,40));
+        tree.insert(3, mapnik::box2d<double>(30,10,40,20));
+        tree.insert(4, mapnik::box2d<double>(1,1,2,2));
+        tree.trim();
+
+        REQUIRE(tree.count() == 5);
+        REQUIRE(tree.count_items() == 4);
+
+        // serialise
+        std::ostringstream out(std::ios::binary);
+        tree.write(out);
+        out.flush();
+
+        REQUIRE(out.str().length() == 252);
+        REQUIRE(out.str().at(0) == 'm');
+
+        // read bounding box
+        std::istringstream in(out.str(), std::ios::binary);
+        auto box = mapnik::util::spatial_index<value_type, filter_in_box, std::istringstream>::bounding_box(in);
+        REQUIRE(box == tree.extent());
+        // bounding box query
+        std::vector<value_type> results;
+        filter_in_box filter(box);
+        mapnik::util::spatial_index<value_type, filter_in_box, std::istringstream>::query(filter, in, results);
+
+        REQUIRE(results[0] == 1);
+        REQUIRE(results[1] == 4);
+        REQUIRE(results[2] == 3);
+        REQUIRE(results[3] == 2);
+        REQUIRE(results.size() == 4);
+
+        // query first N elements interface
+        results.clear();
+        in.seekg(0, std::ios::beg);
+        mapnik::util::spatial_index<value_type, filter_in_box, std::istringstream>::query_first_n(filter, in, results, 2);
+        REQUIRE(results.size() == 2);
+        REQUIRE(results[0] == 1);
+        REQUIRE(results[1] == 4);
+        results.clear();
+        in.seekg(0, std::ios::beg);
+        mapnik::util::spatial_index<value_type, filter_in_box, std::istringstream>::query_first_n(filter, in, results, 5);
+        REQUIRE(results[0] == 1);
+        REQUIRE(results[1] == 4);
+        REQUIRE(results[2] == 3);
+        REQUIRE(results[3] == 2);
+        REQUIRE(results.size() == 4);
+    }
+}
diff --git a/test/unit/geometry/geometry_is_simple.cpp b/test/unit/geometry/geometry_is_simple.cpp
new file mode 100644
index 0000000..a09332f
--- /dev/null
+++ b/test/unit/geometry/geometry_is_simple.cpp
@@ -0,0 +1,320 @@
+#include "catch.hpp"
+
+#include <boost/version.hpp>
+#include <mapnik/geometry.hpp>
+#include <mapnik/geometry_adapters.hpp>
+#include <mapnik/geometry_is_simple.hpp>
+
+TEST_CASE("geometry is_simple") {
+
+// only Boost >= 1.58 has the required is_valid function version
+#if BOOST_VERSION >= 105800
+
+SECTION("point") {
+    mapnik::geometry::geometry_empty empty;
+    CHECK( mapnik::geometry::is_simple(empty) );
+}
+
+SECTION("point") {
+    mapnik::geometry::point<double> pt(0,0);
+    CHECK( mapnik::geometry::is_simple(pt) );
+}
+
+SECTION("point uninitialized") {
+    mapnik::geometry::point<double> pt2;
+    CHECK( mapnik::geometry::is_simple(pt2) );
+}
+
+SECTION("point -- geometry object") {
+    mapnik::geometry::point<double> pt(0,0);
+    mapnik::geometry::geometry<double> geom(pt);
+    CHECK( mapnik::geometry::is_simple(geom) );
+}
+
+// This is funky that boost geometry is_simple does not check for NAN when dealing with a point
+// this test is here in case the logic ever changes
+// Bug report on this: https://svn.boost.org/trac/boost/ticket/11711
+SECTION("point NaN") {
+    mapnik::geometry::point<double> pt(std::numeric_limits<double>::quiet_NaN(),std::numeric_limits<double>::quiet_NaN());
+    CHECK( std::isnan(pt.x) );
+    CHECK( std::isnan(pt.y) );
+    CHECK( mapnik::geometry::is_simple(pt) );
+}
+
+// This is funky that boost geometry is_simple does not check for infinity when dealing with a point
+// this test is here in case the logic ever changes
+// Bug report on this: https://svn.boost.org/trac/boost/ticket/11711
+SECTION("point Infinity") {
+    mapnik::geometry::point<double> pt(std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity());
+    CHECK( std::isinf(pt.x) );
+    CHECK( std::isinf(pt.y) );
+    CHECK( mapnik::geometry::is_simple(pt) );
+}
+
+SECTION("multi point") {
+    mapnik::geometry::multi_point<double> mpt;
+    mpt.add_coord(0,0);    
+    mpt.add_coord(1,1);
+    CHECK( mapnik::geometry::is_simple(mpt) );
+}
+
+SECTION("multi point empty") {
+    mapnik::geometry::multi_point<double> mpt;
+    CHECK( mapnik::geometry::is_simple(mpt) );
+}
+
+SECTION("line_string") {
+    mapnik::geometry::line_string<double> line;
+    line.add_coord(0,0);    
+    line.add_coord(1,1);
+    CHECK( mapnik::geometry::is_simple(line) );
+}
+
+// This fails while is_valid will not fail!
+SECTION("line_string repeated points") {
+    mapnik::geometry::line_string<double> line;
+    line.add_coord(0,0);    
+    line.add_coord(1,1);
+    line.add_coord(1,1);
+    line.add_coord(2,2);
+    CHECK( !mapnik::geometry::is_simple(line) );
+}
+
+SECTION("line_string empty") {
+    mapnik::geometry::line_string<double> line;
+    CHECK( mapnik::geometry::is_simple(line) );
+}
+
+SECTION("multi_line_string") {
+    mapnik::geometry::line_string<double> line1;
+    line1.add_coord(0,0);    
+    line1.add_coord(1,1);
+    mapnik::geometry::line_string<double> line2;
+    line2.add_coord(0,1);    
+    line2.add_coord(1,2);
+    mapnik::geometry::multi_line_string<double> lines;
+    lines.emplace_back(line1);
+    lines.emplace_back(line2);
+    CHECK( mapnik::geometry::is_simple(lines) );
+}
+
+SECTION("multi_line_string empty") {
+    mapnik::geometry::multi_line_string<double> lines;
+    CHECK( mapnik::geometry::is_simple(lines) );
+}
+
+SECTION("multi_line_string empty") {
+    mapnik::geometry::multi_line_string<double> lines;
+    mapnik::geometry::line_string<double> line;
+    lines.emplace_back(line);
+    CHECK( mapnik::geometry::is_simple(lines) );
+}
+
+SECTION("polygon") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( mapnik::geometry::is_simple(poly) );
+}
+
+SECTION("polygon invalid winding order") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(0,1);
+    ring.add_coord(1,1);
+    ring.add_coord(1,0);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( mapnik::geometry::is_simple(poly) );
+}
+
+// repeated points are not considered invalid in a polygon
+// but they are considered not simple
+SECTION("polygon 2 repeated points") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( !mapnik::geometry::is_simple(poly) );
+}
+// repeated points are not considered invalid in a polygon
+// but they are considered not simple
+SECTION("polygon 3 repeated points") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(1,1);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( !mapnik::geometry::is_simple(poly) );
+}
+
+SECTION("polygon that is empty") {
+    mapnik::geometry::polygon<double> poly;
+    CHECK( mapnik::geometry::is_simple(poly) );
+}
+
+SECTION("polygon that has empty exterior ring") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( mapnik::geometry::is_simple(poly) );
+}
+
+SECTION("polygon that has empty interior ring") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> ring2;
+    poly.add_hole(std::move(ring2));
+    CHECK( mapnik::geometry::is_simple(poly) );
+}
+
+// A polygon with a spike can still be simple
+SECTION("polygon with spike") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(2,2);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( mapnik::geometry::is_simple(poly) );
+}
+
+SECTION("polygon with hole") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(3,0);
+    ring.add_coord(3,3);
+    ring.add_coord(0,3);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> hole;
+    hole.add_coord(1,1);    
+    hole.add_coord(1,2);
+    hole.add_coord(2,2);
+    hole.add_coord(2,1);
+    hole.add_coord(1,1);
+    poly.add_hole(std::move(hole));
+    CHECK( mapnik::geometry::is_simple(poly) );
+}
+
+// Polygons with reversed winding order still can be considered simple
+SECTION("polygon with hole with invalid winding order") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(3,0);
+    ring.add_coord(3,3);
+    ring.add_coord(0,3);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> hole;
+    hole.add_coord(1,1);    
+    hole.add_coord(2,1);
+    hole.add_coord(2,2);
+    hole.add_coord(1,2);
+    hole.add_coord(1,1);
+    poly.add_hole(std::move(hole));
+    CHECK( mapnik::geometry::is_simple(poly) );
+}
+
+SECTION("multi polygon") {
+    mapnik::geometry::multi_polygon<double> mp;
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::polygon<double> poly2;
+    mapnik::geometry::linear_ring<double> ring2;
+    ring2.add_coord(0,0);    
+    ring2.add_coord(-1,0);
+    ring2.add_coord(-1,-1);
+    ring2.add_coord(0,-1);
+    ring2.add_coord(0,0);
+    poly2.set_exterior_ring(std::move(ring2));
+    mp.emplace_back(poly);
+    mp.emplace_back(poly2);
+    CHECK( mapnik::geometry::is_simple(mp) );
+}
+
+SECTION("multi polygon with hole") {
+    mapnik::geometry::multi_polygon<double> mp;
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(3,0);
+    ring.add_coord(3,3);
+    ring.add_coord(0,3);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> hole;
+    hole.add_coord(1,1);
+    hole.add_coord(1,2);
+    hole.add_coord(2,2);
+    hole.add_coord(2,1);
+    hole.add_coord(1,1);
+    poly.add_hole(std::move(hole));
+    mapnik::geometry::polygon<double> poly2;
+    mapnik::geometry::linear_ring<double> ring2;
+    ring2.add_coord(0,0);    
+    ring2.add_coord(-3,0);
+    ring2.add_coord(-3,-3);
+    ring2.add_coord(0,-3);
+    ring2.add_coord(0,0);
+    poly2.set_exterior_ring(std::move(ring2));
+    mapnik::geometry::linear_ring<double> hole2;
+    hole2.add_coord(-1,-1);    
+    hole2.add_coord(-1,-2);
+    hole2.add_coord(-2,-2);
+    hole2.add_coord(-2,-1);
+    hole2.add_coord(-1,-1);
+    poly2.add_hole(std::move(hole2));
+    mp.emplace_back(poly);
+    mp.emplace_back(poly2);
+    CHECK( mapnik::geometry::is_simple(mp) );
+}
+
+SECTION("multi polygon empty") {
+    mapnik::geometry::multi_polygon<double> mp;
+    CHECK( mapnik::geometry::is_simple(mp) );
+}
+
+#else // BOOST_VERSION >= 1.58
+
+SECTION("skipped is_simple tests") {
+    WARN( "geometry simple tests disabled due to boost version older that 1.58 used" );
+}
+
+#endif
+
+}
diff --git a/test/unit/geometry/geometry_is_valid.cpp b/test/unit/geometry/geometry_is_valid.cpp
index 5f4f862..85577ec 100644
--- a/test/unit/geometry/geometry_is_valid.cpp
+++ b/test/unit/geometry/geometry_is_valid.cpp
@@ -6,26 +6,443 @@
 
 TEST_CASE("geometry is_valid") {
 
-// only Boost >= 1.56 has the is_valid function
-#if BOOST_VERSION >= 105600
+// only Boost >= 1.56 has the is_valid function, but only after 1.58 is there support for returning what is invalid
+#if BOOST_VERSION >= 105800
+
+
+SECTION("empty geometry") {
+    mapnik::geometry::geometry_empty empty;
+    CHECK( mapnik::geometry::is_valid(empty) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(empty, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(empty, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
 
 SECTION("point") {
     mapnik::geometry::point<double> pt(0,0);
-    REQUIRE( mapnik::geometry::is_valid(pt) );
+    CHECK( mapnik::geometry::is_valid(pt) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(pt, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(pt, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("point -- geometry object") {
+    mapnik::geometry::point<double> pt(0,0);
+    mapnik::geometry::geometry<double> geom(pt);
+    CHECK( mapnik::geometry::is_valid(geom) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(geom, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(geom, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
 
-    // uninitialized: should likely not be considered valid
+SECTION("point unitialized") {
     mapnik::geometry::point<double> pt2;
-    REQUIRE( mapnik::geometry::is_valid(pt2) );
+    CHECK( mapnik::geometry::is_valid(pt2) );
+    std::string message2;
+    CHECK( mapnik::geometry::is_valid(pt2, message2) );
+    CHECK( message2 == "Geometry is valid");
+    boost::geometry::validity_failure_type failure2;
+    CHECK( mapnik::geometry::is_valid(pt2, failure2) );
+    CHECK( failure2 == boost::geometry::no_failure );
+}
+
+// This is funky that boost geometry is_valid does not check for NAN when dealing with a point
+// this test is here in case the logic ever changes
+// Bug report on this: https://svn.boost.org/trac/boost/ticket/11711
+SECTION("point NaN") {
+    mapnik::geometry::point<double> pt(std::numeric_limits<double>::quiet_NaN(),std::numeric_limits<double>::quiet_NaN());
+    CHECK( std::isnan(pt.x) );
+    CHECK( std::isnan(pt.y) );
+    CHECK( mapnik::geometry::is_valid(pt) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(pt, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(pt, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+// This is funky that boost geometry is_valid does not check for infinity when dealing with a point
+// this test is here in case the logic ever changes
+// Bug report on this: https://svn.boost.org/trac/boost/ticket/11711
+SECTION("point Infinity") {
+    mapnik::geometry::point<double> pt(std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity());
+    CHECK( std::isinf(pt.x) );
+    CHECK( std::isinf(pt.y) );
+    CHECK( mapnik::geometry::is_valid(pt) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(pt, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(pt, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("multi point") {
+    mapnik::geometry::multi_point<double> mpt;
+    mpt.add_coord(0,0);    
+    mpt.add_coord(1,1);
+    CHECK( mapnik::geometry::is_valid(mpt) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(mpt, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(mpt, failure) );
+    CHECK( failure == boost::geometry::no_failure );
 }
 
+SECTION("multi point empty") {
+    mapnik::geometry::multi_point<double> mpt;
+    CHECK( mapnik::geometry::is_valid(mpt) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(mpt, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(mpt, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+
 SECTION("line_string") {
     mapnik::geometry::line_string<double> line;
     line.add_coord(0,0);    
     line.add_coord(1,1);
-    REQUIRE( mapnik::geometry::is_valid(line) );
+    CHECK( mapnik::geometry::is_valid(line) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(line, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(line, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+// This shouldn't fail -- test added in case logic ever changes
+SECTION("line_string repeated points") {
+    mapnik::geometry::line_string<double> line;
+    line.add_coord(0,0);    
+    line.add_coord(1,1);
+    line.add_coord(1,1);
+    line.add_coord(2,2);
+    CHECK( mapnik::geometry::is_valid(line) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(line, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(line, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("line_string empty") {
+    mapnik::geometry::line_string<double> line;
+    CHECK( !mapnik::geometry::is_valid(line) );
+    std::string message;
+    CHECK( !mapnik::geometry::is_valid(line, message) );
+    CHECK( message == "Geometry has too few points");
+    boost::geometry::validity_failure_type failure;
+    CHECK( !mapnik::geometry::is_valid(line, failure) );
+    CHECK( failure == boost::geometry::failure_few_points );
+}
+
+SECTION("multi_line_string") {
+    mapnik::geometry::line_string<double> line1;
+    line1.add_coord(0,0);    
+    line1.add_coord(1,1);
+    mapnik::geometry::line_string<double> line2;
+    line2.add_coord(0,1);    
+    line2.add_coord(1,2);
+    mapnik::geometry::multi_line_string<double> lines;
+    lines.emplace_back(line1);
+    lines.emplace_back(line2);
+    CHECK( mapnik::geometry::is_valid(lines) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(lines, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(lines, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("multi_line_string empty") {
+    mapnik::geometry::multi_line_string<double> lines;
+    CHECK( mapnik::geometry::is_valid(lines) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(lines, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(lines, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("polygon") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("polygon invalid winding order") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(0,1);
+    ring.add_coord(1,1);
+    ring.add_coord(1,0);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( !mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( !mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry has wrong orientation" );
+    boost::geometry::validity_failure_type failure;
+    CHECK( !mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::failure_wrong_orientation );
+}
+
+// repeated points are not considered invalid in a polygon
+SECTION("polygon 2 repeated points") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+// repeated points are not considered invalid in a polygon
+SECTION("polygon 3 repeated points") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(1,1);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("polygon that is empty") {
+    mapnik::geometry::polygon<double> poly;
+    CHECK( !mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( !mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry has too few points");
+    boost::geometry::validity_failure_type failure;
+    CHECK( !mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::failure_few_points );
+}
+
+SECTION("polygon with spike") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(2,2);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( !mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( !mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry has spikes. A spike point was found with apex at (2, 2)");
+    boost::geometry::validity_failure_type failure;
+    CHECK( !mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::failure_spikes );
+}
+
+SECTION("polygon with hole") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(3,0);
+    ring.add_coord(3,3);
+    ring.add_coord(0,3);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> hole;
+    hole.add_coord(1,1);    
+    hole.add_coord(1,2);
+    hole.add_coord(2,2);
+    hole.add_coord(2,1);
+    hole.add_coord(1,1);
+    poly.add_hole(std::move(hole));
+    CHECK( mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("polygon with empty hole") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(3,0);
+    ring.add_coord(3,3);
+    ring.add_coord(0,3);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> hole;
+    poly.add_hole(std::move(hole));
+    CHECK( !mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( !mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry has too few points");
+    boost::geometry::validity_failure_type failure;
+    CHECK( !mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::failure_few_points );
+}
+
+
+SECTION("polygon with hole with invalid winding order") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(3,0);
+    ring.add_coord(3,3);
+    ring.add_coord(0,3);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> hole;
+    hole.add_coord(1,1);    
+    hole.add_coord(2,1);
+    hole.add_coord(2,2);
+    hole.add_coord(1,2);
+    hole.add_coord(1,1);
+    poly.add_hole(std::move(hole));
+    CHECK( !mapnik::geometry::is_valid(poly) );
+    std::string message;
+    CHECK( !mapnik::geometry::is_valid(poly, message) );
+    CHECK( message == "Geometry has wrong orientation" );
+    boost::geometry::validity_failure_type failure;
+    CHECK( !mapnik::geometry::is_valid(poly, failure) );
+    CHECK( failure == boost::geometry::failure_wrong_orientation );
+}
+
+SECTION("multi polygon") {
+    mapnik::geometry::multi_polygon<double> mp;
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::polygon<double> poly2;
+    mapnik::geometry::linear_ring<double> ring2;
+    ring2.add_coord(0,0);    
+    ring2.add_coord(-1,0);
+    ring2.add_coord(-1,-1);
+    ring2.add_coord(0,-1);
+    ring2.add_coord(0,0);
+    poly2.set_exterior_ring(std::move(ring2));
+    mp.emplace_back(poly);
+    mp.emplace_back(poly2);
+    CHECK( mapnik::geometry::is_valid(mp) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(mp, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(mp, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
+
+SECTION("multi polygon with hole") {
+    mapnik::geometry::multi_polygon<double> mp;
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(3,0);
+    ring.add_coord(3,3);
+    ring.add_coord(0,3);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> hole;
+    hole.add_coord(1,1);    
+    hole.add_coord(1,2);
+    hole.add_coord(2,2);
+    hole.add_coord(2,1);
+    hole.add_coord(1,1);
+    poly.add_hole(std::move(hole));
+    mapnik::geometry::polygon<double> poly2;
+    mapnik::geometry::linear_ring<double> ring2;
+    ring2.add_coord(0,0);    
+    ring2.add_coord(-3,0);
+    ring2.add_coord(-3,-3);
+    ring2.add_coord(0,-3);
+    ring2.add_coord(0,0);
+    poly2.set_exterior_ring(std::move(ring2));
+    mapnik::geometry::linear_ring<double> hole2;
+    hole2.add_coord(-1,-1);    
+    hole2.add_coord(-1,-2);
+    hole2.add_coord(-2,-2);
+    hole2.add_coord(-2,-1);
+    hole2.add_coord(-1,-1);
+    poly2.add_hole(std::move(hole2));
+    mp.emplace_back(poly);
+    mp.emplace_back(poly2);
+    CHECK( mapnik::geometry::is_valid(mp) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(mp, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(mp, failure) );
+    CHECK( failure == boost::geometry::no_failure );
+}
 
+SECTION("multi polygon empty") {
+    mapnik::geometry::multi_polygon<double> mp;
+    CHECK( mapnik::geometry::is_valid(mp) );
+    std::string message;
+    CHECK( mapnik::geometry::is_valid(mp, message) );
+    CHECK( message == "Geometry is valid");
+    boost::geometry::validity_failure_type failure;
+    CHECK( mapnik::geometry::is_valid(mp, failure) );
+    CHECK( failure == boost::geometry::no_failure );
 }
 
-#endif // BOOST_VERSION >= 1.56
+#endif // BOOST_VERSION >= 1.58
 
 }
diff --git a/test/unit/imaging/image_filter.cpp b/test/unit/imaging/image_filter.cpp
index 0386409..8d41ccb 100644
--- a/test/unit/imaging/image_filter.cpp
+++ b/test/unit/imaging/image_filter.cpp
@@ -6,6 +6,9 @@
 #include <mapnik/color.hpp>
 #include <mapnik/image_filter.hpp>
 #include <mapnik/image_util.hpp>
+#include <mapnik/image_filter_grammar.hpp>
+#include <mapnik/image_filter_grammar_impl.hpp>
+#include <mapnik/css_color_grammar_impl.hpp>
 
 TEST_CASE("image filter") {
 
@@ -388,6 +391,49 @@ SECTION("test colorize-alpha - two color with transparency") {
 
 } // END SECTION
 
+SECTION("test colorize-alpha - parsing correct input") {
+
+    mapnik::image_filter_grammar<std::string::const_iterator, std::vector<mapnik::filter::filter_type>> filter_grammar;
+    boost::spirit::qi::ascii::space_type space;
+    std::vector<mapnik::filter::filter_type> f;
+    std::string s("colorize-alpha(#0000ff 0%, #00ff00 100%)");
+    CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), filter_grammar, space, f) );
+    mapnik::filter::colorize_alpha const & ca = mapnik::util::get<mapnik::filter::colorize_alpha>(f.front());
+
+    {
+        mapnik::filter::color_stop const & s2 = ca[0];
+        CHECK( s2.color.alpha() == 0xff );
+        CHECK( s2.color.red() == 0x00 );
+        CHECK( s2.color.green() == 0x00 );
+        CHECK( s2.color.blue() == 0xff );
+        CHECK( s2.offset == 0.0 );
+    }
+
+    {
+        mapnik::filter::color_stop const & s2 = ca[1];
+        CHECK( s2.color.alpha() == 0xff );
+        CHECK( s2.color.red() == 0x00 );
+        CHECK( s2.color.green() == 0xff );
+        CHECK( s2.color.blue() == 0x00 );
+        CHECK( s2.offset == 1.0 );
+    }
+
+} // END SECTION
+
+SECTION("test colorize-alpha - parsing incorrect input") {
+
+    mapnik::image_filter_grammar<std::string::const_iterator, std::vector<mapnik::filter::filter_type>> filter_grammar;
+    boost::spirit::qi::ascii::space_type space;
+    std::string s("colorize-alpha(#0000ff 0%, #00ff00 00 100%)");
+    std::string::const_iterator itr = s.cbegin();
+    std::string::const_iterator end = s.cend();
+    std::vector<mapnik::filter::filter_type> f;
+    CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), filter_grammar, space, f) );
+    CHECK( f.empty() );
+    CHECK( itr != end );
+
+} // END SECTION
+
 SECTION("test color-blind-protanope") {
     
     mapnik::image_rgba8 im(2,2);
diff --git a/test/unit/serialization/wkb_formats_test.cpp b/test/unit/serialization/wkb_formats_test.cpp
index 9e18b28..ff72a1c 100644
--- a/test/unit/serialization/wkb_formats_test.cpp
+++ b/test/unit/serialization/wkb_formats_test.cpp
@@ -70,7 +70,7 @@ SECTION("wkb") {
                                                                                mapnik::wkbSpatiaLite);
         // winding order is not correct per OGC so we'll fix it
         mapnik::geometry::correct(geom);
-#if BOOST_VERSION >= 105600
+#if BOOST_VERSION >= 105800
         REQUIRE(mapnik::geometry::is_valid(geom));
         REQUIRE(mapnik::geometry::is_simple(geom));
 #endif
@@ -79,7 +79,7 @@ SECTION("wkb") {
                                                 sizeof(sp_valid_blob) / sizeof(sp_valid_blob[0]),
                                                 mapnik::wkbAuto);
         mapnik::geometry::correct(geom);
-#if BOOST_VERSION >= 105600
+#if BOOST_VERSION >= 105800
         REQUIRE(mapnik::geometry::is_valid(geom));
         REQUIRE(mapnik::geometry::is_simple(geom));
 #endif
@@ -94,7 +94,7 @@ SECTION("wkb") {
         geom = mapnik::geometry_utils::from_wkb((const char*)sq_valid_blob,
                                                 sizeof(sq_valid_blob) / sizeof(sq_valid_blob[0]),
                                                 mapnik::wkbGeneric);
-#if BOOST_VERSION >= 105600
+#if BOOST_VERSION >= 105800
         REQUIRE(mapnik::geometry::is_valid(geom));
         REQUIRE(mapnik::geometry::is_simple(geom));
 #endif
@@ -103,7 +103,7 @@ SECTION("wkb") {
                                                  sizeof(sq_valid_blob) / sizeof(sq_valid_blob[0]),
                                                  mapnik::wkbAuto);
 
-#if BOOST_VERSION >= 105600
+#if BOOST_VERSION >= 105800
         REQUIRE(mapnik::geometry::is_valid(geom));
         REQUIRE(mapnik::geometry::is_simple(geom));
 #endif
@@ -113,7 +113,9 @@ SECTION("wkb") {
                                                 mapnik::wkbGeneric);
         REQUIRE(geom.is<mapnik::geometry::geometry_empty>()); // returns geometry_empty
 
-    } catch (std::exception const& ex) {
+    }
+    catch (std::exception const& ex)
+    {
         REQUIRE(false);
         std::clog << "threw: " << ex.what() << "\n";
     }
diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp
index 276e3c5..6dfb0bd 100644
--- a/test/unit/svg/svg_parser_test.cpp
+++ b/test/unit/svg/svg_parser_test.cpp
@@ -333,6 +333,26 @@ TEST_CASE("SVG parser") {
         REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>()));
     }
 
+    SECTION("SVG viewbox fallback")
+    {
+        std::string svg_name("./test/data/svg/viewbox-missing-width-and-height.svg");
+        std::ifstream in(svg_name.c_str());
+        std::string svg_str((std::istreambuf_iterator<char>(in)),
+                        std::istreambuf_iterator<char>());
+
+        using namespace mapnik::svg;
+        mapnik::svg_storage_type path;
+        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+        svg_path_adapter svg_path(stl_storage);
+        svg_converter_type svg(svg_path, path.attributes());
+        svg_parser p(svg);
+        p.parse_from_string(svg_str);
+        auto width = svg.width();
+        auto height = svg.height();
+        REQUIRE(width == 100);
+        REQUIRE(height == 100);
+    }
+
     SECTION("SVG rounded <rect>s missing rx or ry")
     {
         std::string svg_name("./test/data/svg/rounded_rect-missing-one-radius.svg");
diff --git a/test/visual/renderer.hpp b/test/visual/renderer.hpp
index 6ad9791..6b033f9 100644
--- a/test/visual/renderer.hpp
+++ b/test/visual/renderer.hpp
@@ -33,6 +33,7 @@
 #include <mapnik/map.hpp>
 #include <mapnik/image_util.hpp>
 #include <mapnik/image_reader.hpp>
+#include <mapnik/util/variant.hpp>
 #include <mapnik/agg_renderer.hpp>
 #if defined(GRID_RENDERER)
 #include <mapnik/grid/grid_renderer.hpp>
@@ -326,11 +327,23 @@ private:
     }
 
     const Renderer ren;
-    boost::filesystem::path const & output_dir;
-    boost::filesystem::path const & reference_dir;
+    const boost::filesystem::path output_dir;
+    const boost::filesystem::path reference_dir;
     const bool overwrite;
 };
 
+using renderer_type = mapnik::util::variant<renderer<agg_renderer>
+#if defined(HAVE_CAIRO)
+                                            ,renderer<cairo_renderer>
+#endif
+#if defined(SVG_RENDERER)
+                                            ,renderer<svg_renderer>
+#endif
+#if defined(GRID_RENDERER)
+                                            ,renderer<grid_renderer>
+#endif
+                                            >;
+
 }
 
 #endif
diff --git a/test/visual/report.cpp b/test/visual/report.cpp
index deaf5b9..c4cfc13 100644
--- a/test/visual/report.cpp
+++ b/test/visual/report.cpp
@@ -161,16 +161,18 @@ void html_report::report(result const & r, boost::filesystem::path const & outpu
       copy_file(r.reference_image_path, reference);
       copy_file(r.actual_image_path, actual);
 
-       s << "<div class=\"expected\">\n"
-            "  <a href=" << reference.filename() << ">\n"
-            "    <img src=" << reference.filename() << " width=\"100\%\">\n"
-            "  </a>\n"
-            "</div>\n"
-            "<div class=\"text\">" << r.diff << "</div>\n"
-            "<div class=\"actual\">\n"
-            "  <a href=" << actual.filename() << ">\n"
-            "    <img src=" << actual.filename() << " width=\"100\%\">\n"
-            "  </a>\n"
+       s << "<p>" << r.diff << "</p>\n"
+            "<div class=\"r\">"
+            "  <div class=\"i\">"
+            "    <a href=" << reference.filename() << ">\n"
+            "      <img src=" << reference.filename() << " width=\"100\%\">\n"
+            "    </a>\n"
+            "  </div>\n"
+            "  <div class=\"i2\">\n"
+            "    <a href=" << actual.filename() << ">\n"
+            "      <img src=" << actual.filename() << " width=\"100\%\">\n"
+            "    </a>\n"
+            "  </div>\n"
             "</div>\n";
     }
 }
@@ -179,31 +181,21 @@ constexpr const char * html_header = R"template(<!DOCTYPE html>
 <html>
 <head>
   <style>
-    body { margin:10; padding:10; }
-    .expected {
-       float:left;
-       border-width:1px;
-       border-style:solid;
-       width:45%;
-    }    
-    .actual {
-       float:right;
-       border-width:1px;
-       border-style:solid;
-       width:45%;
-    }
-    .text {
-       float:left;
+    .r {
+      width:100%;
+      display: flex;
+      position: relative;
+      border:1px solid black;
+      margin-bottom: 20px;
     }
+    .i2 { max-width: 50%; width:50%; }
+    .i { max-width: 50%; width:50%; }
+    .i:hover{ position: absolute; top:0; left:0; }
+    .i img, .i2 img { width: 100%; }
+    .i img:hover { mix-blend-mode: difference; }
   </style>
 </head>
 <body>
-<script>
-</script>
-<div id='results'>
-     <div class="expected">expected</div>
-     <div class="text">% difference</div>
-     <div class="actual">actual</div>
 )template";
 
 constexpr const char * html_footer = R"template(</div>
diff --git a/test/visual/report.hpp b/test/visual/report.hpp
index 837ed5e..bb21baf 100644
--- a/test/visual/report.hpp
+++ b/test/visual/report.hpp
@@ -59,7 +59,7 @@ public:
     {
     }
 
-    console_short_report(std::ostream & s) : console_report(s)
+    console_short_report(std::ostream & _s) : console_report(_s)
     {
     }
 
diff --git a/test/visual/run.cpp b/test/visual/run.cpp
index 833f97b..70284bc 100644
--- a/test/visual/run.cpp
+++ b/test/visual/run.cpp
@@ -43,11 +43,50 @@ log_levels_map log_levels
 };
 #endif
 
-int main(int argc, char** argv)
+using namespace visual_tests;
+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)
 {
-    using namespace visual_tests;
-    namespace po = boost::program_options;
+    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))
+    {
+        renderers.emplace_back(renderer<agg_renderer>(output_dir, reference_dir, overwrite));
+    }
+#if defined(HAVE_CAIRO)
+    if (append_all || args.count(cairo_renderer::name))
+    {
+        renderers.emplace_back(renderer<cairo_renderer>(output_dir, reference_dir, overwrite));
+    }
+#endif
+#if defined(SVG_RENDERER)
+    if (append_all || 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))
+    {
+        renderers.emplace_back(renderer<grid_renderer>(output_dir, reference_dir, overwrite));
+    }
+#endif
+
+    if (renderers.empty())
+    {
+        return create_renderers(args, output_dir, true);
+    }
+
+    return renderers;
+}
+
+int main(int argc, char** argv)
+{
     po::options_description desc("visual test runner");
     desc.add_options()
         ("help,h", "produce usage message")
@@ -69,6 +108,17 @@ int main(int argc, char** argv)
              [](log_levels_map::value_type const & level) { return level.second == mapnik::logger::get_severity(); } )->first),
              "log level (debug, warn, error, none)")
 #endif
+        ("scale-factor,s", po::value<std::vector<double>>()->default_value({ 1.0, 2.0 }, "1.0, 2.0"), "scale factor")
+        (agg_renderer::name, "render with AGG renderer")
+#if defined(HAVE_CAIRO)
+        (cairo_renderer::name, "render with Cairo renderer")
+#endif
+#if defined(SVG_RENDERER)
+        (svg_renderer::name, "render with SVG renderer")
+#endif
+#if defined(GRID_RENDERER)
+        (grid_renderer::name, "render with Grid renderer")
+#endif
         ;
 
     po::positional_options_description p;
@@ -107,13 +157,15 @@ int main(int argc, char** argv)
         output_dir /= boost::filesystem::unique_path();
     }
 
+    config defaults;
+    defaults.scales = vm["scale-factor"].as<std::vector<double>>();
+
     runner run(vm["styles-dir"].as<std::string>(),
-               output_dir,
-               vm["images-dir"].as<std::string>(),
-               vm.count("overwrite"),
+               defaults,
                vm["iterations"].as<std::size_t>(),
                vm["limit"].as<std::size_t>(),
-               vm["jobs"].as<std::size_t>());
+               vm["jobs"].as<std::size_t>(),
+               create_renderers(vm, output_dir));
     bool show_duration = vm.count("duration");
     report_type report(vm.count("verbose") ? report_type((console_report(show_duration))) : report_type((console_short_report(show_duration))));
     result_list results;
diff --git a/test/visual/runner.cpp b/test/visual/runner.cpp
index a987d05..06ade77 100644
--- a/test/visual/runner.cpp
+++ b/test/visual/runner.cpp
@@ -126,29 +126,17 @@ private:
 };
 
 runner::runner(runner::path_type const & styles_dir,
-               runner::path_type const & output_dir,
-               runner::path_type const & reference_dir,
-               bool overwrite,
+               config const & defaults,
                std::size_t iterations,
                std::size_t fail_limit,
-               std::size_t jobs)
+               std::size_t jobs,
+               runner::renderer_container const & renderers)
     : styles_dir_(styles_dir),
-      output_dir_(output_dir),
-      reference_dir_(reference_dir),
+      defaults_(defaults),
       jobs_(jobs),
       iterations_(iterations),
       fail_limit_(fail_limit),
-      renderers_{ renderer<agg_renderer>(output_dir_, reference_dir_, overwrite)
-#if defined(HAVE_CAIRO)
-                  ,renderer<cairo_renderer>(output_dir_, reference_dir_, overwrite)
-#endif
-#if defined(SVG_RENDERER)
-                  ,renderer<svg_renderer>(output_dir_, reference_dir_, overwrite)
-#endif
-#if defined(GRID_RENDERER)
-                  ,renderer<grid_renderer>(output_dir_, reference_dir_, overwrite)
-#endif
-                }
+      renderers_(renderers)
 {
 }
 
@@ -225,7 +213,6 @@ result_list runner::test_range(files_iterator begin,
                                std::reference_wrapper<report_type> report,
                                std::reference_wrapper<std::atomic<std::size_t>> fail_count) const
 {
-    config defaults;
     result_list results;
 
     for (runner::files_iterator i = begin; i != end; i++)
@@ -235,7 +222,7 @@ result_list runner::test_range(files_iterator begin,
         {
             try
             {
-                result_list r = test_one(file, defaults, report, fail_count.get());
+                result_list r = test_one(file, report, fail_count.get());
                 std::move(r.begin(), r.end(), std::back_inserter(results));
             }
             catch (std::exception const& ex)
@@ -260,10 +247,10 @@ result_list runner::test_range(files_iterator begin,
 }
 
 result_list runner::test_one(runner::path_type const& style_path,
-                             config cfg,
                              report_type & report,
                              std::atomic<std::size_t> & fail_count) const
 {
+    config cfg(defaults_);
     mapnik::Map map(cfg.sizes.front().width, cfg.sizes.front().height);
     result_list results;
 
diff --git a/test/visual/runner.hpp b/test/visual/runner.hpp
index 65b19bb..e38e394 100644
--- a/test/visual/runner.hpp
+++ b/test/visual/runner.hpp
@@ -23,8 +23,6 @@
 #ifndef VISUAL_TEST_RUNNER_HPP
 #define VISUAL_TEST_RUNNER_HPP
 
-#include <mapnik/util/variant.hpp>
-
 #include "config.hpp"
 #include "report.hpp"
 #include "renderer.hpp"
@@ -35,28 +33,18 @@ namespace visual_tests
 
 class runner
 {
-    using renderer_type = mapnik::util::variant<renderer<agg_renderer>
-#if defined(HAVE_CAIRO)
-                                                ,renderer<cairo_renderer>
-#endif
-#if defined(SVG_RENDERER)
-                                                ,renderer<svg_renderer>
-#endif
-#if defined(GRID_RENDERER)
-                                                ,renderer<grid_renderer>
-#endif
-                                                >;
     using path_type = boost::filesystem::path;
     using files_iterator = std::vector<path_type>::const_iterator;
 
 public:
+    using renderer_container = std::vector<renderer_type>;
+
     runner(path_type const & styles_dir,
-           path_type const & output_dir,
-           path_type const & reference_dir,
-           bool overwrite,
+           config const & cfg,
            std::size_t iterations,
            std::size_t fail_limit,
-           std::size_t jobs);
+           std::size_t jobs,
+           renderer_container const & renderers);
 
     result_list test_all(report_type & report) const;
     result_list test(std::vector<std::string> const & style_names, report_type & report) const;
@@ -68,18 +56,17 @@ private:
                            std::reference_wrapper<report_type> report,
                            std::reference_wrapper<std::atomic<std::size_t>> fail_limit) const;
     result_list test_one(path_type const & style_path,
-                         config cfg, report_type & report,
+                         report_type & report,
                          std::atomic<std::size_t> & fail_limit) const;
     void parse_map_sizes(std::string const & str, std::vector<map_size> & sizes) const;
 
     const map_sizes_grammar<std::string::const_iterator> map_sizes_parser_;
     const path_type styles_dir_;
-    const path_type output_dir_;
-    const path_type reference_dir_;
+    const config defaults_;
     const std::size_t jobs_;
     const std::size_t iterations_;
     const std::size_t fail_limit_;
-    const renderer_type renderers_[boost::mpl::size<renderer_type::types>::value];
+    const renderer_container renderers_;
 };
 
 }
diff --git a/utils/mapnik-index/build.py b/utils/mapnik-index/build.py
new file mode 100644
index 0000000..02258ca
--- /dev/null
+++ b/utils/mapnik-index/build.py
@@ -0,0 +1,59 @@
+#
+# This file is part of Mapnik (c++ mapping toolkit)
+#
+# Copyright (C) 2015 Artem Pavlenko
+#
+# Mapnik 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
+#
+#
+
+import os
+import glob
+from copy import copy
+
+Import ('env')
+
+program_env = env.Clone()
+
+source = Split(
+    """
+    mapnik-index.cpp
+    """
+    )
+
+#headers = ['#plugins/input/shape'] + env['CPPPATH']
+headers = env['CPPPATH']
+
+boost_program_options = 'boost_program_options%s' % env['BOOST_APPEND']
+boost_system = 'boost_system%s' % env['BOOST_APPEND']
+libraries =  [env['MAPNIK_NAME'], boost_program_options, boost_system]
+libraries.append(env['ICU_LIB_NAME'])
+libraries.append('mapnik-json')
+libraries.append('mapnik-wkt')
+
+if env['RUNTIME_LINK'] == 'static':
+    libraries.extend(copy(env['LIBMAPNIK_LIBS']))
+    if env['PLATFORM'] == 'Linux':
+        libraries.append('dl')
+
+mapnik_index = program_env.Program('mapnik-index', source, CPPPATH=headers, LIBS=libraries)
+
+Depends(mapnik_index, env.subst('../../src/%s' % env['MAPNIK_LIB_NAME']))
+
+if 'uninstall' not in COMMAND_LINE_TARGETS:
+    env.Install(os.path.join(env['INSTALL_PREFIX'],'bin'), mapnik_index)
+    env.Alias('install', os.path.join(env['INSTALL_PREFIX'],'bin'))
+
+env['create_uninstall_target'](env, os.path.join(env['INSTALL_PREFIX'],'bin','mapnik-index'))
diff --git a/utils/mapnik-index/mapnik-index.cpp b/utils/mapnik-index/mapnik-index.cpp
new file mode 100644
index 0000000..0f54d4c
--- /dev/null
+++ b/utils/mapnik-index/mapnik-index.cpp
@@ -0,0 +1,353 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2015 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
+ *
+ *****************************************************************************/
+
+#include <iostream>
+#include <vector>
+#include <string>
+#include <fstream>
+
+#include <mapnik/util/fs.hpp>
+#include <mapnik/geometry_envelope.hpp>
+#include <mapnik/quad_tree.hpp>
+#include "../../plugins/input/csv/csv_utils.hpp"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wunused-local-typedef"
+#include <boost/algorithm/string.hpp>
+#include <boost/program_options.hpp>
+#pragma GCC diagnostic pop
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#include <boost/interprocess/mapped_region.hpp>
+#include <boost/interprocess/streams/bufferstream.hpp>
+#pragma GCC diagnostic pop
+#include <mapnik/mapped_memory_cache.hpp>
+#include <boost/version.hpp>
+
+const int DEFAULT_DEPTH = 8;
+const double DEFAULT_RATIO = 0.55;
+
+int main (int argc, char** argv)
+{
+    //using namespace mapnik;
+    namespace po = boost::program_options;
+    using std::string;
+    using std::vector;
+    using std::clog;
+    using std::endl;
+
+    bool verbose = false;
+    unsigned int depth = DEFAULT_DEPTH;
+    double ratio = DEFAULT_RATIO;
+    vector<string> csv_files;
+    char separator = 0;
+    char quote = 0;
+    std::string manual_headers;
+    try
+    {
+        po::options_description desc("csvindex utility");
+        desc.add_options()
+            ("help,h", "produce usage message")
+            ("version,V","print version string")
+            ("verbose,v","verbose output")
+            ("depth,d", po::value<unsigned int>(), "max tree depth\n(default 8)")
+            ("ratio,r",po::value<double>(),"split ratio (default 0.55)")
+            ("separator,s", po::value<char>(), "CSV columns separator")
+            ("quote,q", po::value<char>(), "CSV columns quote")
+            ("manual-headers,H", po::value<std::string>(), "CSV manual headers string")
+            ("csv_files",po::value<vector<string> >(),"CSV files to index: file1 file2 ...fileN")
+            ;
+
+        po::positional_options_description p;
+        p.add("csv_files",-1);
+        po::variables_map vm;
+        po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
+        po::notify(vm);
+
+        if (vm.count("version"))
+        {
+            clog << "version 0.3.0" <<std::endl;
+            return 1;
+        }
+
+        if (vm.count("help"))
+        {
+            clog << desc << endl;
+            return 1;
+        }
+        if (vm.count("verbose"))
+        {
+            verbose = true;
+        }
+        if (vm.count("depth"))
+        {
+            depth = vm["depth"].as<unsigned int>();
+        }
+        if (vm.count("ratio"))
+        {
+            ratio = vm["ratio"].as<double>();
+        }
+        if (vm.count("separator"))
+        {
+            separator = vm["separator"].as<char>();
+        }
+        if (vm.count("quote"))
+        {
+            quote = vm["quote"].as<char>();
+        }
+        if (vm.count("manual-headers"))
+        {
+            manual_headers = vm["manual-headers"].as<std::string>();
+        }
+        if (vm.count("csv_files"))
+        {
+            csv_files=vm["csv_files"].as< vector<string> >();
+        }
+    }
+    catch (std::exception const& ex)
+    {
+        clog << "Error: " << ex.what() << endl;
+        return -1;
+    }
+
+    clog << "max tree depth:" << depth << endl;
+    clog << "split ratio:" << ratio << endl;
+
+    if (csv_files.size() == 0)
+    {
+        clog << "no csv files to index" << endl;
+        return 0;
+    }
+
+    for (auto const& filename : csv_files)
+    {
+        clog << "processing " << filename << endl;
+        std::string csvname (filename);
+        if (! mapnik::util::exists (csvname))
+        {
+            clog << "Error : file " << csvname << " does not exist" << endl;
+            continue;
+        }
+        using file_source_type = boost::interprocess::ibufferstream;
+        file_source_type csv_file;
+
+        mapnik::mapped_region_ptr mapped_region;
+        boost::optional<mapnik::mapped_region_ptr> memory =
+            mapnik::mapped_memory_cache::instance().find(csvname, true);
+        if (memory)
+        {
+            mapped_region = *memory;
+            csv_file.buffer(static_cast<char*>(mapped_region->get_address()),mapped_region->get_size());
+        }
+        else
+        {
+            clog << "Error : cannot mmap " << csvname << endl;
+            continue;
+        }
+        auto file_length = detail::file_length(csv_file);
+        // set back to start
+        csv_file.seekg(0, std::ios::beg);
+        char newline;
+        bool has_newline;
+        char detected_quote;
+        std::tie(newline, has_newline, detected_quote) = detail::autodect_newline_and_quote(csv_file, file_length);
+        if (quote == 0) quote = detected_quote;
+        // set back to start
+        csv_file.seekg(0, std::ios::beg);
+        // get first line
+        std::string csv_line;
+        csv_utils::getline_csv(csv_file, csv_line, newline, quote);
+        if (separator == 0) separator = detail::detect_separator(csv_line);
+        csv_file.seekg(0, std::ios::beg);
+        int line_number = 1;
+        detail::geometry_column_locator locator;
+        std::vector<std::string> headers;
+        std::clog << "Parsing CSV using SEPARATOR=" << separator << " QUOTE=" << quote << std::endl;
+        if (!manual_headers.empty())
+        {
+            std::size_t index = 0;
+            headers = csv_utils::parse_line(manual_headers, separator, quote);
+            for (auto const& header : headers)
+            {
+                detail::locate_geometry_column(header, index++, locator);
+                headers.push_back(header);
+            }
+        }
+        else // parse first line as headers
+        {
+            while (csv_utils::getline_csv(csv_file,csv_line,newline, quote))
+            {
+                try
+                {
+                    headers = csv_utils::parse_line(csv_line, separator, quote);
+                    // skip blank lines
+                    if (headers.size() > 0 && headers[0].empty()) ++line_number;
+                    else
+                    {
+                        std::size_t index = 0;
+                        for (auto & header : headers)
+                        {
+                            if (header.empty())
+                            {
+                                // create a placeholder for the empty header
+                                std::ostringstream s;
+                                s << "_" << index;
+                                header = s.str();
+                            }
+                            else
+                            {
+                                detail::locate_geometry_column(header, index, locator);
+                            }
+                            ++index;
+                        }
+                        ++line_number;
+                        break;
+                    }
+                }
+                catch (std::exception const& ex)
+                {
+                    std::string s("CSV index: error parsing headers: ");
+                    s += ex.what();
+                    std::clog << s << std::endl;
+                    return 1;
+                }
+            }
+        }
+
+        if (locator.type == detail::geometry_column_locator::UNKNOWN)
+        {
+            std::clog << "CSV index: could not detect column headers with the name of wkt, geojson, x/y, or "
+                      << "latitude/longitude - this is required for reading geometry data" << std::endl;
+            return 1;
+        }
+
+        std::size_t num_headers = headers.size();
+        auto pos = csv_file.tellg();
+
+        // handle rare case of a single line of data and user-provided headers
+        // where a lack of a newline will mean that csv_utils::getline_csv returns false
+        bool is_first_row = false;
+        if (!has_newline)
+        {
+            csv_file.setstate(std::ios::failbit);
+            pos = 0;
+            if (!csv_line.empty())
+            {
+                is_first_row = true;
+            }
+        }
+
+        mapnik::box2d<double> extent;
+        using box_type = mapnik::box2d<double>;
+        using item_type = std::pair<box_type, std::pair<unsigned, unsigned>>;
+        std::vector<item_type> boxes;
+
+        while (is_first_row || csv_utils::getline_csv(csv_file, csv_line, newline, quote))
+        {
+            auto record_offset = pos;
+            auto record_size = csv_line.length();
+            pos = csv_file.tellg();
+            is_first_row = false;
+            // skip blank lines
+            if (record_size <= 10)
+            {
+                std::string trimmed = csv_line;
+                boost::trim_if(trimmed, boost::algorithm::is_any_of("\",'\r\n "));
+                if (trimmed.empty())
+                {
+                    std::clog << "CSV index: empty row encountered at line: " << line_number << std::endl;
+                    continue;
+                }
+            }
+            try
+            {
+                auto values = csv_utils::parse_line(csv_line, separator, quote);
+                unsigned num_fields = values.size();
+                if (num_fields > num_headers || num_fields < num_headers)
+                {
+                    std::ostringstream s;
+                    s << "CSV Index: # of columns("
+                      << num_fields << ") > # of headers("
+                      << num_headers << ") parsed for row " << line_number << "\n";
+                    std::clog << s.str() << std::endl;
+                    return 1;
+                }
+
+                auto geom = detail::extract_geometry(values, locator);
+                if (!geom.is<mapnik::geometry::geometry_empty>())
+                {
+                    auto box = mapnik::geometry::envelope(geom);
+                    if (!extent.valid()) extent = box;
+                    else extent.expand_to_include(box);
+                    boxes.emplace_back(std::move(box), make_pair(record_offset, record_size));
+                }
+                else
+                {
+                    std::ostringstream s;
+                    s << "CSV Index: expected geometry column: could not parse row "
+                      << line_number << " "
+                      << values[locator.index] << "'";
+                    std::clog << s.str() << std::endl;;
+                }
+            }
+            catch (std::exception const& ex)
+            {
+                std::ostringstream s;
+                s << "CSV Index: unexpected error parsing line: " << line_number
+                  << " - found " << headers.size() << " with values like: " << csv_line << "\n"
+                  << " and got error like: " << ex.what();
+                std::clog << s.str() << std::endl;
+                return 1;
+            }
+        }
+
+        std::clog << extent << std::endl;
+        mapnik::quad_tree<std::pair<std::size_t, std::size_t>> tree(extent, depth, ratio);
+        for (auto const& item : boxes)
+        {
+            tree.insert(std::get<1>(item), std::get<0>(item));
+        }
+
+        std::fstream file((csvname + ".index").c_str(),
+                          std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
+        if (!file)
+        {
+            clog << "cannot open index file for writing file \""
+                 << (csvname + ".index") << "\"" << endl;
+        }
+        else
+        {
+            tree.trim();
+            std::clog <<  "number nodes=" << tree.count() << std::endl;
+            //tree.print();
+            file.exceptions(std::ios::failbit | std::ios::badbit);
+            tree.write(file);
+            file.flush();
+            file.close();
+        }
+    }
+    clog << "done!" << endl;
+    return 0;
+}
diff --git a/utils/pgsql2sqlite/pgsql2sqlite.hpp b/utils/pgsql2sqlite/pgsql2sqlite.hpp
index 5c5a640..f188e08 100644
--- a/utils/pgsql2sqlite/pgsql2sqlite.hpp
+++ b/utils/pgsql2sqlite/pgsql2sqlite.hpp
@@ -388,7 +388,7 @@ void pgsql2sqlite(Connection conn,
                 {
                     if (oid == geometry_oid)
                     {
-                        mapnik::Feature feat(ctx,pkid);
+                        mapnik::feature_impl feat(ctx,pkid);
                         mapnik::geometry::geometry<double> geom = geometry_utils::from_wkb(buf, size, wkbGeneric);
                         if (!mapnik::geometry::is_empty(geom))
                         {
diff --git a/utils/shapeindex/quadtree.hpp b/utils/shapeindex/quadtree.hpp
deleted file mode 100644
index 440ce50..0000000
--- a/utils/shapeindex/quadtree.hpp
+++ /dev/null
@@ -1,297 +0,0 @@
-/*****************************************************************************
- *
- * This file is part of Mapnik (c++ mapping toolkit)
- *
- * Copyright (C) 2015 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 QUADTREE_HPP
-#define QUADTREE_HPP
-// stl
-#include <cstring>
-#include <vector>
-#include <fstream>
-#include <iostream>
-// mapnik
-#include <mapnik/box2d.hpp>
-
-using mapnik::box2d;
-using mapnik::coord2d;
-
-template <typename T>
-struct quadtree_node
-{
-    box2d<double> ext_;
-    std::vector<T> data_;
-    quadtree_node<T>* children_[4];
-    quadtree_node(const box2d<double>& ext)
-        : ext_(ext),data_()
-    {
-        std::memset(children_,0,sizeof(quadtree_node<T>*)*4);
-    }
-
-    ~quadtree_node()
-    {
-        for (int i=0;i<4;++i)
-        {
-            if (children_[i])
-            {
-                delete children_[i],children_[i]=0;
-            }
-        }
-    }
-
-    int num_subnodes() const
-    {
-        int count=0;
-        for (int i=0;i<4;++i)
-        {
-            if (children_[i])
-            {
-                ++count;
-            }
-        }
-        return count;
-    }
-};
-
-template <typename T>
-class quadtree
-{
-private:
-    quadtree_node<T>* root_;
-    const int maxdepth_;
-    const double ratio_;
-public:
-    quadtree(const box2d<double>& extent,int maxdepth,double ratio)
-        : root_(new quadtree_node<T>(extent)),
-          maxdepth_(maxdepth),
-          ratio_(ratio) {}
-
-    ~quadtree()
-    {
-        if (root_) delete root_;
-    }
-
-    void insert(const T& data,const box2d<double>& item_ext)
-    {
-        insert(data,item_ext,root_,maxdepth_);
-    }
-
-    int count() const
-    {
-        return count_nodes(root_);
-    }
-
-    int count_items() const
-    {
-        int count=0;
-        count_items(root_,count);
-        return count;
-    }
-
-    void print() const
-    {
-        print(root_);
-    }
-
-    void trim()
-    {
-        trim_tree(root_);
-    }
-
-    void write(std::ostream& out)
-    {
-        char header[16];
-        std::memset(header,0,16);
-        header[0]='m';
-        header[1]='a';
-        header[2]='p';
-        header[3]='n';
-        header[4]='i';
-        header[5]='k';
-        out.write(header,16);
-        write_node(out,root_);
-    }
-
-private:
-
-    void trim_tree(quadtree_node<T>*&  node)
-    {
-        if (node)
-        {
-            for (int i=0;i<4;++i)
-            {
-                trim_tree(node->children_[i]);
-            }
-
-            if (node->num_subnodes()==1 && node->data_.size()==0)
-            {
-                for (int i=0;i<4;++i)
-                {
-                    if (node->children_[i])
-                    {
-                        node=node->children_[i];
-                        break;
-                    }
-                }
-            }
-        }
-    }
-
-    int count_nodes(const quadtree_node<T>*  node) const
-    {
-        if (!node)
-        {
-            return 0;
-        }
-        else
-        {
-            int count = 1;
-            for (int i=0;i<4;++i)
-            {
-                count += count_nodes(node->children_[i]);
-            }
-            return count;
-        }
-    }
-
-    void count_items(const quadtree_node<T>* node,int& count) const
-    {
-        if (node)
-        {
-            count += node->data_.size();
-            for (int i=0;i<4;++i)
-            {
-                count_items(node->children_[i],count);
-            }
-        }
-    }
-
-    int subnode_offset(const quadtree_node<T>* node) const
-    {
-        int offset=0;
-        for (int i=0;i<4;i++)
-        {
-            if (node->children_[i])
-            {
-                offset +=sizeof(box2d<double>)+(node->children_[i]->data_.size()*sizeof(T))+3*sizeof(int);
-                offset +=subnode_offset(node->children_[i]);
-            }
-        }
-        return offset;
-    }
-
-    void write_node(std::ostream& out,const quadtree_node<T>* node) const
-    {
-        if (node)
-        {
-            int offset=subnode_offset(node);
-            int shape_count=node->data_.size();
-            int recsize=sizeof(box2d<double>) + 3 * sizeof(int) + shape_count * sizeof(T);
-            char* node_record=new char[recsize];
-            std::memset(node_record,0,recsize);
-            std::memcpy(node_record,&offset,4);
-            std::memcpy(node_record+4,&node->ext_,sizeof(box2d<double>));
-            std::memcpy(node_record+36,&shape_count,4);
-            for (int i=0;i<shape_count;++i)
-            {
-                memcpy(node_record + 40 + i * sizeof(T),&(node->data_[i]),sizeof(T));
-            }
-            int num_subnodes=0;
-            for (int i=0;i<4;++i)
-            {
-                if (node->children_[i])
-                {
-                    ++num_subnodes;
-                }
-            }
-            std::memcpy(node_record + 40 + shape_count * sizeof(T),&num_subnodes,4);
-            out.write(node_record,recsize);
-            delete [] node_record;
-
-            for (int i=0;i<4;++i)
-            {
-                write_node(out,node->children_[i]);
-            }
-        }
-    }
-
-    void print(const quadtree_node<T>* node,int level=0) const
-    {
-        if (node)
-        {
-            typename std::vector<T>::const_iterator itr=node->data_.begin();
-            std::string pad;
-            for (int i=0;i<level;++i)
-            {
-                pad+=" ";
-            }
-            std::clog<<pad<<"node "<<node<<" extent:"<<node->ext_<<std::endl;
-            std::clog<<pad;
-            while(itr!=node->data_.end())
-            {
-                std::clog<<*itr<<" ";
-                ++itr;
-            }
-            std::clog<<std::endl;
-            for (int i=0;i<4;++i)
-            {
-                print(node->children_[i],level+4);
-            }
-        }
-    }
-
-    void insert(const T& data,const box2d<double>& item_ext,quadtree_node<T>*  node,int maxdepth)
-    {
-        if (node && node->ext_.contains(item_ext))
-        {
-            double width=node->ext_.width();
-            double height=node->ext_.height();
-
-            double lox=node->ext_.minx();
-            double loy=node->ext_.miny();
-            double hix=node->ext_.maxx();
-            double hiy=node->ext_.maxy();
-
-            box2d<double> ext[4];
-            ext[0]=box2d<double>(lox,loy,lox + width * ratio_,loy + height * ratio_);
-            ext[1]=box2d<double>(hix - width * ratio_,loy,hix,loy + height * ratio_);
-            ext[2]=box2d<double>(lox,hiy - height*ratio_,lox + width * ratio_,hiy);
-            ext[3]=box2d<double>(hix - width * ratio_,hiy - height*ratio_,hix,hiy);
-
-            if (maxdepth > 1)
-            {
-                for (int i=0;i<4;++i)
-                {
-                    if (ext[i].contains(item_ext))
-                    {
-                        if (!node->children_[i])
-                        {
-                            node->children_[i]=new quadtree_node<T>(ext[i]);
-                        }
-                        insert(data,item_ext,node->children_[i],maxdepth-1);
-                        return;
-                    }
-                }
-            }
-            node->data_.push_back(data);
-        }
-    }
-};
-#endif //QUADTREE_HPP
diff --git a/utils/shapeindex/shapeindex.cpp b/utils/shapeindex/shapeindex.cpp
index 435daba..6b2324f 100644
--- a/utils/shapeindex/shapeindex.cpp
+++ b/utils/shapeindex/shapeindex.cpp
@@ -25,7 +25,7 @@
 #include <vector>
 #include <string>
 #include <mapnik/util/fs.hpp>
-#include "quadtree.hpp"
+#include <mapnik/quad_tree.hpp>
 #include "shapefile.hpp"
 #include "shape_io.hpp"
 
@@ -73,7 +73,7 @@ int main (int argc,char** argv)
 
         if (vm.count("version"))
         {
-            clog<<"version 0.3.0" <<std::endl;
+            clog << "version 0.3.0" <<std::endl;
             return 1;
         }
 
@@ -153,7 +153,7 @@ int main (int argc,char** argv)
 
         int pos=50;
         shp.seek(pos*2);
-        quadtree<int> tree(extent,depth,ratio);
+        mapnik::quad_tree<int> tree(extent,depth,ratio);
         int count=0;
         while (true) {
 
@@ -240,7 +240,7 @@ int main (int argc,char** argv)
                  << (shapename+".index") << "\"" << endl;
         } else {
             tree.trim();
-            std::clog<<" number nodes="<<tree.count()<<std::endl;
+            std::clog << " number nodes=" << tree.count() << std::endl;
             file.exceptions(std::ios::failbit | std::ios::badbit);
             tree.write(file);
             file.flush();

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