[mapnik] 01/04: Imported Upstream version 3.0.9~rc3+ds

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Tue Nov 24 23:42:44 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 991733b5b18d411a7c87ed94a1310ec6e325e2e9
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Tue Nov 24 23:54:06 2015 +0100

    Imported Upstream version 3.0.9~rc3+ds
---
 CHANGELOG.md                                       |  14 +-
 deps/agg/src/agg_pixfmt_rgba.cpp                   |   8 +-
 deps/boost/gil/extension/toolbox/hsl.hpp           |   4 +-
 deps/boost/gil/extension/toolbox/hsv.hpp           |   4 +-
 include/mapnik/geometry_centroid.hpp               |  50 +++--
 include/mapnik/geometry_is_empty.hpp               |  83 ++++++++-
 include/mapnik/geometry_remove_empty.hpp           |  77 ++++++++
 include/mapnik/image_scaling.hpp                   |  17 +-
 include/mapnik/image_scaling_traits.hpp            |  25 +--
 .../json/feature_collection_grammar_impl.hpp       |  13 +-
 .../renderer_common/process_raster_symbolizer.hpp  |   8 +-
 include/mapnik/simplify_converter.hpp              |  32 ++--
 include/mapnik/span_image_filter.h                 | 178 ++++++++++++++++++
 include/mapnik/warp.hpp                            |  11 +-
 plugins/input/csv/build.py                         |   2 +-
 plugins/input/csv/csv_utils.hpp                    |  26 +--
 plugins/input/geojson/geojson_datasource.cpp       |  46 +++--
 plugins/input/postgis/connection_manager.hpp       |   2 +-
 src/box2d.cpp                                      |  14 +-
 src/grid/grid_renderer.cpp                         |   3 +-
 src/image_scaling.cpp                              |  34 ++--
 src/map.cpp                                        |   2 +-
 src/warp.cpp                                       |  47 +++--
 test/unit/datasource/geojson.cpp                   |  45 ++++-
 test/unit/datasource/shapeindex.cpp                | 120 ++++++++++++
 test/unit/geometry/centroid.cpp                    | 205 +++++++++++++++++++++
 test/unit/geometry/has_empty.cpp                   | 150 +++++++++++++++
 test/unit/geometry/is_empty.cpp                    | 117 ++++++++++++
 test/unit/geometry/label_algo_test.cpp             |  33 ----
 test/unit/geometry/remove_empty.cpp                |  53 ++++++
 utils/mapnik-index/build.py                        |   2 +-
 utils/mapnik-index/mapnik-index.cpp                |  14 +-
 utils/mapnik-index/process_geojson_file.cpp        |  41 ++++-
 utils/mapnik-index/process_geojson_file.hpp        |   2 +-
 utils/shapefile/shapefile_reader.py                |  14 +-
 utils/shapeindex/shapeindex.cpp                    |  44 +++--
 36 files changed, 1343 insertions(+), 197 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 933a72b..d33353a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,11 +10,21 @@ For a complete change history, see the git log.
 
 Released:
 
-(Packaged from )
+(Work-in-progress)
 
 #### Summary
 
- - Fixed offsetting of complex paths and sharp angles (https://github.com/mapnik/mapnik/pull/3160)
+ - Fixed offsetting of complex paths and sharp angles (https://github.com/mapnik/mapnik/pull/3160) (via @winni159)
+ - Fixed mapnik.util.variant issue when compiling with gcc-5.x and SSO enabled by default (https://github.com/mapnik/mapnik/issues/3103) (via @nkovacs)
+ - Fixed issue with complex scripts where some character sequences weren't rendered correctly (https://github.com/mapnik/mapnik/issues/3050) (via @jkroll20)
+ - Revived postgis.input tests
+ - JSON: geometry grammar has been refactored and optimized to have expectation points
+ - Filled missing specializations for value_bool in `mapnik::value` comparison operators
+ - `mapnik.Image` - fixed copy semantics implementation for internal buffer
+ - JSON parsing: unified error_handler across all grammars
+ - Improved unit test coverage
+ - Raster scaling: fixed nodata handling, acurracy when working with small floats and clipping floats by \[0; 255\] (https://github.com/mapnik/mapnik/pull/3147)
+ - Centroid algorithm: fixed invalid input handling, particularly empty geometries (https://github.com/mapnik/mapnik/pull/3185)
 
 ## 3.0.8
 
diff --git a/deps/agg/src/agg_pixfmt_rgba.cpp b/deps/agg/src/agg_pixfmt_rgba.cpp
index 45e0b19..c19e9d6 100644
--- a/deps/agg/src/agg_pixfmt_rgba.cpp
+++ b/deps/agg/src/agg_pixfmt_rgba.cpp
@@ -1,14 +1,8 @@
 
 #include "agg_pixfmt_rgba.h"
 
-// boost
 #pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#pragma GCC diagnostic ignored "-Wc++11-narrowing"
-#pragma GCC diagnostic ignored "-Wunused-local-typedef"
-#pragma GCC diagnostic ignored "-Wshadow"
-#pragma GCC diagnostic ignored "-Wsign-conversion"
-#pragma GCC diagnostic ignored "-Wconversion"
+#include <mapnik/warning_ignore.hpp>
 #include <boost/gil/gil_all.hpp>
 #include <boost/gil/extension/toolbox/hsv.hpp>
 #include <boost/gil/extension/toolbox/hsl.hpp>
diff --git a/deps/boost/gil/extension/toolbox/hsl.hpp b/deps/boost/gil/extension/toolbox/hsl.hpp
index c77dd72..b1b9c1b 100644
--- a/deps/boost/gil/extension/toolbox/hsl.hpp
+++ b/deps/boost/gil/extension/toolbox/hsl.hpp
@@ -15,9 +15,7 @@
 ////////////////////////////////////////////////////////////////////////////////////////
 
 #pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#pragma GCC diagnostic ignored "-Wc++11-narrowing"
-#pragma GCC diagnostic ignored "-Wunused-local-typedef"
+#include <mapnik/warning_ignore.hpp>
 #include <boost/gil/gil_all.hpp>
 #pragma GCC diagnostic pop
 
diff --git a/deps/boost/gil/extension/toolbox/hsv.hpp b/deps/boost/gil/extension/toolbox/hsv.hpp
index 5195cc2..dc6e14e 100644
--- a/deps/boost/gil/extension/toolbox/hsv.hpp
+++ b/deps/boost/gil/extension/toolbox/hsv.hpp
@@ -15,9 +15,7 @@
 ////////////////////////////////////////////////////////////////////////////////////////
 
 #pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#pragma GCC diagnostic ignored "-Wc++11-narrowing"
-#pragma GCC diagnostic ignored "-Wunused-local-typedef"
+#include <mapnik/warning_ignore.hpp>
 #include <boost/gil/gil_all.hpp>
 #pragma GCC diagnostic pop
 
diff --git a/include/mapnik/geometry_centroid.hpp b/include/mapnik/geometry_centroid.hpp
index 71d0c07..590ba48 100644
--- a/include/mapnik/geometry_centroid.hpp
+++ b/include/mapnik/geometry_centroid.hpp
@@ -26,6 +26,8 @@
 #include <mapnik/geometry.hpp>
 #include <mapnik/geometry_adapters.hpp>
 #include <boost/geometry/algorithms/centroid.hpp>
+#include <mapnik/geometry_is_empty.hpp>
+#include <mapnik/geometry_remove_empty.hpp>
 
 namespace mapnik { namespace geometry {
 
@@ -57,40 +59,64 @@ struct geometry_centroid
 
     result_type operator() (point<T> const& geom) const
     {
-        boost::geometry::centroid(geom, pt_);
-        return true;
+        return centroid_simple(geom);
     }
 
     result_type operator() (line_string<T> const& geom) const
     {
-        boost::geometry::centroid(geom, pt_);
-        return true;
+        return centroid_simple(geom);
     }
 
     result_type operator() (polygon<T> const& geom) const
     {
-        boost::geometry::centroid(geom, pt_);
-        return true;
+        return centroid_simple(geom);
     }
 
     result_type operator() (multi_point<T> const& geom) const
     {
-        boost::geometry::centroid(geom, pt_);
-        return true;
+        return centroid_simple(geom);
     }
 
     result_type operator() (multi_line_string<T> const& geom) const
     {
-        boost::geometry::centroid(geom, pt_);
-        return true;
+        return centroid_multi(geom);
     }
 
     result_type operator() (multi_polygon<T> const& geom) const
     {
-        boost::geometry::centroid(geom, pt_);
-        return true;
+        return centroid_multi(geom);
     }
+
     point<T> & pt_;
+
+private:
+    template <typename Geom>
+    result_type centroid_simple(Geom const & geom) const
+    {
+        try
+        {
+            boost::geometry::centroid(geom, pt_);
+            return true;
+        }
+        catch (boost::geometry::centroid_exception const & e)
+        {
+            return false;
+        }
+    }
+
+    template <typename Geom>
+    result_type centroid_multi(Geom const & geom) const
+    {
+// https://github.com/mapnik/mapnik/issues/3169
+#if BOOST_VERSION <= 105900
+        if (mapnik::geometry::has_empty(geom))
+        {
+            Geom stripped = mapnik::geometry::remove_empty(geom);
+            return centroid_simple(stripped);
+        }
+#endif
+        return centroid_simple(geom);
+    }
 };
 
 }
diff --git a/include/mapnik/geometry_is_empty.hpp b/include/mapnik/geometry_is_empty.hpp
index 5d017aa..953bddc 100644
--- a/include/mapnik/geometry_is_empty.hpp
+++ b/include/mapnik/geometry_is_empty.hpp
@@ -72,13 +72,88 @@ struct geometry_is_empty
     }
 
     template <typename T>
-    bool operator() (T const& geom) const
+    bool operator() (T const&) const
     {
         return true;
     }
 
 };
 
+struct geometry_has_empty
+{
+    bool operator() (mapnik::geometry::geometry<double> const& geom) const
+    {
+        return mapnik::util::apply_visitor(*this, geom);
+    }
+
+    bool operator() (mapnik::geometry::geometry_empty const&) const
+    {
+        return false;
+    }
+
+    bool operator() (mapnik::geometry::point<double> const&) const
+    {
+        return false;
+    }
+
+    bool operator() (mapnik::geometry::line_string<double> const&) const
+    {
+        return false;
+    }
+
+    bool operator() (mapnik::geometry::polygon<double> const&) const
+    {
+        return false;
+    }
+
+    bool operator() (mapnik::geometry::multi_point<double> const&) const
+    {
+        return false;
+    }
+
+    bool operator() (mapnik::geometry::multi_line_string<double> const& geom) const
+    {
+        return test_multigeometry(geom);
+    }
+
+    bool operator() (mapnik::geometry::multi_polygon<double> const& geom) const
+    {
+        return test_multigeometry(geom);
+    }
+
+    bool operator() (mapnik::geometry::geometry_collection<double> const& geom) const
+    {
+        for (auto const & item : geom)
+        {
+            if (geometry_is_empty()(item) || (*this)(item))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    template <typename T>
+    bool operator() (T const&) const
+    {
+        return true;
+    }
+
+private:
+    template <typename T>
+    bool test_multigeometry(T const & geom) const
+    {
+        for (auto const & item : geom)
+        {
+            if (item.empty())
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+};
+
 }
 
 template <typename GeomType>
@@ -87,6 +162,12 @@ inline bool is_empty(GeomType const& geom)
     return detail::geometry_is_empty()(geom);
 }
 
+template <typename GeomType>
+inline bool has_empty(GeomType const& geom)
+{
+    return detail::geometry_has_empty()(geom);
+}
+
 }}
 
 #endif // MAPNIK_GEOMETRY_IS_EMPTY_HPP
diff --git a/include/mapnik/geometry_remove_empty.hpp b/include/mapnik/geometry_remove_empty.hpp
new file mode 100644
index 0000000..9838b5a
--- /dev/null
+++ b/include/mapnik/geometry_remove_empty.hpp
@@ -0,0 +1,77 @@
+/*****************************************************************************
+ *
+ * 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_GEOMETRY_REMOVE_EMPTY_HPP
+#define MAPNIK_GEOMETRY_REMOVE_EMPTY_HPP
+
+#include <mapnik/geometry.hpp>
+#include <mapnik/geometry_is_empty.hpp>
+
+namespace mapnik { namespace geometry {
+
+namespace detail {
+
+struct geometry_remove_empty
+{
+    mapnik::geometry::multi_line_string<double> operator() (mapnik::geometry::multi_line_string<double> const & geom) const
+    {
+        return remove_empty(geom);
+    }
+
+    mapnik::geometry::multi_polygon<double> operator() (mapnik::geometry::multi_polygon<double> const & geom) const
+    {
+        return remove_empty(geom);
+    }
+
+    template <typename T>
+    T operator() (T const & geom) const
+    {
+        return geom;
+    }
+
+private:
+    template <typename T>
+    T remove_empty(T const & geom) const
+    {
+        T new_geom;
+        for (auto const & g : geom)
+        {
+            if (!g.empty())
+            {
+                new_geom.emplace_back(g);
+            }
+        }
+        return new_geom;
+    }
+};
+
+}
+
+template <typename GeomType>
+inline GeomType remove_empty(GeomType const& geom)
+{
+    return detail::geometry_remove_empty()(geom);
+}
+
+}}
+
+#endif // MAPNIK_GEOMETRY_REMOVE_EMPTY_HPP
diff --git a/include/mapnik/image_scaling.hpp b/include/mapnik/image_scaling.hpp
index ad4954e..ddae368 100644
--- a/include/mapnik/image_scaling.hpp
+++ b/include/mapnik/image_scaling.hpp
@@ -67,7 +67,22 @@ MAPNIK_DECL void scale_image_agg(T & target, T const& source,
                                  double image_ratio_y,
                                  double x_off_f,
                                  double y_off_f,
-                                 double filter_factor);
+                                 double filter_factor,
+                                 boost::optional<double> const & nodata_value);
+template <typename T>
+inline void scale_image_agg(T & target, T const& source,
+                                 scaling_method_e scaling_method,
+                                 double image_ratio_x,
+                                 double image_ratio_y,
+                                 double x_off_f,
+                                 double y_off_f,
+                                 double filter_factor)
+{
+    scale_image_agg(target, source, scaling_method,
+                    image_ratio_x,image_ratio_y,
+                    x_off_f, y_off_f, filter_factor,
+                    boost::optional<double>());
+}
 }
 
 #endif // MAPNIK_IMAGE_SCALING_HPP
diff --git a/include/mapnik/image_scaling_traits.hpp b/include/mapnik/image_scaling_traits.hpp
index e2d0220..69eec5a 100644
--- a/include/mapnik/image_scaling_traits.hpp
+++ b/include/mapnik/image_scaling_traits.hpp
@@ -23,6 +23,9 @@
 #ifndef MAPNIK_IMAGE_SCALING_TRAITS_HPP
 #define MAPNIK_IMAGE_SCALING_TRAITS_HPP
 
+// mapnik
+#include <mapnik/span_image_filter.h>
+
 // agg
 #include "agg_image_accessors.h"
 #include "agg_pixfmt_rgba.h"
@@ -45,7 +48,7 @@ struct agg_scaling_traits<image_rgba8>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_rgba_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_rgba_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_rgba_affine<img_src_type>;
 
 };
 
@@ -57,7 +60,7 @@ struct agg_scaling_traits<image_gray8>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -68,7 +71,7 @@ struct agg_scaling_traits<image_gray8s>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -79,7 +82,7 @@ struct agg_scaling_traits<image_gray16>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -90,7 +93,7 @@ struct agg_scaling_traits<image_gray16s>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -101,7 +104,7 @@ struct agg_scaling_traits<image_gray32>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -112,7 +115,7 @@ struct agg_scaling_traits<image_gray32s>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -123,7 +126,7 @@ struct agg_scaling_traits<image_gray32f>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -134,7 +137,7 @@ struct agg_scaling_traits<image_gray64>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -145,7 +148,7 @@ struct agg_scaling_traits<image_gray64s>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <>
@@ -156,7 +159,7 @@ struct agg_scaling_traits<image_gray64f>
     using interpolator_type = agg::span_interpolator_linear<>;
     using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
     using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
-    using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
+    using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
 };
 
 template <typename Filter>
diff --git a/include/mapnik/json/feature_collection_grammar_impl.hpp b/include/mapnik/json/feature_collection_grammar_impl.hpp
index 49ee249..b2836af 100644
--- a/include/mapnik/json/feature_collection_grammar_impl.hpp
+++ b/include/mapnik/json/feature_collection_grammar_impl.hpp
@@ -54,24 +54,23 @@ feature_collection_grammar<Iterator,FeatureType, FeatureCallback,ErrorHandler>::
         start = feature_collection(_r1, _r2, _r3)
             ;
 
-        feature_collection = lit('{') >> (type | features(_r1, _r2, _r3) | feature_g.json_.key_value) % lit(',') >> lit('}')
+        feature_collection = lit('{') > (type | features(_r1, _r2, _r3) | feature_g.json_.key_value) % lit(',') > lit('}')
             ;
 
-        type = lit("\"type\"") >> lit(':') >> lit("\"FeatureCollection\"")
+        type = lit("\"type\"") > lit(':') > lit("\"FeatureCollection\"")
             ;
 
         features = lit("\"features\"")
-            >> lit(':')
-            >> lit('[')
-            >> -(feature(_r1, _r2, _r3) [_r2 +=1] % lit(','))
-            >> lit(']')
+            > lit(':') > lit('[') >
+            ( lit(']') | ((feature(_r1, _r2, _r3) [_r2 +=1] % lit(',')) > lit(']')))
             ;
 
         feature = eps[_a = phoenix::construct<mapnik::feature_ptr>(new_<mapnik::feature_impl>(_r1, _r2))]
-            >> feature_g(*_a)[on_feature(_r3,_a)]
+            > feature_g(*_a)[on_feature(_r3,_a)]
             ;
 
         start.name("start");
+        feature_collection.name("FeatureCollection");
         type.name("type");
         features.name("features");
         feature.name("feature");
diff --git a/include/mapnik/renderer_common/process_raster_symbolizer.hpp b/include/mapnik/renderer_common/process_raster_symbolizer.hpp
index 5646785..f8ce32d 100644
--- a/include/mapnik/renderer_common/process_raster_symbolizer.hpp
+++ b/include/mapnik/renderer_common/process_raster_symbolizer.hpp
@@ -77,7 +77,7 @@ struct image_dispatcher
         if (need_scaling_)
         {
             image_rgba8 data_out(width_, height_, true, true);
-            scale_image_agg(data_out, data_in,  method_, scale_x_, scale_y_, 0.0, 0.0, filter_factor_);
+            scale_image_agg(data_out, data_in,  method_, scale_x_, scale_y_, 0.0, 0.0, filter_factor_, nodata_);
             composite_(data_out, comp_op_, opacity_, start_x_, start_y_);
         }
         else
@@ -95,7 +95,7 @@ struct image_dispatcher
         if (need_scaling_)
         {
             image_type data_out(width_, height_);
-            scale_image_agg(data_out, data_in,  method_, scale_x_, scale_y_, 0.0, 0.0, filter_factor_);
+            scale_image_agg(data_out, data_in,  method_, scale_x_, scale_y_, 0.0, 0.0, filter_factor_, nodata_);
             if (colorizer) colorizer->colorize(dst, data_out, nodata_, feature_);
         }
         else
@@ -157,7 +157,7 @@ struct image_warp_dispatcher
     void operator() (image_rgba8 const& data_in) const
     {
         image_rgba8 data_out(width_, height_, true, true);
-        warp_image(data_out, data_in, prj_trans_, target_ext_, source_ext_, offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_);
+        warp_image(data_out, data_in, prj_trans_, target_ext_, source_ext_, offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_, nodata_);
         composite_(data_out, comp_op_, opacity_, start_x_, start_y_);
     }
 
@@ -167,7 +167,7 @@ struct image_warp_dispatcher
         using image_type = T;
         image_type data_out(width_, height_);
         if (nodata_) data_out.set(*nodata_);
-        warp_image(data_out, data_in, prj_trans_, target_ext_, source_ext_, offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_);
+        warp_image(data_out, data_in, prj_trans_, target_ext_, source_ext_, offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_, nodata_);
         image_rgba8 dst(width_, height_);
         raster_colorizer_ptr colorizer = get<raster_colorizer_ptr>(sym_, keys::colorizer);
         if (colorizer) colorizer->colorize(dst, data_out, nodata_, feature_);
diff --git a/include/mapnik/simplify_converter.hpp b/include/mapnik/simplify_converter.hpp
index f107a8d..77d3674 100644
--- a/include/mapnik/simplify_converter.hpp
+++ b/include/mapnik/simplify_converter.hpp
@@ -106,7 +106,7 @@ public:
         initial,
         process,
         closing,
-        end,
+        done,
         cache
     };
 
@@ -210,7 +210,7 @@ private:
         if (status_ == closing)
         {
             *x = *y = 0.0;
-            status_ = end;
+            status_ = done;
             return SEG_CLOSE;
         }
 
@@ -237,7 +237,7 @@ private:
                 {
                     // The previous vertex was already output in the previous call.
                     // We can now safely output SEG_CLOSE.
-                    status_ = end;
+                    status_ = done;
                 }
                 else
                 {
@@ -268,11 +268,11 @@ private:
     }
 
     template <typename Iterator>
-    bool fit_sleeve(Iterator itr, Iterator itr_end, vertex2d const& v)
+    bool fit_sleeve(Iterator itr, Iterator end, vertex2d const& v)
     {
         sleeve s(*itr,v,tolerance_);
         ++itr; // skip first vertex
-        for (; itr!=itr_end; ++itr)
+        for (; itr != end; ++itr)
         {
             if (!s.inside(*itr))
             {
@@ -486,14 +486,14 @@ private:
         return status_ = process;
     }
 
-    void RDP(std::vector<vertex2d>& vertices, const size_t start, const size_t end)
+    void RDP(std::vector<vertex2d>& vertices, const size_t first, const size_t last)
     {
         // Squared length of a vector
         auto sqlen = [] (vertex2d const& vec) { return vec.x*vec.x + vec.y*vec.y; };
         // Compute square distance of p to a line segment
         auto segment_distance = [&sqlen] (vertex2d const& p, vertex2d const& a, vertex2d const& b, vertex2d const& dir, double dir_sq_len)
         {
-            // Special case where segment has same start and end point at which point we are just doing a radius check
+            // Special case where segment has same first and last point at which point we are just doing a radius check
             if (dir_sq_len == 0)
             {
                 return sqlen(vertex2d(p.x - b.x, p.y - b.y, SEG_END));
@@ -525,15 +525,15 @@ private:
         };
 
         // Compute the directional vector along the segment
-        vertex2d dir = vertex2d(vertices[end].x - vertices[start].x, vertices[end].y - vertices[start].y, SEG_END);
+        vertex2d dir = vertex2d(vertices[last].x - vertices[first].x, vertices[last].y - vertices[first].y, SEG_END);
         double dir_sq_len = sqlen(dir);
 
         // Find the point with the maximum distance from this line segment
         double max = std::numeric_limits<double>::min();
         size_t keeper = 0;
-        for (size_t i = start + 1; i < end; ++i)
+        for (size_t i = first + 1; i < last; ++i)
         {
-            double d = segment_distance(vertices[i], vertices[start], vertices[end], dir, dir_sq_len);
+            double d = segment_distance(vertices[i], vertices[first], vertices[last], dir, dir_sq_len);
             if (d > max)
             {
                 keeper = i;
@@ -546,19 +546,19 @@ private:
         if (max > tolerance_ * tolerance_)
         {
             // Make sure not to smooth out the biggest outlier (keeper)
-            if (keeper - start != 1)
+            if (keeper - first != 1)
             {
-                RDP(vertices, start, keeper);
+                RDP(vertices, first, keeper);
             }
-            if (end - keeper != 1)
+            if (last - keeper != 1)
             {
-                RDP(vertices, keeper, end);
+                RDP(vertices, keeper, last);
             }
-        }// Everyone between the start and the end was close enough to the line
+        }// Everyone between the first and the last was close enough to the line
         else
         {
             // Mark each of them as discarded
-            for (size_t i = start + 1; i < end; ++i)
+            for (size_t i = first + 1; i < last; ++i)
             {
                 vertices[i].cmd = SEG_END;
             }
diff --git a/include/mapnik/span_image_filter.h b/include/mapnik/span_image_filter.h
new file mode 100644
index 0000000..cd84301
--- /dev/null
+++ b/include/mapnik/span_image_filter.h
@@ -0,0 +1,178 @@
+/*****************************************************************************
+ *
+ * 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_SPAN_IMAGE_FILTER_INCLUDED
+#define MAPNIK_SPAN_IMAGE_FILTER_INCLUDED
+
+#include "agg_span_image_filter_gray.h"
+#include "agg_span_image_filter_rgba.h"
+
+#include <limits>
+
+namespace mapnik
+{
+
+template<class Source>
+class span_image_resample_gray_affine : public agg::span_image_resample_affine<Source>
+{
+public:
+    using source_type = Source;
+    using color_type = typename source_type::color_type;
+    using base_type = agg::span_image_resample_affine<source_type>;
+    using interpolator_type = typename base_type::interpolator_type;
+    using value_type = typename color_type::value_type;
+    using long_type = typename color_type::long_type;
+
+    enum base_scale_e
+    {
+        downscale_shift = agg::image_filter_shift,
+        base_mask       = color_type::base_mask
+    };
+
+    span_image_resample_gray_affine(source_type & src,
+                                    interpolator_type & inter,
+                                    agg::image_filter_lut const & filter,
+                                    boost::optional<value_type> const & nodata_value) :
+        base_type(src, inter, filter),
+        nodata_value_(nodata_value)
+    { }
+
+    void generate(color_type* span, int x, int y, unsigned len)
+    {
+        base_type::interpolator().begin(x + base_type::filter_dx_dbl(),
+                                        y + base_type::filter_dy_dbl(), len);
+
+        long_type fg;
+
+        int diameter     = base_type::filter().diameter();
+        int filter_scale = diameter << agg::image_subpixel_shift;
+        int radius_x     = (diameter * base_type::m_rx) >> 1;
+        int radius_y     = (diameter * base_type::m_ry) >> 1;
+        int len_x_lr     =
+            (diameter * base_type::m_rx + agg::image_subpixel_mask) >>
+                agg::image_subpixel_shift;
+
+        const agg::int16* weight_array = base_type::filter().weight_array();
+
+        do
+        {
+            base_type::interpolator().coordinates(&x, &y);
+
+            int src_x = x >> agg::image_subpixel_shift;
+            int src_y = y >> agg::image_subpixel_shift;
+            const value_type* pix = reinterpret_cast<const value_type*>(base_type::source().span(src_x, src_y, 0));
+            if (nodata_value_ && *nodata_value_ == *pix)
+            {
+                span->v = *nodata_value_;
+                span->a = base_mask;
+                ++span;
+                ++base_type::interpolator();
+                continue;
+            }
+
+            x += base_type::filter_dx_int() - radius_x;
+            y += base_type::filter_dy_int() - radius_y;
+
+            fg = 0;
+
+            int y_lr = y >> agg::image_subpixel_shift;
+            int y_hr = ((agg::image_subpixel_mask - (y & agg::image_subpixel_mask)) *
+                            base_type::m_ry_inv) >>
+                                agg::image_subpixel_shift;
+            int total_weight = 0;
+            int x_lr = x >> agg::image_subpixel_shift;
+            int x_hr = ((agg::image_subpixel_mask - (x & agg::image_subpixel_mask)) *
+                            base_type::m_rx_inv) >>
+                                agg::image_subpixel_shift;
+
+            int x_hr2 = x_hr;
+            const value_type* fg_ptr = reinterpret_cast<const value_type*>(base_type::source().span(x_lr, y_lr, len_x_lr));
+            for(;;)
+            {
+                int weight_y = weight_array[y_hr];
+                x_hr = x_hr2;
+                for(;;)
+                {
+                    int weight = (weight_y * weight_array[x_hr] +
+                                 agg::image_filter_scale) >>
+                                 downscale_shift;
+                    if (!nodata_value_ || *nodata_value_ != *fg_ptr)
+                    {
+                        fg += *fg_ptr * weight;
+                        total_weight += weight;
+                    }
+                    x_hr  += base_type::m_rx_inv;
+                    if (x_hr >= filter_scale) break;
+                    fg_ptr = reinterpret_cast<const value_type*>(base_type::source().next_x());
+                }
+                y_hr += base_type::m_ry_inv;
+                if (y_hr >= filter_scale) break;
+                fg_ptr = reinterpret_cast<const value_type*>(base_type::source().next_y());
+            }
+
+            fg /= total_weight;
+            if (fg < std::numeric_limits<value_type>::min())
+            {
+                span->v = std::numeric_limits<value_type>::min();
+            }
+            else if (fg > std::numeric_limits<value_type>::max())
+            {
+                span->v = std::numeric_limits<value_type>::max();
+            }
+            else
+            {
+                span->v = static_cast<value_type>(fg);
+            }
+            span->a = base_mask;
+
+            ++span;
+            ++base_type::interpolator();
+        } while(--len);
+    }
+
+private:
+    boost::optional<value_type> nodata_value_;
+};
+
+template<class Source>
+class span_image_resample_rgba_affine : public agg::span_image_resample_rgba_affine<Source>
+{
+public:
+    using source_type = Source;
+    using color_type = typename source_type::color_type;
+    using order_type = typename source_type::order_type;
+    using base_type = agg::span_image_resample_rgba_affine<source_type>;
+    using interpolator_type = typename base_type::interpolator_type;
+    using value_type = typename color_type::value_type;
+    using long_type = typename color_type::long_type;
+
+    span_image_resample_rgba_affine(source_type & src,
+                                    interpolator_type & inter,
+                                    agg::image_filter_lut const & filter,
+                                    boost::optional<value_type> const & nodata_value) :
+        agg::span_image_resample_rgba_affine<Source>(src, inter, filter)
+    { }
+};
+
+}
+
+#endif
diff --git a/include/mapnik/warp.hpp b/include/mapnik/warp.hpp
index 0bf3332..abf0108 100644
--- a/include/mapnik/warp.hpp
+++ b/include/mapnik/warp.hpp
@@ -38,13 +38,20 @@ MAPNIK_DECL void reproject_and_scale_raster(raster & target,
                                             proj_transform const& prj_trans,
                                             double offset_x, double offset_y,
                                             unsigned mesh_size,
-                                            scaling_method_e scaling_method);
+                                            scaling_method_e scaling_method,
+                                            boost::optional<double> const & nodata_value);
 
+MAPNIK_DECL void reproject_and_scale_raster(raster & target, raster const& source,
+                                            proj_transform const& prj_trans,
+                                            double offset_x, double offset_y,
+                                            unsigned mesh_size,
+                                            scaling_method_e scaling_method);
 
 template <typename T>
 MAPNIK_DECL void warp_image (T & target, T const& source, proj_transform const& prj_trans,
                              box2d<double> const& target_ext, box2d<double> const& source_ext,
-                             double offset_x, double offset_y, unsigned mesh_size, scaling_method_e scaling_method, double filter_factor);
+                             double offset_x, double offset_y, unsigned mesh_size, scaling_method_e scaling_method, double filter_factor,
+                             boost::optional<double> const & nodata_value);
 }
 
 #endif // MAPNIK_WARP_HPP
diff --git a/plugins/input/csv/build.py b/plugins/input/csv/build.py
index f21c93f..edc3ae0 100644
--- a/plugins/input/csv/build.py
+++ b/plugins/input/csv/build.py
@@ -29,7 +29,7 @@ if env.get('BOOST_LIB_VERSION_FROM_HEADER'):
         can_build = True
 
 if not can_build:
-    print 'WARNING: skipping building the optional topojson datasource plugin which requires boost >= 1.56'
+    print 'WARNING: skipping building the optional CSV datasource plugin which requires boost >= 1.56'
 else:
     Import ('plugin_base')
 
diff --git a/plugins/input/csv/csv_utils.hpp b/plugins/input/csv/csv_utils.hpp
index 43da304..b820773 100644
--- a/plugins/input/csv/csv_utils.hpp
+++ b/plugins/input/csv/csv_utils.hpp
@@ -177,28 +177,30 @@ std::tuple<char,bool,char> autodect_newline_and_quote(T & stream, std::size_t fi
     // autodetect newlines
     char newline = '\n';
     bool has_newline = false;
-    char quote = '"';
     bool has_quote = false;
+    char quote = '"';
     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)
     {
         char c = static_cast<char>(stream.get());
-        if (c == '\r')
+        switch (c)
         {
+        case '\r':
             newline = '\r';
             has_newline = true;
-            //break;
-        }
-        if (c == '\n')
-        {
+            break;
+        case '\n':
             has_newline = true;
-            //break;
-        }
-        else if (!has_quote && c == '\'')
-        {
-            quote = '\'';
-            has_quote = true;
+            break;
+        case '\'':
+        case '"':
+            if (!has_quote)
+            {
+                quote = c;
+                has_quote = true;
+            }
+            break;
         }
     }
     return std::make_tuple(newline, has_newline, quote);
diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp
index b46bf5b..dbb80f9 100644
--- a/plugins/input/geojson/geojson_datasource.cpp
+++ b/plugins/input/geojson/geojson_datasource.cpp
@@ -345,28 +345,40 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end)
 template <typename Iterator>
 void geojson_datasource::parse_geojson(Iterator start, Iterator end)
 {
+    using boost::spirit::qi::expectation_failure;
     boost::spirit::standard::space_type space;
     mapnik::context_ptr ctx = std::make_shared<mapnik::context_type>();
     std::size_t start_id = 1;
 
     mapnik::json::default_feature_callback callback(features_);
     Iterator itr = start;
-    bool result = boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_fc_grammar)
-                                                  (boost::phoenix::ref(ctx),boost::phoenix::ref(start_id), boost::phoenix::ref(callback)),
-                                                  space);
-    if (!result || itr != end)
+
+    try
     {
-        if (!inline_string_.empty()) throw mapnik::datasource_exception("geojson_datasource: Failed to parse GeoJSON file from in-memory string");
-        else throw mapnik::datasource_exception("geojson_datasource: Failed to parse GeoJSON file '" + filename_ + "'");
+        bool result = boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_fc_grammar)
+                                                      (boost::phoenix::ref(ctx),boost::phoenix::ref(start_id), boost::phoenix::ref(callback)),
+                                                      space);
+        if (!result || itr != end)
+        {
+            itr = start;
+            // try parsing as single Feature or single Geometry JSON
+            result = boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_feature_callback_grammar)
+                                                     (boost::phoenix::ref(ctx),boost::phoenix::ref(start_id), boost::phoenix::ref(callback)),
+                                                     space);
+            if (!result || itr != end)
+            {
+                if (!inline_string_.empty()) throw mapnik::datasource_exception("geojson_datasource: Failed parse GeoJSON file from in-memory string");
+                else throw mapnik::datasource_exception("geojson_datasource: Failed parse GeoJSON file '" + filename_ + "'");
+            }
+        }
     }
-
-    if (features_.size() == 0)
+    catch (expectation_failure<char const*> const& ex)
     {
         itr = start;
         // try parsing as single Feature or single Geometry JSON
-        result = boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_feature_callback_grammar)
-                                                 (boost::phoenix::ref(ctx),boost::phoenix::ref(start_id), boost::phoenix::ref(callback)),
-                                                 space);
+        bool result = boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_feature_callback_grammar)
+                                                      (boost::phoenix::ref(ctx),boost::phoenix::ref(start_id), boost::phoenix::ref(callback)),
+                                                      space);
         if (!result || itr != end)
         {
             if (!inline_string_.empty()) throw mapnik::datasource_exception("geojson_datasource: Failed parse GeoJSON file from in-memory string");
@@ -555,18 +567,18 @@ mapnik::featureset_ptr geojson_datasource::features(mapnik::query const& q) cons
         if (tree_)
         {
             tree_->query(boost::geometry::index::intersects(box),std::back_inserter(index_array));
-
+            // sort index array to preserve original feature ordering in GeoJSON
+            std::sort(index_array.begin(),index_array.end(),
+                      [] (item_type const& item0, item_type const& item1)
+                      {
+                          return item0.second.first < item1.second.first;
+                      });
             if (cache_features_)
             {
                 return std::make_shared<geojson_featureset>(features_, std::move(index_array));
             }
             else
             {
-                std::sort(index_array.begin(),index_array.end(),
-                          [] (item_type const& item0, item_type const& item1)
-                          {
-                              return item0.second.first < item1.second.first;
-                          });
                 return std::make_shared<geojson_memory_index_featureset>(filename_, std::move(index_array));
             }
         }
diff --git a/plugins/input/postgis/connection_manager.hpp b/plugins/input/postgis/connection_manager.hpp
index 37c547f..73656cb 100644
--- a/plugins/input/postgis/connection_manager.hpp
+++ b/plugins/input/postgis/connection_manager.hpp
@@ -113,7 +113,7 @@ private:
 
 public:
 
-    bool registerPool(const ConnectionCreator<Connection>& creator,unsigned initialSize,unsigned maxSize)
+    bool registerPool(ConnectionCreator<Connection> const& creator,unsigned initialSize,unsigned maxSize)
     {
         ContType::const_iterator itr = pools_.find(creator.id());
 
diff --git a/src/box2d.cpp b/src/box2d.cpp
index 0e60a38..26e6929 100644
--- a/src/box2d.cpp
+++ b/src/box2d.cpp
@@ -53,7 +53,10 @@ namespace mapnik
 {
 template <typename T>
 box2d<T>::box2d()
-    :minx_(0),miny_(0),maxx_(-1),maxy_(-1) {}
+    :minx_( std::numeric_limits<T>::max()),
+     miny_( std::numeric_limits<T>::max()),
+     maxx_(-std::numeric_limits<T>::max()),
+     maxy_(-std::numeric_limits<T>::max()) {}
 
 template <typename T>
 box2d<T>::box2d(T minx,T miny,T maxx,T maxy)
@@ -264,15 +267,16 @@ bool box2d<T>::intersects(box2d<T> const& other) const
 template <typename T>
 box2d<T> box2d<T>::intersect(box2d_type const& other) const
 {
-    if (intersects(other)) {
+    if (intersects(other))
+    {
         T x0=std::max(minx_,other.minx_);
         T y0=std::max(miny_,other.miny_);
-
         T x1=std::min(maxx_,other.maxx_);
         T y1=std::min(maxy_,other.maxy_);
-
         return box2d<T>(x0,y0,x1,y1);
-    } else {
+    }
+    else
+    {
         return box2d<T>();
     }
 }
diff --git a/src/grid/grid_renderer.cpp b/src/grid/grid_renderer.cpp
index 93afb5b..33d10ee 100644
--- a/src/grid/grid_renderer.cpp
+++ b/src/grid/grid_renderer.cpp
@@ -197,12 +197,13 @@ struct grid_render_marker_visitor
         else
         {
             image_rgba8 target(data.width(), data.height());
+            boost::optional<double> nodata;
             mapnik::scale_image_agg(target,
                                     data,
                                     SCALING_NEAR,
                                     1,
                                     1,
-                                    0.0, 0.0, 1.0); // TODO: is 1.0 a valid default here, and do we even care in grid_renderer what the image looks like?
+                                    0.0, 0.0, 1.0, nodata); // TODO: is 1.0 a valid default here, and do we even care in grid_renderer what the image looks like?
             pixmap_.set_rectangle(feature_.id(), target,
                                   boost::math::iround(pos_.x - cx),
                                   boost::math::iround(pos_.y - cy));
diff --git a/src/image_scaling.cpp b/src/image_scaling.cpp
index 63bb88c..f0cc450 100644
--- a/src/image_scaling.cpp
+++ b/src/image_scaling.cpp
@@ -24,8 +24,6 @@
 #include <mapnik/image.hpp>
 #include <mapnik/image_scaling.hpp>
 #include <mapnik/image_scaling_traits.hpp>
-// does not handle alpha correctly
-//#include <mapnik/span_image_filter.hpp>
 
 // boost
 #pragma GCC diagnostic push
@@ -100,7 +98,7 @@ boost::optional<std::string> scaling_method_to_string(scaling_method_e scaling_m
 template <typename T>
 void scale_image_agg(T & target, T const& source, scaling_method_e scaling_method,
                      double image_ratio_x, double image_ratio_y, double x_off_f, double y_off_f,
-                     double filter_factor)
+                     double filter_factor, boost::optional<double> const & nodata_value)
 {
     // "the image filters should work namely in the premultiplied color space"
     // http://old.nabble.com/Re:--AGG--Basic-image-transformations-p1110665.html
@@ -158,42 +156,46 @@ void scale_image_agg(T & target, T const& source, scaling_method_e scaling_metho
         using span_gen_type = typename detail::agg_scaling_traits<image_type>::span_image_resample_affine;
         agg::image_filter_lut filter;
         detail::set_scaling_method(filter, scaling_method, filter_factor);
-        span_gen_type sg(img_src, interpolator, filter);
+        boost::optional<typename span_gen_type::value_type> nodata;
+        if (nodata_value)
+        {
+            nodata = nodata_value;
+        }
+        span_gen_type sg(img_src, interpolator, filter, nodata);
         agg::render_scanlines_aa(ras, sl, rb_dst_pre, sa, sg);
     }
-
 }
 
 template MAPNIK_DECL void scale_image_agg(image_rgba8 &, image_rgba8 const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray8 &, image_gray8 const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray8s &, image_gray8s const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray16 &, image_gray16 const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray16s &, image_gray16s const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray32 &, image_gray32 const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray32s &, image_gray32s const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray32f &, image_gray32f const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray64 &, image_gray64 const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray64s &, image_gray64s const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 
 template MAPNIK_DECL void scale_image_agg(image_gray64f &, image_gray64f const&, scaling_method_e,
-                              double, double , double, double , double);
+                              double, double , double, double , double, boost::optional<double> const &);
 }
diff --git a/src/map.cpp b/src/map.cpp
index 81ad147..c44735c 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -580,7 +580,7 @@ void Map::zoom_all()
 
 void Map::zoom_to_box(box2d<double> const& box)
 {
-    current_extent_=box;
+    current_extent_= box;
     fixAspectRatio();
 }
 
diff --git a/src/warp.cpp b/src/warp.cpp
index 5729748..b250d6e 100644
--- a/src/warp.cpp
+++ b/src/warp.cpp
@@ -51,7 +51,8 @@ namespace mapnik {
 template <typename T>
 MAPNIK_DECL void warp_image (T & target, T const& source, proj_transform const& prj_trans,
                  box2d<double> const& target_ext, box2d<double> const& source_ext,
-                 double offset_x, double offset_y, unsigned mesh_size, scaling_method_e scaling_method, double filter_factor)
+                 double offset_x, double offset_y, unsigned mesh_size, scaling_method_e scaling_method, double filter_factor,
+                 boost::optional<double> const & nodata_value)
 {
     using image_type = T;
     using pixel_type = typename image_type::pixel_type;
@@ -147,7 +148,12 @@ MAPNIK_DECL void warp_image (T & target, T const& source, proj_transform const&
                     using span_gen_type = typename detail::agg_scaling_traits<image_type>::span_image_resample_affine;
                     agg::image_filter_lut filter;
                     detail::set_scaling_method(filter, scaling_method, filter_factor);
-                    span_gen_type sg(ia, interpolator, filter);
+                    boost::optional<typename span_gen_type::value_type> nodata;
+                    if (nodata_value)
+                    {
+                        nodata = nodata_value;
+                    }
+                    span_gen_type sg(ia, interpolator, filter, nodata);
                     agg::render_scanlines_bin(rasterizer, scanline, rb, sa, sg);
                 }
             }
@@ -162,7 +168,8 @@ struct warp_image_visitor
 {
     warp_image_visitor (raster & target_raster, proj_transform const& prj_trans, box2d<double> const& source_ext,
                         double offset_x, double offset_y, unsigned mesh_size,
-                        scaling_method_e scaling_method, double filter_factor)
+                        scaling_method_e scaling_method, double filter_factor,
+                        boost::optional<double> const & nodata_value)
         : target_raster_(target_raster),
           prj_trans_(prj_trans),
           source_ext_(source_ext),
@@ -170,7 +177,9 @@ struct warp_image_visitor
           offset_y_(offset_y),
           mesh_size_(mesh_size),
           scaling_method_(scaling_method),
-        filter_factor_(filter_factor) {}
+          filter_factor_(filter_factor),
+          nodata_value_(nodata_value)
+    {}
 
     void operator() (image_null const&) {}
 
@@ -183,7 +192,7 @@ struct warp_image_visitor
         {
             image_type & target = util::get<image_type>(target_raster_.data_);
             warp_image (target, source, prj_trans_, target_raster_.ext_, source_ext_,
-                        offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_);
+                        offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_, nodata_value_);
         }
     }
 
@@ -195,6 +204,7 @@ struct warp_image_visitor
     unsigned mesh_size_;
     scaling_method_e scaling_method_;
     double filter_factor_;
+    boost::optional<double> const & nodata_value_;
 };
 
 }
@@ -203,24 +213,39 @@ void reproject_and_scale_raster(raster & target, raster const& source,
                                 proj_transform const& prj_trans,
                                 double offset_x, double offset_y,
                                 unsigned mesh_size,
-                                scaling_method_e scaling_method)
+                                scaling_method_e scaling_method,
+                                boost::optional<double> const & nodata_value)
 {
     detail::warp_image_visitor warper(target, prj_trans, source.ext_, offset_x, offset_y, mesh_size,
-                                      scaling_method, source.get_filter_factor());
+                                      scaling_method, source.get_filter_factor(), nodata_value);
     util::apply_visitor(warper, source.data_);
 }
 
+void reproject_and_scale_raster(raster & target, raster const& source,
+                                            proj_transform const& prj_trans,
+                                            double offset_x, double offset_y,
+                                            unsigned mesh_size,
+                                            scaling_method_e scaling_method)
+{
+    reproject_and_scale_raster(target, source, prj_trans,
+                               offset_x, offset_y,
+                               mesh_size,
+                               scaling_method,
+                               boost::optional<double>());
+}
+
+
 template MAPNIK_DECL void warp_image (image_rgba8&, image_rgba8 const&, proj_transform const&,
-                                      box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double);
+                                      box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double, boost::optional<double> const &);
 
 template MAPNIK_DECL void warp_image (image_gray8&, image_gray8 const&, proj_transform const&,
-                                      box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double);
+                                      box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double, boost::optional<double> const &);
 
 template MAPNIK_DECL void warp_image (image_gray16&, image_gray16 const&, proj_transform const&,
-                                      box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double);
+                                      box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double, boost::optional<double> const &);
 
 template MAPNIK_DECL void warp_image (image_gray32f&, image_gray32f const&, proj_transform const&,
-                                      box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double);
+                                      box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double, boost::optional<double> const &);
 
 
 }// namespace mapnik
diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp
index 785d669..ad1f2d9 100644
--- a/test/unit/datasource/geojson.cpp
+++ b/test/unit/datasource/geojson.cpp
@@ -446,7 +446,7 @@ TEST_CASE("geojson") {
                         "./test/data/json/feature-malformed-3.geojson"})
             {
                 std::string filename(c_str);
-                params["file"] = filename; // mismatched parentheses
+                params["file"] = filename;
 
                 // cleanup in the case of a failed previous run
                 if (mapnik::util::exists(filename + ".index"))
@@ -468,6 +468,7 @@ TEST_CASE("geojson") {
 
                     for (auto cache_features : {true, false})
                     {
+                        params["cache_features"] = cache_features;
                         CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
                     }
 
@@ -480,6 +481,48 @@ TEST_CASE("geojson") {
             }
         }
 
+        SECTION("GeoJSON ensure mapnik::featureset::next() throws on malformed input")
+        {
+            std::string filename{"./test/data/json/featurecollection-malformed.json"};
+            mapnik::parameters params;
+            params["type"] = "geojson";
+            params["file"] = filename;
+
+            // cleanup in the case of a failed previous run
+            if (mapnik::util::exists(filename + ".index"))
+            {
+                mapnik::util::remove(filename + ".index");
+            }
+
+            CHECK(!mapnik::util::exists(filename + ".index"));
+            int ret = create_disk_index(filename);
+            int ret_posix = (ret >> 8) & 0x000000ff;
+            INFO(ret);
+            INFO(ret_posix);
+            CHECK(mapnik::util::exists(filename + ".index"));
+
+            for (auto cache_features : {true,false})
+            {
+                params["cache_features"] = false;
+                auto ds = mapnik::datasource_cache::instance().create(params);
+                auto fields = ds->get_descriptor().get_descriptors();
+                mapnik::query query(ds->envelope());
+                auto features = ds->features(query);
+                REQUIRE_THROWS(
+                    auto feature = features->next();
+                    while (feature != nullptr)
+                    {
+                        feature = features->next();
+                    });
+            }
+
+            // cleanup
+            if (mapnik::util::exists(filename + ".index"))
+            {
+                mapnik::util::remove(filename + ".index");
+            }
+        }
+
         SECTION("GeoJSON ensure input fully consumed and throw exception otherwise")
         {
             mapnik::parameters params;
diff --git a/test/unit/datasource/shapeindex.cpp b/test/unit/datasource/shapeindex.cpp
new file mode 100644
index 0000000..e169678
--- /dev/null
+++ b/test/unit/datasource/shapeindex.cpp
@@ -0,0 +1,120 @@
+/*****************************************************************************
+ *
+ * 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 "catch.hpp"
+
+#include <mapnik/datasource.hpp>
+#include <mapnik/datasource_cache.hpp>
+#include <mapnik/util/fs.hpp>
+#include <cstdlib>
+#pragma GCC diagnostic push
+#include <mapnik/warning_ignore.hpp>
+#include <boost/algorithm/string.hpp>
+#pragma GCC diagnostic pop
+
+namespace {
+
+std::size_t count_shapefile_features(std::string const& filename)
+{
+    mapnik::parameters params;
+    params["type"] = "shape";
+    params["file"] = filename;
+    auto ds = mapnik::datasource_cache::instance().create(params);
+    CHECK(ds->type() == mapnik::datasource::datasource_t::Vector);
+    auto fields = ds->get_descriptor().get_descriptors();
+    mapnik::query query(ds->envelope());
+    for (auto const& field : fields)
+    {
+        query.add_property_name(field.get_name());
+    }
+    auto features = ds->features(query);
+
+    std::size_t feature_count = 0;
+    auto feature = features->next();
+    while (feature)
+    {
+        ++feature_count;
+        feature = features->next();
+    }
+    return feature_count;
+}
+
+int create_shapefile_index(std::string const& filename, bool silent = true)
+{
+    std::string cmd;
+    if (std::getenv("DYLD_LIBRARY_PATH") != nullptr)
+    {
+        cmd += std::string("export DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " && ";
+    }
+    cmd += "shapeindex " + filename;
+    if (silent)
+    {
+#ifndef _WINDOWS
+        cmd += " 2>/dev/null";
+#else
+        cmd += " 2> nul";
+#endif
+    }
+    return std::system(cmd.c_str());
+}
+
+}
+
+TEST_CASE("shapeindex")
+{
+    std::string shape_plugin("./plugins/input/shape.input");
+    if (mapnik::util::exists(shape_plugin))
+    {
+        SECTION("Shapefile index")
+        {
+            for (auto const& path : mapnik::util::list_directory("test/data/shp/"))
+            {
+                if (boost::iends_with(path,".shp"))
+                {
+                    std::string index_path = path.substr(0, path.rfind(".")) + ".index";
+                    // remove *.index if present
+                    if (mapnik::util::exists(index_path))
+                    {
+                        mapnik::util::remove(index_path);
+                    }
+                    // count features
+                    std::size_t feature_count = count_shapefile_features(path);
+                    // create *.index
+                    create_shapefile_index(path);
+                    if (feature_count == 0)
+                    {
+                        REQUIRE(!mapnik::util::exists(index_path)); // index won't be created if there's no features
+                    }
+                    // count features
+                    std::size_t feature_count_indexed = count_shapefile_features(path);
+                    // ensure number of features are the same
+                    REQUIRE(feature_count == feature_count_indexed);
+                    // remove *.index if present
+                    if (mapnik::util::exists(index_path))
+                    {
+                        mapnik::util::remove(index_path);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/test/unit/geometry/centroid.cpp b/test/unit/geometry/centroid.cpp
new file mode 100644
index 0000000..1e58aad
--- /dev/null
+++ b/test/unit/geometry/centroid.cpp
@@ -0,0 +1,205 @@
+#include "catch.hpp"
+
+#include <mapnik/geometry_centroid.hpp>
+
+TEST_CASE("geometry centroid") {
+
+SECTION("empty geometry") {
+
+    mapnik::geometry::geometry_empty geom;
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(!mapnik::geometry::centroid(geom, centroid));
+}
+
+SECTION("geometry collection") {
+
+    mapnik::geometry::geometry_collection<double> geom;
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(!mapnik::geometry::centroid(geom, centroid));
+}
+
+SECTION("point") {
+
+    mapnik::geometry::point<double> pt(10, 10);
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(mapnik::geometry::centroid(pt, centroid));
+    REQUIRE(pt.x == centroid.x);
+    REQUIRE(pt.y == centroid.y);
+}
+
+SECTION("linestring") {
+
+    mapnik::geometry::line_string<double> line;
+    line.add_coord(0, 0);
+    line.add_coord(25, 25);
+    line.add_coord(50, 50);
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(mapnik::geometry::centroid(line, centroid));
+    REQUIRE(centroid.x == 25);
+    REQUIRE(centroid.y == 25);
+}
+
+SECTION("empty linestring") {
+
+    mapnik::geometry::line_string<double> line;
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(!mapnik::geometry::centroid(line, centroid));
+}
+
+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));
+
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(mapnik::geometry::centroid(poly, centroid));
+    REQUIRE(centroid.x == 0.5);
+    REQUIRE(centroid.y == 0.5);
+}
+
+SECTION("polygon with empty exterior ring") {
+
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    poly.set_exterior_ring(std::move(ring));
+
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(!mapnik::geometry::centroid(poly, centroid));
+}
+
+SECTION("empty polygon") {
+
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(!mapnik::geometry::centroid(poly, centroid));
+}
+
+SECTION("multi-point") {
+
+    mapnik::geometry::multi_point<double> geom;
+    geom.add_coord(0, 0);
+    geom.add_coord(25, 25);
+    geom.add_coord(50, 50);
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(mapnik::geometry::centroid(geom, centroid));
+    REQUIRE(centroid.x == 25);
+    REQUIRE(centroid.y == 25);
+}
+
+SECTION("empty multi-point") {
+
+    mapnik::geometry::multi_point<double> geom;
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(!mapnik::geometry::centroid(geom, centroid));
+}
+
+SECTION("multi-linestring") {
+
+    mapnik::geometry::multi_line_string<double> geom;
+    {
+        mapnik::geometry::line_string<double> line;
+        line.add_coord(0, 0);
+        line.add_coord(0, 25);
+        line.add_coord(0, 50);
+        geom.emplace_back(std::move(line));
+    }
+    {
+        mapnik::geometry::line_string<double> line;
+        line.add_coord(0, 0);
+        line.add_coord(25, 0);
+        line.add_coord(50, 0);
+        geom.emplace_back(std::move(line));
+    }
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(mapnik::geometry::centroid(geom, centroid));
+    REQUIRE(centroid.x == 12.5);
+    REQUIRE(centroid.y == 12.5);
+}
+
+SECTION("multi-linestring: one component empty") {
+
+    mapnik::geometry::multi_line_string<double> geom;
+    mapnik::geometry::line_string<double> line;
+    line.add_coord(0, 0);
+    line.add_coord(0, 25);
+    line.add_coord(0, 50);
+    geom.emplace_back(std::move(line));
+    geom.emplace_back();
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(mapnik::geometry::centroid(geom, centroid));
+    REQUIRE(centroid.x == 0);
+    REQUIRE(centroid.y == 25);
+}
+
+SECTION("empty multi-linestring") {
+
+    mapnik::geometry::multi_line_string<double> geom;
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(!mapnik::geometry::centroid(geom, centroid));
+}
+
+SECTION("multi-polygon") {
+
+    mapnik::geometry::multi_polygon<double> geom;
+    {
+        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));
+        geom.emplace_back(std::move(poly));
+    }
+    {
+        mapnik::geometry::polygon<double> poly;
+        mapnik::geometry::linear_ring<double> ring;
+        ring.add_coord(1, 1);
+        ring.add_coord(2, 1);
+        ring.add_coord(2, 2);
+        ring.add_coord(1, 2);
+        ring.add_coord(1, 1);
+        poly.set_exterior_ring(std::move(ring));
+        geom.emplace_back(std::move(poly));
+    }
+
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(mapnik::geometry::centroid(geom, centroid));
+    REQUIRE(centroid.x == 1);
+    REQUIRE(centroid.y == 1);
+}
+
+SECTION("multi-polygon: one component empty") {
+
+    mapnik::geometry::multi_polygon<double> geom;
+    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));
+    geom.emplace_back(std::move(poly));
+    geom.emplace_back();
+
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(mapnik::geometry::centroid(geom, centroid));
+    REQUIRE(centroid.x == 0.5);
+    REQUIRE(centroid.y == 0.5);
+}
+
+SECTION("empty multi-polygon") {
+
+    mapnik::geometry::multi_polygon<double> geom;
+    mapnik::geometry::point<double> centroid;
+    REQUIRE(!mapnik::geometry::centroid(geom, centroid));
+}
+}
diff --git a/test/unit/geometry/has_empty.cpp b/test/unit/geometry/has_empty.cpp
new file mode 100644
index 0000000..a240c6f
--- /dev/null
+++ b/test/unit/geometry/has_empty.cpp
@@ -0,0 +1,150 @@
+#include "catch.hpp"
+
+#include <mapnik/geometry_is_empty.hpp>
+
+TEST_CASE("geometry has_empty") {
+
+SECTION("empty geometry") {
+
+    mapnik::geometry::geometry_empty geom;
+    REQUIRE(!mapnik::geometry::has_empty(geom));
+}
+
+SECTION("geometry collection") {
+
+    {
+        mapnik::geometry::geometry_collection<double> geom;
+        REQUIRE(!mapnik::geometry::has_empty(geom));
+    }
+    {
+        mapnik::geometry::geometry_collection<double> geom;
+        mapnik::geometry::geometry_empty geom1;
+        geom.emplace_back(std::move(geom1));
+        REQUIRE(mapnik::geometry::has_empty(geom));
+    }
+    {
+        mapnik::geometry::geometry_collection<double> geom;
+        mapnik::geometry::multi_line_string<double> mls;
+        mapnik::geometry::line_string<double> line;
+        mls.emplace_back(std::move(line));
+        geom.emplace_back(std::move(mls));
+        REQUIRE(mapnik::geometry::has_empty(geom));
+    }
+    {
+        mapnik::geometry::geometry_collection<double> geom;
+        mapnik::geometry::multi_line_string<double> mls;
+        mapnik::geometry::line_string<double> line;
+        line.add_coord(0, 0);
+        mls.emplace_back(std::move(line));
+        geom.emplace_back(std::move(mls));
+        REQUIRE(!mapnik::geometry::has_empty(geom));
+    }
+}
+
+SECTION("point") {
+
+    mapnik::geometry::point<double> pt(10, 10);
+    REQUIRE(!mapnik::geometry::has_empty(pt));
+}
+
+SECTION("linestring") {
+
+    {
+        mapnik::geometry::line_string<double> line;
+        REQUIRE(!mapnik::geometry::has_empty(line));
+    }
+    {
+        mapnik::geometry::line_string<double> line;
+        line.add_coord(0, 0);
+        line.add_coord(25, 25);
+        line.add_coord(50, 50);
+        REQUIRE(!mapnik::geometry::has_empty(line));
+    }
+}
+
+SECTION("polygon") {
+
+    {
+        mapnik::geometry::polygon<double> poly;
+        REQUIRE(!mapnik::geometry::has_empty(poly));
+    }
+    {
+        mapnik::geometry::polygon<double> poly;
+        mapnik::geometry::linear_ring<double> ring;
+        poly.set_exterior_ring(std::move(ring));
+        REQUIRE(!mapnik::geometry::has_empty(poly));
+    }
+    {
+        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));
+        REQUIRE(!mapnik::geometry::has_empty(poly));
+    }
+}
+
+SECTION("multi-point") {
+
+    {
+        mapnik::geometry::multi_point<double> geom;
+        REQUIRE(!mapnik::geometry::has_empty(geom));
+    }
+    {
+        mapnik::geometry::multi_point<double> geom;
+        geom.add_coord(0, 0);
+        geom.add_coord(25, 25);
+        geom.add_coord(50, 50);
+        REQUIRE(!mapnik::geometry::has_empty(geom));
+    }
+}
+
+SECTION("multi-linestring") {
+
+    {
+        mapnik::geometry::multi_line_string<double> geom;
+        REQUIRE(!mapnik::geometry::has_empty(geom));
+    }
+    {
+        mapnik::geometry::multi_line_string<double> geom;
+        mapnik::geometry::line_string<double> line;
+        geom.emplace_back(std::move(line));
+        REQUIRE(mapnik::geometry::has_empty(geom));
+    }
+    {
+        mapnik::geometry::multi_line_string<double> geom;
+        mapnik::geometry::line_string<double> line;
+        line.add_coord(0, 0);
+        geom.emplace_back(std::move(line));
+        REQUIRE(!mapnik::geometry::has_empty(geom));
+    }
+}
+
+SECTION("multi-polygon") {
+
+    {
+        mapnik::geometry::multi_polygon<double> geom;
+        REQUIRE(!mapnik::geometry::has_empty(geom));
+    }
+    {
+        mapnik::geometry::multi_polygon<double> geom;
+        mapnik::geometry::polygon<double> poly;
+        mapnik::geometry::linear_ring<double> ring;
+        poly.set_exterior_ring(std::move(ring));
+        geom.emplace_back(std::move(poly));
+        REQUIRE(mapnik::geometry::has_empty(geom));
+    }
+    {
+        mapnik::geometry::multi_polygon<double> geom;
+        mapnik::geometry::polygon<double> poly;
+        mapnik::geometry::linear_ring<double> ring;
+        ring.add_coord(0, 0);
+        poly.set_exterior_ring(std::move(ring));
+        geom.emplace_back(std::move(poly));
+        REQUIRE(!mapnik::geometry::has_empty(geom));
+    }
+}
+}
diff --git a/test/unit/geometry/is_empty.cpp b/test/unit/geometry/is_empty.cpp
new file mode 100644
index 0000000..bb6a634
--- /dev/null
+++ b/test/unit/geometry/is_empty.cpp
@@ -0,0 +1,117 @@
+#include "catch.hpp"
+
+#include <mapnik/geometry_is_empty.hpp>
+
+TEST_CASE("geometry is_empty") {
+
+SECTION("empty geometry") {
+
+    mapnik::geometry::geometry_empty geom;
+    REQUIRE(mapnik::geometry::is_empty(geom));
+}
+
+SECTION("geometry collection") {
+
+    {
+        mapnik::geometry::geometry_collection<double> geom;
+        REQUIRE(mapnik::geometry::is_empty(geom));
+    }
+    {
+        mapnik::geometry::geometry_collection<double> geom;
+        mapnik::geometry::geometry_empty geom1;
+        geom.emplace_back(std::move(geom1));
+        REQUIRE(!mapnik::geometry::is_empty(geom));
+    }
+}
+
+SECTION("point") {
+
+    mapnik::geometry::point<double> pt(10, 10);
+    REQUIRE(!mapnik::geometry::is_empty(pt));
+}
+
+SECTION("linestring") {
+
+    {
+        mapnik::geometry::line_string<double> line;
+        REQUIRE(mapnik::geometry::is_empty(line));
+    }
+    {
+        mapnik::geometry::line_string<double> line;
+        line.add_coord(0, 0);
+        line.add_coord(25, 25);
+        line.add_coord(50, 50);
+        REQUIRE(!mapnik::geometry::is_empty(line));
+    }
+}
+
+SECTION("polygon") {
+
+    {
+        mapnik::geometry::polygon<double> poly;
+        REQUIRE(mapnik::geometry::is_empty(poly));
+    }
+    {
+        mapnik::geometry::polygon<double> poly;
+        mapnik::geometry::linear_ring<double> ring;
+        poly.set_exterior_ring(std::move(ring));
+        REQUIRE(mapnik::geometry::is_empty(poly));
+    }
+    {
+        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));
+        REQUIRE(!mapnik::geometry::is_empty(poly));
+    }
+}
+
+SECTION("multi-point") {
+
+    {
+        mapnik::geometry::multi_point<double> geom;
+        REQUIRE(mapnik::geometry::is_empty(geom));
+    }
+    {
+        mapnik::geometry::multi_point<double> geom;
+        geom.add_coord(0, 0);
+        geom.add_coord(25, 25);
+        geom.add_coord(50, 50);
+        REQUIRE(!mapnik::geometry::is_empty(geom));
+    }
+}
+
+SECTION("multi-linestring") {
+
+    {
+        mapnik::geometry::multi_line_string<double> geom;
+        REQUIRE(mapnik::geometry::is_empty(geom));
+    }
+    {
+        mapnik::geometry::multi_line_string<double> geom;
+        mapnik::geometry::line_string<double> line;
+        geom.emplace_back(std::move(line));
+        REQUIRE(!mapnik::geometry::is_empty(geom));
+    }
+}
+
+SECTION("multi-polygon") {
+
+    {
+        mapnik::geometry::multi_polygon<double> geom;
+        REQUIRE(mapnik::geometry::is_empty(geom));
+    }
+    {
+        mapnik::geometry::multi_polygon<double> geom;
+        mapnik::geometry::polygon<double> poly;
+        mapnik::geometry::linear_ring<double> ring;
+        poly.set_exterior_ring(std::move(ring));
+        geom.emplace_back(std::move(poly));
+        REQUIRE(!mapnik::geometry::is_empty(geom));
+    }
+}
+}
diff --git a/test/unit/geometry/label_algo_test.cpp b/test/unit/geometry/label_algo_test.cpp
deleted file mode 100644
index 1c315ca..0000000
--- a/test/unit/geometry/label_algo_test.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "catch.hpp"
-
-#include <iostream>
-#include <mapnik/geometry.hpp>
-#include <mapnik/geometry_centroid.hpp>
-#include <algorithm>
-
-TEST_CASE("labeling") {
-
-SECTION("algorithms") {
-
-    // reused these for simplicity
-    mapnik::geometry::point<double> centroid;
-    {
-        // single point
-        mapnik::geometry::point<double> pt(10,10);
-        REQUIRE( mapnik::geometry::centroid(pt, centroid));
-        REQUIRE( pt.x == centroid.x);
-        REQUIRE( pt.y == centroid.y);
-    }
-
-    // linestring with three consecutive verticies
-    {
-        mapnik::geometry::line_string<double> line;
-        line.add_coord(0, 0);
-        line.add_coord(25, 25);
-        line.add_coord(50, 50);
-        REQUIRE(mapnik::geometry::centroid(line, centroid));
-        REQUIRE( centroid.x == 25 );
-        REQUIRE( centroid.y == 25 );
-    }
-}
-}
diff --git a/test/unit/geometry/remove_empty.cpp b/test/unit/geometry/remove_empty.cpp
new file mode 100644
index 0000000..1523754
--- /dev/null
+++ b/test/unit/geometry/remove_empty.cpp
@@ -0,0 +1,53 @@
+#include "catch.hpp"
+
+#include <mapnik/geometry_remove_empty.hpp>
+
+TEST_CASE("geometry remove_empty") {
+
+SECTION("point") {
+
+    using geom_type = mapnik::geometry::point<double>;
+    geom_type pt(10, 10);
+    geom_type pt2 = mapnik::geometry::remove_empty(pt);
+    REQUIRE(pt.x == pt2.x);
+    REQUIRE(pt.y == pt2.y);
+}
+
+SECTION("multi-linestring") {
+
+    using geom_type = mapnik::geometry::multi_line_string<double>;
+    geom_type geom;
+    mapnik::geometry::line_string<double> line;
+    line.add_coord(0, 0);
+    line.add_coord(0, 25);
+    line.add_coord(0, 50);
+    geom.emplace_back(std::move(line));
+    geom.emplace_back();
+
+    REQUIRE(geom.size() == 2);
+    geom_type geom2 = mapnik::geometry::remove_empty(geom);
+    REQUIRE(geom2.size() == 1);
+    REQUIRE(geom2[0].size() == 3);
+}
+
+SECTION("multi-polygon") {
+
+    using geom_type = mapnik::geometry::multi_polygon<double>;
+    geom_type geom;
+    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));
+    geom.emplace_back(std::move(poly));
+    geom.emplace_back();
+
+    REQUIRE(geom.size() == 2);
+    geom_type geom2 = mapnik::geometry::remove_empty(geom);
+    REQUIRE(geom2.size() == 1);
+    REQUIRE(geom2[0].exterior_ring.size() == 5);
+}
+}
diff --git a/utils/mapnik-index/build.py b/utils/mapnik-index/build.py
index 341c4b8..5bb1770 100644
--- a/utils/mapnik-index/build.py
+++ b/utils/mapnik-index/build.py
@@ -42,9 +42,9 @@ 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]
 # need on linux: https://github.com/mapnik/mapnik/issues/3145
-libraries.append(env['ICU_LIB_NAME'])
 libraries.append('mapnik-json')
 libraries.append('mapnik-wkt')
+libraries.append(env['ICU_LIB_NAME'])
 
 if env['RUNTIME_LINK'] == 'static':
     libraries.extend(copy(env['LIBMAPNIK_LIBS']))
diff --git a/utils/mapnik-index/mapnik-index.cpp b/utils/mapnik-index/mapnik-index.cpp
index a19b1f1..7361c05 100644
--- a/utils/mapnik-index/mapnik-index.cpp
+++ b/utils/mapnik-index/mapnik-index.cpp
@@ -61,6 +61,7 @@ int main (int argc, char** argv)
     //using namespace mapnik;
     namespace po = boost::program_options;
     bool verbose = false;
+    bool validate_features = false;
     unsigned int depth = DEFAULT_DEPTH;
     double ratio = DEFAULT_RATIO;
     std::vector<std::string> files;
@@ -80,6 +81,7 @@ int main (int argc, char** argv)
             ("quote,q", po::value<char>(), "CSV columns quote")
             ("manual-headers,H", po::value<std::string>(), "CSV manual headers string")
             ("files",po::value<std::vector<std::string> >(),"Files to index: file1 file2 ...fileN")
+            ("validate-features", "Validate GeoJSON features")
             ;
 
         po::positional_options_description p;
@@ -102,6 +104,10 @@ int main (int argc, char** argv)
         {
             verbose = true;
         }
+        if (vm.count("validate-features"))
+        {
+            validate_features = true;
+        }
         if (vm.count("depth"))
         {
             depth = vm["depth"].as<unsigned int>();
@@ -180,8 +186,12 @@ int main (int argc, char** argv)
         else if (mapnik::detail::is_geojson(filename))
         {
             std::clog << "processing '" << filename << "' as GeoJSON\n";
-            auto result = mapnik::detail::process_geojson_file(boxes, filename);
-            if (!result.first) continue;
+            auto result = mapnik::detail::process_geojson_file(boxes, filename, validate_features);
+            if (!result.first)
+            {
+                std::clog << "Error: failed to process " << filename << std::endl;
+                continue;
+            }
             extent = result.second;
         }
 
diff --git a/utils/mapnik-index/process_geojson_file.cpp b/utils/mapnik-index/process_geojson_file.cpp
index 1ca0be7..af9656b 100644
--- a/utils/mapnik-index/process_geojson_file.cpp
+++ b/utils/mapnik-index/process_geojson_file.cpp
@@ -39,16 +39,34 @@
 
 #include <mapnik/json/positions_grammar.hpp>
 #include <mapnik/json/extract_bounding_box_grammar_impl.hpp>
+#include <mapnik/json/feature_collection_grammar_impl.hpp>
 
 namespace {
+struct feature_validate_callback
+{
+    feature_validate_callback(mapnik::box2d<double> const& box)
+        : box_(box) {}
+
+    void operator() (mapnik::feature_ptr const& f) const
+    {
+        if (box_ != f->envelope())
+        {
+            throw std::runtime_error("Bounding boxes mismatch validation feature");
+        }
+    }
+    mapnik::box2d<double> const& box_;
+};
+
 using base_iterator_type = char const*;
 const mapnik::json::extract_bounding_box_grammar<base_iterator_type> geojson_datasource_static_bbox_grammar;
+const mapnik::transcoder tr("utf8");
+const mapnik::json::feature_grammar_callback<base_iterator_type, mapnik::feature_impl, feature_validate_callback> fc_grammar(tr);
 }
 
 namespace mapnik { namespace detail {
 
 template <typename T>
-std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const& filename)
+std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const& filename, bool validate_features)
 {
     mapnik::box2d<double> extent;
 #if defined(MAPNIK_MEMORY_MAPPED_FILE)
@@ -81,9 +99,10 @@ std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const&
 #endif
 
     boost::spirit::standard::space_type space;
+    auto const* itr = start;
     try
     {
-        if (!boost::spirit::qi::phrase_parse(start, end, (geojson_datasource_static_bbox_grammar)(boost::phoenix::ref(boxes)) , space))
+        if (!boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_bbox_grammar)(boost::phoenix::ref(boxes)) , space))
         {
             std::clog << "mapnik-index (GeoJSON) : could not extract bounding boxes from : '" <<  filename <<  "'" << std::endl;
             return std::make_pair(false, extent);
@@ -93,12 +112,28 @@ std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const&
     {
         std::clog << "mapnik-index (GeoJSON): " << ex.what() << std::endl;
     }
+    mapnik::context_ptr ctx = std::make_shared<mapnik::context_type>();
+    std::size_t start_id = 1;
     for (auto const& item : boxes)
     {
         if (item.first.valid())
         {
             if (!extent.valid()) extent = item.first;
             else extent.expand_to_include(item.first);
+
+            if (validate_features)
+            {
+                base_iterator_type feat_itr = start + item.second.first;
+                base_iterator_type feat_end = feat_itr + item.second.second;
+                feature_validate_callback callback(item.first);
+                bool result = boost::spirit::qi::phrase_parse(feat_itr, feat_end, (fc_grammar)
+                                                              (boost::phoenix::ref(ctx), boost::phoenix::ref(start_id), boost::phoenix::ref(callback)),
+                                                              space);
+                if (!result || feat_itr != feat_end)
+                {
+                    return std::make_pair(false, extent);
+                }
+            }
         }
     }
     return std::make_pair(true, extent);
@@ -107,6 +142,6 @@ std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const&
 using box_type = mapnik::box2d<double>;
 using item_type = std::pair<box_type, std::pair<std::size_t, std::size_t>>;
 using boxes_type = std::vector<item_type>;
-template std::pair<bool,box2d<double>> process_geojson_file(boxes_type&, std::string const&);
+template std::pair<bool,box2d<double>> process_geojson_file(boxes_type&, std::string const&, bool);
 
 }}
diff --git a/utils/mapnik-index/process_geojson_file.hpp b/utils/mapnik-index/process_geojson_file.hpp
index d7ccd63..a4d468a 100644
--- a/utils/mapnik-index/process_geojson_file.hpp
+++ b/utils/mapnik-index/process_geojson_file.hpp
@@ -29,7 +29,7 @@
 namespace mapnik { namespace detail {
 
 template <typename T>
-std::pair<bool, box2d<double>> process_geojson_file(T & boxes, std::string const& filename);
+std::pair<bool, box2d<double>> process_geojson_file(T & boxes, std::string const& filename, bool validate_features);
 
 }}
 
diff --git a/utils/shapefile/shapefile_reader.py b/utils/shapefile/shapefile_reader.py
index 1519c67..a57d516 100755
--- a/utils/shapefile/shapefile_reader.py
+++ b/utils/shapefile/shapefile_reader.py
@@ -29,16 +29,24 @@ def test_record(_type, record) :
         test_polygon(record)
 
 def test_pointz(record):
+    _type, = struct.unpack("<i", record[0:4])
+    if _type == 0:
+        print "NULL shape"
+        return
     if len(record) != 36 :
         print>>sys.stderr,"BAD SHAPE FILE: expected 36 bytes got",len(record)
         sys.exit(1)
-    _type,x,y,z,m = struct.unpack("<idddd",record)
+    x,y,z,m = struct.unpack("<dddd",record[4:36])
     if _type != 11:
         print>>sys.stderr,"BAD SHAPE FILE: expected PointZ or NullShape got",_type
         sys.exit(1)
 
 def test_polygon(record):
-    _type, x0, y0, x1, y0, num_parts, num_points = struct.unpack("<iddddii", record[0:44])
+    _type, = struct.unpack("<i", record[0:4])
+    if _type == 0:
+        print "NULL shape"
+        return
+    x0, y0, x1, y0, num_parts, num_points = struct.unpack("<ddddii", record[4:44])
     if _type != 5:
         print>>sys.stderr, "BAD SHAPE FILE: expected Polygon or NullShape got", _type
         sys.exit(1)
@@ -85,7 +93,7 @@ if __name__ == "__main__" :
     record = struct.Struct(">II")
     calc_total_size = 50
     count = 0
-    while shx.tell() < shx_file_length * 2 :
+    while shx.tell() <= shx_file_length * 2 - 4 * 2 :
         offset,shx_content_length = record.unpack_from(shx.read(8))
         shp.seek(offset*2, os.SEEK_SET)
         record_number,content_length = record_header.unpack_from(shp.read(8))
diff --git a/utils/shapeindex/shapeindex.cpp b/utils/shapeindex/shapeindex.cpp
index 5555c20..a9c5242 100644
--- a/utils/shapeindex/shapeindex.cpp
+++ b/utils/shapeindex/shapeindex.cpp
@@ -162,7 +162,7 @@ int main (int argc,char** argv)
         mapnik::quad_tree<int> tree(extent, depth, ratio);
         int count = 0;
 
-        while (true)
+        while (shx.is_good() && pos <= file_length - 4)
         {
             int offset = shx.read_xdr_integer();
             int content_length = shx.read_xdr_integer();
@@ -189,34 +189,40 @@ int main (int argc,char** argv)
             {
                 shp.read_envelope(item_ext);
             }
-
-            tree.insert(offset * 2,item_ext);
-
             if (verbose)
             {
                 std::clog << "record number " << record_number << " box=" << item_ext << std::endl;
             }
-            ++count;
-            if (pos >= file_length) break;
+            if (item_ext.valid())
+            {
+                tree.insert(offset * 2,item_ext);
+                ++count;
+            }
         }
 
-        std::clog << " number shapes=" << count << std::endl;
-
-        std::fstream file((shapename+".index").c_str(),
-                          std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
-        if (!file)
+        if (count > 0)
         {
-            std::clog << "cannot open index file for writing file \""
-                 << (shapename+".index") << "\"" << std::endl;
+            std::clog << " number shapes=" << count << std::endl;
+            std::fstream file((shapename+".index").c_str(),
+                              std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
+            if (!file)
+            {
+                std::clog << "cannot open index file for writing file \""
+                          << (shapename+".index") << "\"" << std::endl;
+            }
+            else
+            {
+                tree.trim();
+                std::clog << " number nodes=" << tree.count() << std::endl;
+                file.exceptions(std::ios::failbit | std::ios::badbit);
+                tree.write(file);
+                file.flush();
+                file.close();
+            }
         }
         else
         {
-            tree.trim();
-            std::clog << " number nodes=" << tree.count() << std::endl;
-            file.exceptions(std::ios::failbit | std::ios::badbit);
-            tree.write(file);
-            file.flush();
-            file.close();
+            std::clog << "No non-empty geometries in shapefile" << std::endl;
         }
     }
 

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