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 <[email protected]> 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 _______________________________________________ Pkg-grass-devel mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

