From 1c6f449ba81308f3ca2c9bf4c833db41385d8d88 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 22 Jul 2013 14:17:43 -0400 Subject: [PATCH 01/48] comment unused typedefs --- src/cairo_renderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index beaaef0c9..3a13b6067 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -816,8 +816,8 @@ void cairo_renderer_base::process(polygon_pattern_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { - typedef agg::conv_clip_polygon clipped_geometry_type; - typedef coord_transform path_type; + //typedef agg::conv_clip_polygon clipped_geometry_type; + //typedef coord_transform path_type; cairo_save_restore guard(context_); context_.set_operator(sym.comp_op()); From 5c13504cc0df3ba4d303ae61888a6f519c0e3813 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 22 Jul 2013 14:17:59 -0400 Subject: [PATCH 02/48] remove unreachable break --- src/load_map.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/load_map.cpp b/src/load_map.cpp index 88fc64d32..c7643e1ef 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -269,8 +269,6 @@ void map_parser::parse_map(Map & map, xml_node const& pt, std::string const& bas { throw config_error(std::string("Invalid version string encountered: '") + *beg + "' in '" + *min_version_string + "'"); - - break; } if (i==2) { From 236c8221af8dbd58e7523b7c03752051e074c4b1 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 22 Jul 2013 14:29:46 -0400 Subject: [PATCH 03/48] miniz: remove MINIZ_NO_STDIO as it is uneeded since it is covered by catchall MINIZ_NO_ARCHIVE_APIS --- src/miniz_png.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/miniz_png.cpp b/src/miniz_png.cpp index ec445d0ba..78776c53e 100644 --- a/src/miniz_png.cpp +++ b/src/miniz_png.cpp @@ -28,7 +28,6 @@ // miniz #define MINIZ_NO_ARCHIVE_APIS -#define MINIZ_NO_STDIO #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES #include "miniz.c" From 4d3ab5e3aa528fdaf5586704cdfe632b9dd3e029 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 22 Jul 2013 14:49:10 -0400 Subject: [PATCH 04/48] agg_renderer: remove uneeded define of AGG_RENDERING_BUFFER row_ptr_cache since it is already hardcoded in agg_renderer_buffer.h and agg_config.h --- src/agg/agg_renderer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index f3e6cea3a..672cb4e11 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -45,8 +45,6 @@ #include #include #include -// agg -#define AGG_RENDERING_BUFFER row_ptr_cache #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" #include "agg_scanline_u.h" From d1667c125c858f98fde53d2f7b60cfde713a106f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 22 Jul 2013 16:21:18 -0400 Subject: [PATCH 05/48] return human readable cairo error --- include/mapnik/cairo_context.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mapnik/cairo_context.hpp b/include/mapnik/cairo_context.hpp index 16a55e617..369edaf94 100644 --- a/include/mapnik/cairo_context.hpp +++ b/include/mapnik/cairo_context.hpp @@ -59,7 +59,7 @@ typedef cairo_status_t ErrorStatus; /// Throws the appropriate exception, if exceptions are enabled. inline void throw_exception(ErrorStatus status) { - throw std::runtime_error("cairo: fixme"); + throw std::runtime_error(std::string("cairo: ") + cairo_status_to_string(status)); } //We inline this because it is called so often. @@ -70,7 +70,7 @@ inline void check_status_and_throw_exception(ErrorStatus status) } template -void check_object_status_and_throw_exception(const T& object) +void check_object_status_and_throw_exception(T const& object) { check_status_and_throw_exception(object.get_status()); } From 3bd2e6501f43123d632652d226c3c84b7dd6abda Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 15:51:22 -0400 Subject: [PATCH 06/48] fix a batch of unused variable warnings (leaving several with are true bugs/missing features) --- benchmark/run.cpp | 2 +- deps/agg/include/agg_pixfmt_rgba.h | 2 +- include/mapnik/debug.hpp | 21 ++++++++++++++++----- include/mapnik/image_filter.hpp | 8 ++++---- include/mapnik/params_impl.hpp | 2 +- include/mapnik/value.hpp | 4 ++-- include/mapnik/value_types.hpp | 8 ++++---- include/mapnik/vertex_converters.hpp | 11 +++++++---- src/datasource_cache_static.cpp | 13 +++++++------ src/expression_string.cpp | 2 +- 10 files changed, 44 insertions(+), 29 deletions(-) diff --git a/benchmark/run.cpp b/benchmark/run.cpp index 8668ab5d4..6173f6758 100644 --- a/benchmark/run.cpp +++ b/benchmark/run.cpp @@ -266,7 +266,7 @@ struct test5 s.resize(s.capacity()); while (true) { - size_t n2 = static_cast(snprintf(&s[0], s.size()+1, "%g", val_)); + size_t n2 = static_cast(snprintf(&s[0], s.size()+1, "%g", val)); if (n2 <= s.size()) { s.resize(n2); diff --git a/deps/agg/include/agg_pixfmt_rgba.h b/deps/agg/include/agg_pixfmt_rgba.h index a859982dd..5ad41f817 100644 --- a/deps/agg/include/agg_pixfmt_rgba.h +++ b/deps/agg/include/agg_pixfmt_rgba.h @@ -134,7 +134,7 @@ namespace agg static AGG_INLINE void blend_pix(value_type* p, unsigned cr, unsigned cg, unsigned cb, unsigned alpha, - unsigned cover=0) + unsigned /*cover*/=0) { calc_type r = p[Order::R]; calc_type g = p[Order::G]; diff --git a/include/mapnik/debug.hpp b/include/mapnik/debug.hpp index caf5777fc..c190eef96 100644 --- a/include/mapnik/debug.hpp +++ b/include/mapnik/debug.hpp @@ -164,7 +164,7 @@ namespace mapnik { public: typedef std::basic_ostringstream stream_buffer; - void operator()(const logger::severity_type& severity, const stream_buffer &s) + void operator()(const logger::severity_type& /*severity*/, const stream_buffer &s) { #ifdef MAPNIK_THREADSAFE static boost::mutex mutex; @@ -193,15 +193,19 @@ namespace mapnik { base_log() {} +#ifdef MAPNIK_LOG base_log(const char* object_name) { -#ifdef MAPNIK_LOG if (object_name != NULL) { object_name_ = object_name; } -#endif } +#else + base_log(const char* /*object_name*/) + { + } +#endif ~base_log() { @@ -214,13 +218,20 @@ namespace mapnik { } template +#ifdef MAPNIK_LOG base_log &operator<<(const T &x) { -#ifdef MAPNIK_LOG + streambuf_ << x; -#endif return *this; } +#else + base_log &operator<<(const T& /*x*/) + { + + return *this; + } +#endif private: #ifdef MAPNIK_LOG diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index 3946cb8ad..32317d4f7 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -582,7 +582,7 @@ void apply_filter(Src & src, scale_hsla const& transform) } template -void apply_filter(Src & src, gray const& op) +void apply_filter(Src & src, gray const& /*op*/) { using namespace boost::gil; @@ -632,14 +632,14 @@ void x_gradient_impl(Src const& src_view, Dst const& dst_view) } template -void apply_filter(Src & src, x_gradient const& op) +void apply_filter(Src & src, x_gradient const& /*op*/) { double_buffer tb(src); x_gradient_impl(tb.src_view, tb.dst_view); } template -void apply_filter(Src & src, y_gradient const& op) +void apply_filter(Src & src, y_gradient const& /*op*/) { double_buffer tb(src); x_gradient_impl(rotated90ccw_view(tb.src_view), @@ -647,7 +647,7 @@ void apply_filter(Src & src, y_gradient const& op) } template -void apply_filter(Src & src, invert const& op) +void apply_filter(Src & src, invert const& /*op*/) { using namespace boost::gil; diff --git a/include/mapnik/params_impl.hpp b/include/mapnik/params_impl.hpp index 258275ff5..a39e55cfc 100644 --- a/include/mapnik/params_impl.hpp +++ b/include/mapnik/params_impl.hpp @@ -47,7 +47,7 @@ namespace mapnik { namespace detail { template struct extract_value { - static inline boost::optional do_extract_from_string(std::string const& source) + static inline boost::optional do_extract_from_string(std::string const& /*source*/) { std::string err_msg = (boost::format("No conversion from std::string to %s") % typeid(T).name()).str(); throw std::runtime_error(err_msg); diff --git a/include/mapnik/value.hpp b/include/mapnik/value.hpp index a9a15725f..9ca954a6f 100644 --- a/include/mapnik/value.hpp +++ b/include/mapnik/value.hpp @@ -122,7 +122,7 @@ struct equals } template - bool operator() (T const& lhs, U const& rhs) const + bool operator() (T const& /*lhs*/, U const& /*rhs*/) const { return false; } @@ -182,7 +182,7 @@ struct not_equals // back compatibility shim to equate empty string with null for != test // https://github.com/mapnik/mapnik/issues/1859 // TODO - consider removing entire specialization at Mapnik 3.x - bool operator() (value_null lhs,value_unicode_string const& rhs) const + bool operator() (value_null /*lhs*/, value_unicode_string const& rhs) const { if (rhs.isEmpty()) return false; return true; diff --git a/include/mapnik/value_types.hpp b/include/mapnik/value_types.hpp index 41672eb53..c188d40b9 100644 --- a/include/mapnik/value_types.hpp +++ b/include/mapnik/value_types.hpp @@ -47,12 +47,12 @@ typedef bool value_bool; struct value_null { - bool operator==(value_null const& other) const + bool operator==(value_null const& /*other*/) const { return true; } - bool operator!=(value_null const& other) const + bool operator!=(value_null const& /*other*/) const { return false; } @@ -88,11 +88,11 @@ struct value_null } }; -inline std::size_t hash_value(const value_null& val) { +inline std::size_t hash_value(const value_null& /*val*/) { return 0; } -inline std::ostream& operator<< (std::ostream & out,value_null const& v) +inline std::ostream& operator<< (std::ostream & out,value_null const& /*v*/) { return out; } diff --git a/include/mapnik/vertex_converters.hpp b/include/mapnik/vertex_converters.hpp index 616741561..6eab9d00b 100644 --- a/include/mapnik/vertex_converters.hpp +++ b/include/mapnik/vertex_converters.hpp @@ -64,6 +64,9 @@ #include "agg_conv_clipper.h" #include "agg_path_storage.h" +// stl +#include + namespace mapnik { struct transform_tag {}; @@ -86,9 +89,9 @@ struct converter_traits typedef T0 geometry_type; typedef geometry_type conv_type; template - static void setup(geometry_type & geom, Args const& args) + static void setup(geometry_type & geom, Args const& /*args*/) { - throw "BOOM!"; + throw std::runtime_error("invalid call to setup"); } }; @@ -216,7 +219,7 @@ struct converter_traits typedef T geometry_type; typedef typename agg::conv_close_polygon conv_type; template - static void setup(geometry_type & geom, Args const& args) + static void setup(geometry_type & geom, Args const& /*args*/) { // no-op } @@ -291,7 +294,7 @@ template <> struct converter_fwd { template - static void forward(Base& base, T0 & geom,T1 const& args) + static void forward(Base& base, T0 & geom,T1 const& /*args*/) { base.template dispatch(geom, typename boost::is_same::type()); } diff --git a/src/datasource_cache_static.cpp b/src/datasource_cache_static.cpp index 3d7e4b58d..07e178ca4 100644 --- a/src/datasource_cache_static.cpp +++ b/src/datasource_cache_static.cpp @@ -133,23 +133,24 @@ static datasource_map ds_map = boost::assign::map_list_of ; #endif +#ifdef MAPNIK_STATIC_PLUGINS datasource_ptr create_static_datasource(parameters const& params) { datasource_ptr ds; - -#ifdef MAPNIK_STATIC_PLUGINS boost::optional type = params.get("type"); - datasource_map::iterator it = ds_map.find(*type); - if (it != ds_map.end()) { ds = it->second(params); } -#endif - return ds; } +#else +datasource_ptr create_static_datasource(parameters const& /*params*/) +{ + return datasource_ptr(); +} +#endif std::vector get_static_datasource_names() { diff --git a/src/expression_string.cpp b/src/expression_string.cpp index f42d5f6cc..484d3c273 100644 --- a/src/expression_string.cpp +++ b/src/expression_string.cpp @@ -59,7 +59,7 @@ struct expression_string : boost::static_visitor str_ += "]"; } - void operator() (geometry_type_attribute const& attr) const + void operator() (geometry_type_attribute const& /*attr*/) const { str_ += "[mapnik::geometry_type]"; } From 31931b89cb8b07d43e04858a66460573fb57c17e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 16:00:16 -0400 Subject: [PATCH 07/48] git rid of a few fixme --- bindings/python/mapnik_map.cpp | 11 ----------- include/mapnik/cairo_renderer.hpp | 4 ---- plugins/input/shape/shape_index_featureset.cpp | 2 +- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/bindings/python/mapnik_map.cpp b/bindings/python/mapnik_map.cpp index 6a46bc8b5..37c4a50d8 100644 --- a/bindings/python/mapnik_map.cpp +++ b/bindings/python/mapnik_map.cpp @@ -91,17 +91,6 @@ mapnik::featureset_ptr query_map_point(mapnik::Map const& m, int index, double x return m.query_map_point(idx, x, y); } -// deepcopy -/* -mapnik::Map map_deepcopy(mapnik::Map & m, boost::python::dict memo) -{ - // FIXME: ignore memo for now - mapnik::Map result; - mapnik::util::deepcopy(m, result); - return result; -} -*/ - void set_maximum_extent(mapnik::Map & m, boost::optional > const& box) { if (box) diff --git a/include/mapnik/cairo_renderer.hpp b/include/mapnik/cairo_renderer.hpp index 1a4bade72..c2278231d 100644 --- a/include/mapnik/cairo_renderer.hpp +++ b/include/mapnik/cairo_renderer.hpp @@ -43,10 +43,6 @@ // boost #include -// FIXME -// forward declare so that -// apps using mapnik do not -// need agg headers namespace agg { struct trans_affine; } diff --git a/plugins/input/shape/shape_index_featureset.cpp b/plugins/input/shape/shape_index_featureset.cpp index 78836a030..20d4bd181 100644 --- a/plugins/input/shape/shape_index_featureset.cpp +++ b/plugins/input/shape/shape_index_featureset.cpp @@ -144,7 +144,7 @@ feature_ptr shape_index_featureset::next() return feature_ptr(); } - // FIXME + // FIXME: https://github.com/mapnik/mapnik/issues/1020 feature->set_id(shape_.id_); if (attr_ids_.size()) { From c5b7cd0e3e1c8318feebe32fc009512bc667b44d Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 16:10:00 -0400 Subject: [PATCH 08/48] remove dead code --- include/mapnik/graphics.hpp | 53 ------------------------------------ include/mapnik/grid/grid.hpp | 24 ---------------- 2 files changed, 77 deletions(-) diff --git a/include/mapnik/graphics.hpp b/include/mapnik/graphics.hpp index ae7681304..b9a3c8f31 100644 --- a/include/mapnik/graphics.hpp +++ b/include/mapnik/graphics.hpp @@ -137,59 +137,6 @@ public: data_(x,y)=rgba; } } - inline void blendPixel(int x,int y,unsigned int rgba1,int t) - { - blendPixel2(x,y,rgba1,t,1.0); // do not change opacity - } - - inline void blendPixel2(int x,int y,unsigned int rgba1,int t,double opacity) - { - if (checkBounds(x,y)) - { - unsigned rgba0 = data_(x,y); -#ifdef MAPNIK_BIG_ENDIAN - unsigned a1 = (unsigned)((rgba1 & 0xff) * opacity) & 0xff; // adjust for desired opacity - a1 = (t*a1) / 255; - if (a1 == 0) return; - unsigned r1 = (rgba1 >> 24) & 0xff; - unsigned g1 = (rgba1 >> 16 ) & 0xff; - unsigned b1 = (rgba1 >> 8) & 0xff; - - unsigned a0 = (rgba0 & 0xff); - unsigned r0 = ((rgba0 >> 24 ) & 0xff) * a0; - unsigned g0 = ((rgba0 >> 16 ) & 0xff) * a0; - unsigned b0 = ((rgba0 >> 8) & 0xff) * a0; - - a0 = ((a1 + a0) << 8) - a0*a1; - - r0 = ((((r1 << 8) - r0) * a1 + (r0 << 8)) / a0); - g0 = ((((g1 << 8) - g0) * a1 + (g0 << 8)) / a0); - b0 = ((((b1 << 8) - b0) * a1 + (b0 << 8)) / a0); - a0 = a0 >> 8; - data_(x,y)= (a0)| (b0 << 8) | (g0 << 16) | (r0 << 24) ; -#else - unsigned a1 = (unsigned)(((rgba1 >> 24) & 0xff) * opacity) & 0xff; // adjust for desired opacity - a1 = (t*a1) / 255; - if (a1 == 0) return; - unsigned r1 = rgba1 & 0xff; - unsigned g1 = (rgba1 >> 8 ) & 0xff; - unsigned b1 = (rgba1 >> 16) & 0xff; - - unsigned a0 = (rgba0 >> 24) & 0xff; - unsigned r0 = (rgba0 & 0xff) * a0; - unsigned g0 = ((rgba0 >> 8 ) & 0xff) * a0; - unsigned b0 = ((rgba0 >> 16) & 0xff) * a0; - - a0 = ((a1 + a0) << 8) - a0*a1; - - r0 = ((((r1 << 8) - r0) * a1 + (r0 << 8)) / a0); - g0 = ((((g1 << 8) - g0) * a1 + (g0 << 8)) / a0); - b0 = ((((b1 << 8) - b0) * a1 + (b0 << 8)) / a0); - a0 = a0 >> 8; - data_(x,y)= (a0 << 24)| (b0 << 16) | (g0 << 8) | (r0) ; -#endif - } - } void composite_pixel(unsigned op, int x,int y,unsigned c, unsigned cover, double opacity); diff --git a/include/mapnik/grid/grid.hpp b/include/mapnik/grid/grid.hpp index bdbceae78..e9d7e85c1 100644 --- a/include/mapnik/grid/grid.hpp +++ b/include/mapnik/grid/grid.hpp @@ -198,30 +198,6 @@ public: return height_; } - inline void blendPixel(value_type feature_id,int x,int y,unsigned int rgba1,int t) - { - blendPixel2(feature_id ,x,y,rgba1,t,1.0); // do not change opacity - } - - inline void blendPixel2(value_type feature_id,int x,int y,unsigned int rgba1,int t,double opacity) - { - if (checkBounds(x,y)) - { - -#ifdef MAPNIK_BIG_ENDIAN - unsigned a = (int)((rgba1 & 0xff) * opacity) & 0xff; // adjust for desired opacity -#else - unsigned a = (int)(((rgba1 >> 24) & 0xff) * opacity) & 0xff; // adjust for desired opacity -#endif - // if the pixel is more than a tenth - // opaque then burn in the feature id - if (a >= 25) - { - data_(x,y) = feature_id; - } - } - } - inline void set_rectangle(value_type id,image_data_32 const& data,int x0,int y0) { box2d ext0(0,0,width_,height_); From 93d36ff1e3375372b743d4062179eb3329f884dd Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 16:10:16 -0400 Subject: [PATCH 09/48] avoid -Wunused-parameter warning --- bindings/python/python_optional.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/python_optional.hpp b/bindings/python/python_optional.hpp index 9bc38d119..3b41ff757 100644 --- a/bindings/python/python_optional.hpp +++ b/bindings/python/python_optional.hpp @@ -228,7 +228,7 @@ public: : boost::python::class_(name, doc, i) { } template - self& def_readwrite_convert(char const* name, D const& d, char const* doc=0) + self& def_readwrite_convert(char const* name, D const& d, char const* /*doc*/=0) { this->add_property(name, boost::python::make_getter(d, boost::python::return_value_policy()), From 365572322c6d8de4ea0518327d7d9dd4aedc6215 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:38:57 -0400 Subject: [PATCH 10/48] tests: disable the negative image test since this makes it hard to run the tests against older mapnik versions that do not have the fix --- tests/python_tests/image_test.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/python_tests/image_test.py b/tests/python_tests/image_test.py index fa75b32eb..f67dc49b2 100644 --- a/tests/python_tests/image_test.py +++ b/tests/python_tests/image_test.py @@ -20,9 +20,11 @@ def test_image_premultiply(): im.demultiply() eq_(im.premultiplied(),False) -@raises(RuntimeError) -def test_negative_image_dimensions(): - im = mapnik.Image(-40,40) +# Disabled for now since this breaks hard if run against +# a mapnik version that does not have the fix +#@raises(RuntimeError) +#def test_negative_image_dimensions(): + #im = mapnik.Image(-40,40) def test_tiff_round_trip(): filepath = '/tmp/mapnik-tiff-io.tiff' From 6269b42ffc028f5d6b7996f1754e76e24dd09089 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:41:59 -0400 Subject: [PATCH 11/48] iwyu --- include/mapnik/agg_helpers.hpp | 13 ++++--------- src/agg/agg_renderer.cpp | 6 +++--- src/agg/process_building_symbolizer.cpp | 5 +++-- src/agg/process_line_pattern_symbolizer.cpp | 4 ++-- src/agg/process_line_symbolizer.cpp | 2 +- src/agg/process_markers_symbolizer.cpp | 2 +- src/agg/process_polygon_pattern_symbolizer.cpp | 6 +++--- src/agg/process_polygon_symbolizer.cpp | 3 ++- 8 files changed, 19 insertions(+), 22 deletions(-) diff --git a/include/mapnik/agg_helpers.hpp b/include/mapnik/agg_helpers.hpp index c96c75a02..100847e7f 100644 --- a/include/mapnik/agg_helpers.hpp +++ b/include/mapnik/agg_helpers.hpp @@ -25,17 +25,12 @@ // mapnik #include -#include +#include // for line_cap_e, line_join_e, etc + // agg -#include "agg_basics.h" -#include "agg_gamma_functions.h" -#include "agg_math_stroke.h" -#include "agg_pixfmt_rgba.h" -#include "agg_scanline_u.h" -#include "agg_scanline_p.h" -#include "agg_renderer_outline_aa.h" -#include "agg_renderer_scanline.h" +#include "agg_gamma_functions.h" // for gamma_power, gamma_linear, etc +#include "agg_math_stroke.h" // for line_join_e::miter_join, etc #include "agg_rasterizer_outline_aa.h" namespace mapnik { diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index 672cb4e11..9f2b2e272 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -47,6 +47,7 @@ #include #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" #include "agg_scanline_u.h" #include "agg_image_filters.h" #include "agg_trans_bilinear.h" @@ -191,7 +192,7 @@ void agg_renderer::end_map_processing(Map const& ) { agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4); - agg::pixfmt_rgba32 pixf(buf); + agg::pixfmt_rgba32_pre pixf(buf); pixf.demultiply(); MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End map processing"; } @@ -296,7 +297,6 @@ void agg_renderer::render_marker(pixel_position const& pos, { typedef agg::rgba8 color_type; typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba blender_type; // comp blender typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; typedef agg::renderer_base renderer_base; @@ -425,7 +425,7 @@ template template void agg_renderer::debug_draw_box(R& buf, box2d const& box, double x, double y, double angle) { - typedef agg::pixfmt_rgba32 pixfmt; + typedef agg::pixfmt_rgba32_pre pixfmt; typedef agg::renderer_base renderer_base; typedef agg::renderer_scanline_aa_solid renderer_type; diff --git a/src/agg/process_building_symbolizer.cpp b/src/agg/process_building_symbolizer.cpp index bd9fb6921..8b8c8475f 100644 --- a/src/agg/process_building_symbolizer.cpp +++ b/src/agg/process_building_symbolizer.cpp @@ -39,6 +39,7 @@ // agg #include "agg_basics.h" #include "agg_rendering_buffer.h" +#include "agg_color_rgba.h" #include "agg_pixfmt_rgba.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_scanline_u.h" @@ -54,11 +55,11 @@ void agg_renderer::process(building_symbolizer const& sym, proj_transform const& prj_trans) { typedef coord_transform path_type; - typedef agg::renderer_base ren_base; + typedef agg::renderer_base ren_base; typedef agg::renderer_scanline_aa_solid renderer; agg::rendering_buffer buf(current_buffer_->raw_data(),current_buffer_->width(),current_buffer_->height(), current_buffer_->width() * 4); - agg::pixfmt_rgba32 pixf(buf); + agg::pixfmt_rgba32_pre pixf(buf); ren_base renb(pixf); color const& fill_ = sym.get_fill(); diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index d308ffb17..add915e00 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -36,8 +36,9 @@ // agg #include "agg_basics.h" -#include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" +#include "agg_rendering_buffer.h" #include "agg_rasterizer_outline.h" #include "agg_rasterizer_outline_aa.h" #include "agg_scanline_u.h" @@ -90,7 +91,6 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, { typedef agg::rgba8 color; typedef agg::order_rgba order; - typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba_pre blender_type; typedef agg::pattern_filter_bilinear_rgba8 pattern_filter_type; typedef agg::line_image_pattern pattern_type; diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index a0338aaf6..bf7dcd2fb 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -34,6 +34,7 @@ #include "agg_basics.h" #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_scanline_u.h" #include "agg_renderer_scanline.h" @@ -77,7 +78,6 @@ void agg_renderer::process(line_symbolizer const& sym, typedef agg::rgba8 color_type; typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; typedef agg::renderer_base renderer_base; diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 2bf71bfa2..2e8b5469d 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -45,6 +45,7 @@ #include "agg_renderer_scanline.h" #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_scanline_u.h" #include "agg_path_storage.h" @@ -64,7 +65,6 @@ void agg_renderer::process(markers_symbolizer const& sym, { typedef agg::rgba8 color_type; typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender typedef agg::rendering_buffer buf_type; typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; diff --git a/src/agg/process_polygon_pattern_symbolizer.cpp b/src/agg/process_polygon_pattern_symbolizer.cpp index 4aacf9851..71e5b0def 100644 --- a/src/agg/process_polygon_pattern_symbolizer.cpp +++ b/src/agg/process_polygon_pattern_symbolizer.cpp @@ -40,6 +40,7 @@ #include "agg_basics.h" #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_scanline_u.h" // for polygon_pattern_symbolizer @@ -93,13 +94,12 @@ void agg_renderer::process(polygon_pattern_symbolizer const& sym, typedef agg::rgba8 color; typedef agg::order_rgba order; - typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba_pre blender_type; typedef agg::pixfmt_custom_blend_rgba pixfmt_type; typedef agg::wrap_mode_repeat wrap_x_type; typedef agg::wrap_mode_repeat wrap_y_type; - typedef agg::image_accessor_wrap img_source_type; @@ -117,7 +117,7 @@ void agg_renderer::process(polygon_pattern_symbolizer const& sym, unsigned w=(*pat)->width(); unsigned h=(*pat)->height(); agg::rendering_buffer pattern_rbuf((agg::int8u*)(*pat)->getBytes(),w,h,w*4); - agg::pixfmt_rgba32 pixf_pattern(pattern_rbuf); + agg::pixfmt_rgba32_pre pixf_pattern(pattern_rbuf); img_source_type img_src(pixf_pattern); pattern_alignment_e align = sym.get_alignment(); diff --git a/src/agg/process_polygon_symbolizer.cpp b/src/agg/process_polygon_symbolizer.cpp index 7e7e15d4c..25e2f09a2 100644 --- a/src/agg/process_polygon_symbolizer.cpp +++ b/src/agg/process_polygon_symbolizer.cpp @@ -36,6 +36,8 @@ #include "agg_basics.h" #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" +#include "agg_renderer_scanline.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_scanline_u.h" @@ -86,7 +88,6 @@ void agg_renderer::process(polygon_symbolizer const& sym, typedef agg::rgba8 color_type; typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; typedef agg::renderer_base renderer_base; From fd7164bd3ce7684e3cfe87f6c218aeeda15becad Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:42:47 -0400 Subject: [PATCH 12/48] use pixfmt pre for the sake of clarity --- include/mapnik/image_filter.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index 32317d4f7..fc99431b6 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -37,6 +37,7 @@ // agg #include "agg_basics.h" #include "agg_rendering_buffer.h" +#include "agg_color_rgba.h" #include "agg_pixfmt_rgba.h" #include "agg_scanline_u.h" #include "agg_blur.h" @@ -401,7 +402,7 @@ template void apply_filter(Src & src, agg_stack_blur const& op) { agg::rendering_buffer buf(src.raw_data(),src.width(),src.height(), src.width() * 4); - agg::pixfmt_rgba32 pixf(buf); + agg::pixfmt_rgba32_pre pixf(buf); agg::stack_blur_rgba32(pixf,op.rx,op.ry); } From 0ac4ec0dd9a3803cab6959cac78674d73c854a55 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:42:53 -0400 Subject: [PATCH 13/48] iwyu --- include/mapnik/marker_helpers.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index 04645ef16..f5bcce8fe 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -38,6 +38,7 @@ // agg #include "agg_ellipse.h" #include "agg_basics.h" +#include "agg_color_rgba.h" #include "agg_renderer_base.h" #include "agg_renderer_scanline.h" #include "agg_rendering_buffer.h" From 72bfa282fa91fd4fefc4941504c3a03bddaab2e9 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:43:19 -0400 Subject: [PATCH 14/48] iwyu --- include/mapnik/svg/svg_path_attributes.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/mapnik/svg/svg_path_attributes.hpp b/include/mapnik/svg/svg_path_attributes.hpp index 3a09e8e6b..831fd1f5a 100644 --- a/include/mapnik/svg/svg_path_attributes.hpp +++ b/include/mapnik/svg/svg_path_attributes.hpp @@ -26,7 +26,6 @@ // agg #include "agg_math_stroke.h" #include "agg_color_rgba.h" -#include "agg_pixfmt_rgba.h" #include "agg_trans_affine.h" // mapnik From f682fcc6d1ccee776fe049126060d6ac3feb459c Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:44:16 -0400 Subject: [PATCH 15/48] deal with various -Wunused-parameter warnings that are not importatn --- include/mapnik/attribute_collector.hpp | 6 ++---- .../mapnik/feature_style_processor_impl.hpp | 6 +----- include/mapnik/grid/grid_pixfmt.hpp | 8 ++++---- include/mapnik/grid/grid_renderer.hpp | 4 ++-- include/mapnik/placement_finder.hpp | 3 +-- include/mapnik/svg/output/svg_renderer.hpp | 12 ++++++------ include/mapnik/svg/svg_renderer_agg.hpp | 5 ++--- include/mapnik/symbolizer_hash.hpp | 5 ++--- include/mapnik/tiff_io.hpp | 6 +++--- include/mapnik/util/container_adapter.hpp | 2 +- include/mapnik/util/geometry_svg_generator.hpp | 2 +- include/mapnik/vertex_converters.hpp | 4 ++-- include/mapnik/xml_attribute_cast.hpp | 18 +++++++++--------- 13 files changed, 36 insertions(+), 45 deletions(-) diff --git a/include/mapnik/attribute_collector.hpp b/include/mapnik/attribute_collector.hpp index 5dc9d1328..bc70dd459 100644 --- a/include/mapnik/attribute_collector.hpp +++ b/include/mapnik/attribute_collector.hpp @@ -47,7 +47,6 @@ #include // for text_placements // boost -#include #include #include @@ -62,12 +61,11 @@ struct expression_attributes : boost::static_visitor explicit expression_attributes(Container& names) : names_(names) {} - void operator() (value_type const& x) const + void operator() (value_type const& /*x*/) const { - boost::ignore_unused_variable_warning(x); } - void operator() (geometry_type_attribute const& type) const + void operator() (geometry_type_attribute const& /*type*/) const { // do nothing } diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index e2f62e1f6..76f4f190c 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -49,7 +49,6 @@ #include #include #include -#include // stl #include @@ -74,11 +73,8 @@ template <> // No-op specialization struct process_impl { template - static void process(T0 & ren, T1 const& sym, T2 & f, T3 const& tr) + static void process(T0 & /*ren*/, T1 const& /*sym*/, T2 & /*f*/, T3 const& /*tr*/) { - boost::ignore_unused_variable_warning(ren); - boost::ignore_unused_variable_warning(f); - boost::ignore_unused_variable_warning(tr); #ifdef MAPNIK_DEBUG std::clog << "NO-OP ...\n"; #endif diff --git a/include/mapnik/grid/grid_pixfmt.hpp b/include/mapnik/grid/grid_pixfmt.hpp index 30c43fcd2..5ad790b9b 100644 --- a/include/mapnik/grid/grid_pixfmt.hpp +++ b/include/mapnik/grid/grid_pixfmt.hpp @@ -40,7 +40,7 @@ template struct blender_gray enum base_scale_e { base_shift = color_type::base_shift }; static AGG_INLINE void blend_pix(value_type* p, unsigned cv, - unsigned alpha, unsigned cover=0) + unsigned alpha, unsigned /*cover*/=0) { *p = (value_type)((((cv - calc_type(*p)) * alpha) + (calc_type(*p) << base_shift)) >> base_shift); } @@ -256,7 +256,7 @@ public: void blend_hline(int x, int y, unsigned len, const color_type& c, - agg::int8u cover) + agg::int8u /*cover*/) { value_type* p = (value_type*) m_rbuf->row_ptr(x, y, len) + x * Step + Offset; @@ -588,7 +588,7 @@ public: void blend_from_color(const SrcPixelFormatRenderer& from, const color_type& color, int xdst, int ydst, - int xsrc, int ysrc, + int /*xsrc*/, int ysrc, unsigned len, agg::int8u cover) { @@ -615,7 +615,7 @@ public: void blend_from_lut(const SrcPixelFormatRenderer& from, const color_type* color_lut, int xdst, int ydst, - int xsrc, int ysrc, + int /*xsrc*/, int ysrc, unsigned len, agg::int8u cover) { diff --git a/include/mapnik/grid/grid_renderer.hpp b/include/mapnik/grid/grid_renderer.hpp index f2b8e83b5..8abf326be 100644 --- a/include/mapnik/grid/grid_renderer.hpp +++ b/include/mapnik/grid/grid_renderer.hpp @@ -73,8 +73,8 @@ public: void end_map_processing(Map const& map); void start_layer_processing(layer const& lay, box2d const& query_extent); void end_layer_processing(layer const& lay); - void start_style_processing(feature_type_style const& st) {} - void end_style_processing(feature_type_style const& st) {} + void start_style_processing(feature_type_style const& /*st*/) {} + void end_style_processing(feature_type_style const& /*st*/) {} void render_marker(mapnik::feature_impl & feature, unsigned int step, pixel_position const& pos, marker const& marker, const agg::trans_affine & tr, double opacity, composite_mode_e comp_op); void process(point_symbolizer const& sym, diff --git a/include/mapnik/placement_finder.hpp b/include/mapnik/placement_finder.hpp index 0af9e1479..ad14c6936 100644 --- a/include/mapnik/placement_finder.hpp +++ b/include/mapnik/placement_finder.hpp @@ -57,8 +57,7 @@ template class placement_finder : mapnik::noncopyable { public: - placement_finder(feature_impl const& feature, - text_placement_info const& placement_info, + placement_finder(text_placement_info const& placement_info, string_info const& info, DetectorT & detector, box2d const& extent); diff --git a/include/mapnik/svg/output/svg_renderer.hpp b/include/mapnik/svg/output/svg_renderer.hpp index f76e19072..1e98fee09 100644 --- a/include/mapnik/svg/output/svg_renderer.hpp +++ b/include/mapnik/svg/output/svg_renderer.hpp @@ -79,8 +79,8 @@ public: void end_map_processing(Map const& map); void start_layer_processing(layer const& lay, box2d const& query_extent); void end_layer_processing(layer const& lay); - void start_style_processing(feature_type_style const& st) {} - void end_style_processing(feature_type_style const& st) {} + void start_style_processing(feature_type_style const& /*st*/) {} + void end_style_processing(feature_type_style const& /*st*/) {} /*! * @brief Overloads that process each kind of symbolizer individually. @@ -115,9 +115,9 @@ public: void process(markers_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans); - void process(debug_symbolizer const& sym, - mapnik::feature_impl & feature, - proj_transform const& prj_trans) {} + void process(debug_symbolizer const& /*sym*/, + mapnik::feature_impl & /*feature*/, + proj_transform const& /*prj_trans*/) {} /*! * @brief Overload that process the whole set of symbolizers of a rule. @@ -127,7 +127,7 @@ public: mapnik::feature_impl & feature, proj_transform const& prj_trans); - void painted(bool painted) + void painted(bool /*painted*/) { // nothing to do } diff --git a/include/mapnik/svg/svg_renderer_agg.hpp b/include/mapnik/svg/svg_renderer_agg.hpp index d3586873a..326edebe0 100644 --- a/include/mapnik/svg/svg_renderer_agg.hpp +++ b/include/mapnik/svg/svg_renderer_agg.hpp @@ -52,7 +52,6 @@ #include "agg_gradient_lut.h" #include "agg_gamma_lut.h" #include "agg_span_interpolator_linear.h" -#include "agg_pixfmt_rgba.h" namespace mapnik { namespace svg { @@ -341,8 +340,8 @@ public: Renderer& ren, int feature_id, agg::trans_affine const& mtx, - double opacity, - box2d const& symbol_bbox) + double /*opacity*/, + box2d const& /*symbol_bbox*/) { using namespace agg; diff --git a/include/mapnik/symbolizer_hash.hpp b/include/mapnik/symbolizer_hash.hpp index 795743402..db89ff8a9 100644 --- a/include/mapnik/symbolizer_hash.hpp +++ b/include/mapnik/symbolizer_hash.hpp @@ -34,10 +34,9 @@ namespace mapnik { struct symbolizer_hash { template - static std::size_t value(T const& sym) + static std::size_t value(T const& /*sym*/) { - std::size_t seed = 0; - return seed; + return 0; } // specialisation for polygon_symbolizer static std::size_t value(polygon_symbolizer const& sym) diff --git a/include/mapnik/tiff_io.hpp b/include/mapnik/tiff_io.hpp index 18b666180..9303aab5a 100644 --- a/include/mapnik/tiff_io.hpp +++ b/include/mapnik/tiff_io.hpp @@ -146,16 +146,16 @@ static toff_t tiff_size_proc(thandle_t fd) return (toff_t)len; } -static tsize_t tiff_dummy_read_proc(thandle_t fd, tdata_t buf, tsize_t size) +static tsize_t tiff_dummy_read_proc(thandle_t /*fd*/, tdata_t /*buf*/, tsize_t /*size*/) { return 0; } -static void tiff_dummy_unmap_proc(thandle_t fd, tdata_t base, toff_t size) +static void tiff_dummy_unmap_proc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/) { } -static int tiff_dummy_map_proc(thandle_t fd, tdata_t* pbase, toff_t* psize) +static int tiff_dummy_map_proc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/) { return 0; } diff --git a/include/mapnik/util/container_adapter.hpp b/include/mapnik/util/container_adapter.hpp index 5f98e995e..d6c1bbc26 100644 --- a/include/mapnik/util/container_adapter.hpp +++ b/include/mapnik/util/container_adapter.hpp @@ -55,7 +55,7 @@ template <> struct end_container { static mapnik::util::path_iterator - call (mapnik::geometry_type const& g) + call (mapnik::geometry_type const& /*g*/) { return mapnik::util::path_iterator(); } diff --git a/include/mapnik/util/geometry_svg_generator.hpp b/include/mapnik/util/geometry_svg_generator.hpp index aec04fd8d..107ad8eb1 100644 --- a/include/mapnik/util/geometry_svg_generator.hpp +++ b/include/mapnik/util/geometry_svg_generator.hpp @@ -76,7 +76,7 @@ template <> struct end_container { static mapnik::util::path_iterator - call (path_type const& g) + call (path_type const& /*g*/) { return mapnik::util::path_iterator(); } diff --git a/include/mapnik/vertex_converters.hpp b/include/mapnik/vertex_converters.hpp index 6eab9d00b..9cb0d6e49 100644 --- a/include/mapnik/vertex_converters.hpp +++ b/include/mapnik/vertex_converters.hpp @@ -89,7 +89,7 @@ struct converter_traits typedef T0 geometry_type; typedef geometry_type conv_type; template - static void setup(geometry_type & geom, Args const& /*args*/) + static void setup(geometry_type & /*geom*/, Args const& /*args*/) { throw std::runtime_error("invalid call to setup"); } @@ -219,7 +219,7 @@ struct converter_traits typedef T geometry_type; typedef typename agg::conv_close_polygon conv_type; template - static void setup(geometry_type & geom, Args const& /*args*/) + static void setup(geometry_type & /*geom*/, Args const& /*args*/) { // no-op } diff --git a/include/mapnik/xml_attribute_cast.hpp b/include/mapnik/xml_attribute_cast.hpp index 20a411c47..76562f0c2 100644 --- a/include/mapnik/xml_attribute_cast.hpp +++ b/include/mapnik/xml_attribute_cast.hpp @@ -44,7 +44,7 @@ namespace mapnik { namespace detail { template struct do_xml_attribute_cast { - static inline boost::optional xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& /*source*/) { std::string err_msg = (boost::format("No conversion from std::string to %s") % typeid(T).name()).str(); throw std::runtime_error(err_msg); @@ -55,7 +55,7 @@ struct do_xml_attribute_cast template <> struct do_xml_attribute_cast { - static inline boost::optional xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { bool result; if (mapnik::util::string2bool(source, result)) @@ -68,7 +68,7 @@ struct do_xml_attribute_cast template <> struct do_xml_attribute_cast { - static inline boost::optional xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { int result; if (mapnik::util::string2int(source, result)) @@ -82,7 +82,7 @@ struct do_xml_attribute_cast template <> struct do_xml_attribute_cast { - static inline boost::optional xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { int result; if (mapnik::util::string2int(source, result)) @@ -98,7 +98,7 @@ struct do_xml_attribute_cast template <> struct do_xml_attribute_cast { - static inline boost::optional xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { int result; if (mapnik::util::string2int(source, result)) @@ -111,7 +111,7 @@ struct do_xml_attribute_cast template <> struct do_xml_attribute_cast { - static inline boost::optional xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { float result; if (mapnik::util::string2float(source, result)) @@ -124,7 +124,7 @@ struct do_xml_attribute_cast template <> struct do_xml_attribute_cast { - static inline boost::optional xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { double result; if (mapnik::util::string2double(source, result)) @@ -137,7 +137,7 @@ struct do_xml_attribute_cast template struct do_xml_attribute_cast > { - static inline boost::optional > xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional > xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { typedef typename boost::optional > result_type; try @@ -168,7 +168,7 @@ struct do_xml_attribute_cast template <> struct do_xml_attribute_cast { - static inline boost::optional xml_attribute_cast_impl(xml_tree const& tree, std::string const& source) + static inline boost::optional xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { return boost::optional(source); } From 84842f59c77dd8f51a65173f244a5c578cd7614b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:44:39 -0400 Subject: [PATCH 16/48] use pixfmt pre for the sake of clarity --- src/graphics.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/graphics.cpp b/src/graphics.cpp index 5119cf397..cf55b37c3 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -30,6 +30,7 @@ // agg #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" // boost #include @@ -202,7 +203,7 @@ void image_32::premultiply() void image_32::demultiply() { agg::rendering_buffer buffer(data_.getBytes(),width_,height_,width_ * 4); - agg::pixfmt_rgba32 pixf(buffer); + agg::pixfmt_rgba32_pre pixf(buffer); pixf.demultiply(); premultiplied_ = false; } From c49d94fdf7241aa6115ea9a56c8c9ee2a45c4715 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:45:25 -0400 Subject: [PATCH 17/48] iwyu --- src/grid/process_markers_symbolizer.cpp | 1 - src/image_compositing.cpp | 1 + src/image_scaling.cpp | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index 94cd4cc1f..e04b3d53c 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -65,7 +65,6 @@ porting notes --> // agg #include "agg_basics.h" #include "agg_rendering_buffer.h" -#include "agg_pixfmt_rgba.h" #include "agg_rasterizer_scanline_aa.h" // boost diff --git a/src/image_compositing.cpp b/src/image_compositing.cpp index cc089ebd7..e6c7b147a 100644 --- a/src/image_compositing.cpp +++ b/src/image_compositing.cpp @@ -34,6 +34,7 @@ #include "agg_scanline_u.h" #include "agg_renderer_scanline.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" namespace mapnik { diff --git a/src/image_scaling.cpp b/src/image_scaling.cpp index 0d268a723..c6b32b2d7 100644 --- a/src/image_scaling.cpp +++ b/src/image_scaling.cpp @@ -33,6 +33,7 @@ // agg #include "agg_image_accessors.h" #include "agg_pixfmt_rgba.h" +#include "agg_color_rgba.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_renderer_scanline.h" #include "agg_rendering_buffer.h" From 7a6117b18dd5f56098610205d1355bb5d78a8407 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:46:19 -0400 Subject: [PATCH 18/48] remove dead code and iwyu --- src/svg/svg_parser.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 99738c04d..1a322ac2c 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -29,6 +29,7 @@ #include "agg_ellipse.h" #include "agg_rounded_rect.h" #include "agg_span_gradient.h" +#include "agg_color_rgba.h" #include #include @@ -64,7 +65,6 @@ void parse_linear_gradient(svg_parser & parser,xmlTextReaderPtr reader); void parse_radial_gradient(svg_parser & parser,xmlTextReaderPtr reader); bool parse_common_gradient(svg_parser & parser,xmlTextReaderPtr reader); void parse_gradient_stop(svg_parser & parser,xmlTextReaderPtr reader); -void parse_pattern(svg_parser & parser,xmlTextReaderPtr reader); void parse_attr(svg_parser & parser,xmlTextReaderPtr reader); void parse_attr(svg_parser & parser,const xmlChar * name, const xmlChar * value ); @@ -1046,11 +1046,6 @@ void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader) //MAPNIK_LOG_DEBUG(svg_parser) << "Found Linear Gradient: " << "(" << x1 << " " << y1 << "),(" << x2 << " " << y2 << ")"; } -void parse_pattern(svg_parser & parser, xmlTextReaderPtr reader) -{ - //const xmlChar *value; -} - svg_parser::svg_parser(svg_converter > & path) : path_(path), From 827a9d147a1acc7b0698ebce80f5707ab42fc01d Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:46:40 -0400 Subject: [PATCH 19/48] code cleanup --- src/warp.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/warp.cpp b/src/warp.cpp index c40953053..7a88005f7 100644 --- a/src/warp.cpp +++ b/src/warp.cpp @@ -78,11 +78,9 @@ void reproject_and_scale_raster(raster & target, raster const& source, prj_trans.backward(xs.getData(), ys.getData(), NULL, mesh_nx*mesh_ny); // Initialize AGG objects - typedef agg::pixfmt_rgba32 pixfmt; + typedef agg::pixfmt_rgba32_pre pixfmt; typedef pixfmt::color_type color_type; typedef agg::renderer_base renderer_base; - typedef agg::pixfmt_rgba32_pre pixfmt_pre; - typedef agg::renderer_base renderer_base_pre; agg::rasterizer_scanline_aa<> rasterizer; agg::scanline_u8 scanline; @@ -90,8 +88,8 @@ void reproject_and_scale_raster(raster & target, raster const& source, target.data_.width(), target.data_.height(), target.data_.width()*4); - pixfmt_pre pixf_pre(buf); - renderer_base_pre rb_pre(pixf_pre); + pixfmt pixf(buf); + renderer_base rb(pixf); rasterizer.clip_box(0, 0, target.data_.width(), target.data_.height()); agg::rendering_buffer buf_tile( (unsigned char*)source.data_.getData(), @@ -184,13 +182,13 @@ void reproject_and_scale_raster(raster & target, raster const& source, span_gen_type; span_gen_type sg(ia, interpolator); - agg::render_scanlines_aa(rasterizer, scanline, rb_pre, + agg::render_scanlines_aa(rasterizer, scanline, rb, sa, sg); } else { typedef agg::span_image_resample_rgba_affine span_gen_type; span_gen_type sg(ia, interpolator, filter); - agg::render_scanlines_aa(rasterizer, scanline, rb_pre, + agg::render_scanlines_aa(rasterizer, scanline, rb, sa, sg); } } From cfc977e6cdbf4f6cec5850eb2142074748e2aeaa Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:46:54 -0400 Subject: [PATCH 20/48] deal with various -Wunused-parameter warnings that are not important --- src/formatting/base.cpp | 10 +++--- src/grid/grid_renderer.cpp | 4 +-- src/jpeg_reader.cpp | 9 ++--- src/placement_finder.cpp | 3 +- src/png_reader.cpp | 4 +-- src/save_map.cpp | 42 +++++++++++++--------- src/svg/output/process_line_symbolizer.cpp | 4 +-- src/symbolizer_helpers.cpp | 2 +- src/tiff_reader.cpp | 8 ++--- 9 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/formatting/base.cpp b/src/formatting/base.cpp index bc3be41f8..849c8f13e 100644 --- a/src/formatting/base.cpp +++ b/src/formatting/base.cpp @@ -30,13 +30,15 @@ // boost #include +// stl +#include + namespace mapnik { namespace formatting { -void node::to_xml(boost::property_tree::ptree &xml) const +void node::to_xml(boost::property_tree::ptree & /*xml*/) const { - //TODO: Should this throw a config_error? - MAPNIK_LOG_ERROR(base) << "Trying to write unsupported node type to XML."; + throw std::runtime_error("Trying to write unsupported node type to XML"); } node_ptr node::from_xml(xml_node const& xml) @@ -62,7 +64,7 @@ node_ptr node::from_xml(xml_node const& xml) } } -void node::add_expressions(expression_set &output) const +void node::add_expressions(expression_set & /*output*/) const { //Do nothing by default } diff --git a/src/grid/grid_renderer.cpp b/src/grid/grid_renderer.cpp index 285e0c6b9..5a9e58868 100644 --- a/src/grid/grid_renderer.cpp +++ b/src/grid/grid_renderer.cpp @@ -109,7 +109,7 @@ void grid_renderer::start_map_processing(Map const& m) } template -void grid_renderer::end_map_processing(Map const& m) +void grid_renderer::end_map_processing(Map const& /*m*/) { MAPNIK_LOG_DEBUG(grid_renderer) << "grid_renderer: End map processing"; } @@ -140,7 +140,7 @@ void grid_renderer::end_layer_processing(layer const&) } template -void grid_renderer::render_marker(mapnik::feature_impl & feature, unsigned int step, pixel_position const& pos, marker const& marker, agg::trans_affine const& tr, double opacity, composite_mode_e comp_op) +void grid_renderer::render_marker(mapnik::feature_impl & feature, unsigned int step, pixel_position const& pos, marker const& marker, agg::trans_affine const& tr, double opacity, composite_mode_e /*comp_op*/) { if (marker.is_vector()) { diff --git a/src/jpeg_reader.cpp b/src/jpeg_reader.cpp index 5af08699e..ac9fd7281 100644 --- a/src/jpeg_reader.cpp +++ b/src/jpeg_reader.cpp @@ -178,7 +178,7 @@ void jpeg_reader::skip(j_decompress_ptr cinfo, long count) } template -void jpeg_reader::term (j_decompress_ptr cinfo) +void jpeg_reader::term (j_decompress_ptr /*cinfo*/) { // no-op } @@ -203,15 +203,16 @@ void jpeg_reader::attach_stream (j_decompress_ptr cinfo, input_stream* in) } template -void jpeg_reader::on_error(j_common_ptr cinfo) +void jpeg_reader::on_error(j_common_ptr /*cinfo*/) { - throw image_reader_exception("JPEG Reader: libjpeg could not read image"); } template void jpeg_reader::on_error_message(j_common_ptr cinfo) { - // used to supress jpeg from printing to stderr + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + throw image_reader_exception(std::string("JPEG Reader: libjpeg could not read image: ") + buffer); } template diff --git a/src/placement_finder.cpp b/src/placement_finder.cpp index 0f7a3906e..63db19727 100644 --- a/src/placement_finder.cpp +++ b/src/placement_finder.cpp @@ -97,8 +97,7 @@ double get_total_distance(T & shape_path) } template -placement_finder::placement_finder(feature_impl const& feature, - text_placement_info const& placement_info, +placement_finder::placement_finder(text_placement_info const& placement_info, string_info const& info, DetectorT & detector, box2d const& extent) diff --git a/src/png_reader.cpp b/src/png_reader.cpp index 2ef46f65b..100ce2ea6 100644 --- a/src/png_reader.cpp +++ b/src/png_reader.cpp @@ -96,12 +96,12 @@ const bool registered2 = register_image_reader("png", create_png_reader2); } -void user_error_fn(png_structp png_ptr, png_const_charp error_msg) +void user_error_fn(png_structp /*png_ptr*/, png_const_charp error_msg) { throw image_reader_exception(std::string("failed to read invalid png: '") + error_msg + "'"); } -void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg) +void user_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg) { MAPNIK_LOG_DEBUG(png_reader) << "libpng warning: '" << warning_msg << "'"; } diff --git a/src/save_map.cpp b/src/save_map.cpp index edfa029a3..be74aa681 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -190,8 +190,7 @@ public: if (sym.get_colorizer()) { - serialize_raster_colorizer(sym_node, sym.get_colorizer(), - explicit_defaults_); + serialize_raster_colorizer(sym_node, sym.get_colorizer()); } boost::optional premultiplied = sym.premultiplied(); @@ -352,13 +351,16 @@ public: } template +#ifdef MAPNIK_DEBUG void operator () ( Symbolizer const& sym) { - // not-supported -#ifdef MAPNIK_DEBUG MAPNIK_LOG_WARN(save_map) << typeid(sym).name() << " is not supported"; -#endif } +#else + void operator () ( Symbolizer const& /*sym*/) + { + } +#endif private: serialize_symbolizer(); @@ -394,16 +396,23 @@ private: } void serialize_raster_colorizer(ptree & sym_node, - raster_colorizer_ptr const& colorizer, - bool explicit_defaults) + raster_colorizer_ptr const& colorizer) { ptree & col_node = sym_node.push_back( ptree::value_type("RasterColorizer", ptree() ))->second; - - set_attr(col_node, "default-mode", colorizer->get_default_mode()); - set_attr(col_node, "default-color", colorizer->get_default_color()); - set_attr(col_node, "epsilon", colorizer->get_epsilon()); - + raster_colorizer dfl; + if (colorizer->get_default_mode() != dfl.get_default_mode() || explicit_defaults_) + { + set_attr(col_node, "default-mode", colorizer->get_default_mode()); + } + if (colorizer->get_default_color() != dfl.get_default_color() || explicit_defaults_) + { + set_attr(col_node, "default-color", colorizer->get_default_color()); + } + if (colorizer->get_epsilon() != dfl.get_epsilon() || explicit_defaults_) + { + set_attr(col_node, "epsilon", colorizer->get_epsilon()); + } unsigned i; colorizer_stops const &stops = colorizer->get_stops(); for (i=0; i.type", "int" ); } - void operator () ( mapnik::value_double val ) const + void operator () ( mapnik::value_double /*val*/ ) const { node_.put(".type", "float" ); } - void operator () ( std::string const& val ) const + void operator () ( std::string const& /*val*/ ) const { node_.put(".type", "string" ); } - void operator () ( mapnik::value_null val ) const + void operator () ( mapnik::value_null /*val*/ ) const { node_.put(".type", "string" ); } diff --git a/src/svg/output/process_line_symbolizer.cpp b/src/svg/output/process_line_symbolizer.cpp index a93346326..6f2008408 100644 --- a/src/svg/output/process_line_symbolizer.cpp +++ b/src/svg/output/process_line_symbolizer.cpp @@ -30,8 +30,8 @@ namespace mapnik */ template void svg_renderer::process(line_symbolizer const& sym, - mapnik::feature_impl & feature, - proj_transform const& prj_trans) + mapnik::feature_impl & /*feature*/, + proj_transform const& /*prj_trans*/) { path_attributes_.set_stroke_color(sym.get_stroke().get_color()); path_attributes_.set_stroke_opacity(sym.get_stroke().get_opacity()); diff --git a/src/symbolizer_helpers.cpp b/src/symbolizer_helpers.cpp index 4d89b039c..683eeee00 100644 --- a/src/symbolizer_helpers.cpp +++ b/src/symbolizer_helpers.cpp @@ -330,7 +330,7 @@ bool text_symbolizer_helper::next_placement() angle_ = 0.0; } - finder_.reset(new placement_finder(feature_, *placement_, + finder_.reset(new placement_finder(*placement_, text_.get_string_info(), detector_, dims_)); placement_valid_ = true; diff --git a/src/tiff_reader.cpp b/src/tiff_reader.cpp index 4540800d6..a92e99c34 100644 --- a/src/tiff_reader.cpp +++ b/src/tiff_reader.cpp @@ -61,7 +61,7 @@ static toff_t tiff_seek_proc(thandle_t fd, toff_t off, int whence) return static_cast(in->tellg()); } -static int tiff_close_proc(thandle_t fd) +static int tiff_close_proc(thandle_t /*fd*/) { return 0; } @@ -86,16 +86,16 @@ static tsize_t tiff_read_proc(thandle_t fd, tdata_t buf, tsize_t size) return static_cast(in->gcount()); } -static tsize_t tiff_write_proc(thandle_t fd, tdata_t buf, tsize_t size) +static tsize_t tiff_write_proc(thandle_t /*fd*/, tdata_t /*buf*/, tsize_t /*size*/) { return 0; } -static void tiff_unmap_proc(thandle_t fd, tdata_t base, toff_t size) +static void tiff_unmap_proc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/) { } -static int tiff_map_proc(thandle_t fd, tdata_t* pbase, toff_t* psize) +static int tiff_map_proc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/) { return 0; } From 5ad2ebb30ec16f40084e9cceba8f59419f7497f9 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 18:59:05 -0400 Subject: [PATCH 21/48] iwyu --- src/config_error.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config_error.cpp b/src/config_error.cpp index c8889b149..8de797072 100644 --- a/src/config_error.cpp +++ b/src/config_error.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include namespace mapnik From ef4dfeb74704faefe39a64dda0e5c5aba5c9151e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 19:37:25 -0400 Subject: [PATCH 22/48] make it possible to disable compilation of grid_renderer - closes #1962 --- CHANGELOG.md | 4 + SConstruct | 2 + bindings/python/mapnik_grid.cpp | 4 + bindings/python/mapnik_grid_view.cpp | 4 + bindings/python/mapnik_python.cpp | 73 ++- bindings/python/python_grid_utils.cpp | 4 + include/build.py | 3 + include/mapnik/svg/svg_renderer_agg.hpp | 6 + src/build.py | 33 +- src/feature_style_processor.cpp | 6 + src/font_engine_freetype.cpp | 8 + tests/python_tests/buffer_clear_test.py | 24 +- tests/python_tests/render_grid_test.py | 729 ++++++++++++------------ tests/visual_tests/test.py | 2 +- 14 files changed, 507 insertions(+), 395 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 162d4901b..278204bb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ For a complete change history, see the git log. ## Future +- Added to python bindings: `has_tiff`, `has_png`, `has_webp`, `has_proj4`, `has_svg_renderer`, and `has_grid_renderer` + +- Made it possible to disable compilation of `grid_renderer` with `./configure GRID_RENDERER=False` (#1962) + - Added `webp` image encoding and decoding support (#1955) - Added `premultiplied` property on mapnik::image_32 / mapnik.Image to enable knowledge of premultiplied status of image buffer. diff --git a/SConstruct b/SConstruct index cf8904425..40b9ff277 100644 --- a/SConstruct +++ b/SConstruct @@ -336,6 +336,7 @@ opts.AddVariables( # Variables affecting rendering back-ends + BoolVariable('GRID_RENDERER', 'build support for native grid renderer', 'True'), BoolVariable('SVG_RENDERER', 'build support for native svg renderer', 'False'), BoolVariable('CPP_TESTS', 'Compile the C++ tests', 'True'), BoolVariable('BENCHMARK', 'Compile the C++ benchmark scripts', 'False'), @@ -447,6 +448,7 @@ pickle_store = [# Scons internal variables 'CAIRO_LIBPATHS', 'CAIRO_ALL_LIBS', 'CAIRO_CPPPATHS', + 'GRID_RENDERER', 'SVG_RENDERER', 'SQLITE_LINKFLAGS', 'BOOST_LIB_VERSION_FROM_HEADER', diff --git a/bindings/python/mapnik_grid.cpp b/bindings/python/mapnik_grid.cpp index 1118d3b4b..09b21ee3d 100644 --- a/bindings/python/mapnik_grid.cpp +++ b/bindings/python/mapnik_grid.cpp @@ -20,6 +20,8 @@ * *****************************************************************************/ +#if defined(GRID_RENDERER) + // boost #include #include @@ -80,3 +82,5 @@ void export_grid() ; } + +#endif diff --git a/bindings/python/mapnik_grid_view.cpp b/bindings/python/mapnik_grid_view.cpp index aa29dda78..5e9d2745f 100644 --- a/bindings/python/mapnik_grid_view.cpp +++ b/bindings/python/mapnik_grid_view.cpp @@ -20,6 +20,8 @@ * *****************************************************************************/ +#if defined(GRID_RENDERER) + // boost #include #include @@ -49,3 +51,5 @@ void export_grid_view() ) ; } + +#endif diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 81ee0661e..1e0db91a9 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -41,8 +41,10 @@ void export_image(); void export_image_view(); void export_gamma_method(); void export_scaling_method(); +#if defined(GRID_RENDERER) void export_grid(); void export_grid_view(); +#endif void export_map(); void export_python(); void export_expression(); @@ -92,7 +94,9 @@ void export_logger(); #include #include #include +#if defined(GRID_RENDERER) #include "python_grid_utils.hpp" +#endif #include "mapnik_value_converter.hpp" #include "mapnik_threads.hpp" #include "python_optional.hpp" @@ -366,7 +370,33 @@ std::string mapnik_version_string() return MAPNIK_VERSION_STRING; } -// indicator for jpeg read/write support within libmapnik +bool has_proj4() +{ +#if defined(MAPNIK_USE_PROJ4) + return true; +#else + return false; +#endif +} + +bool has_svg_renderer() +{ +#if defined(SVG_RENDERER) + return true; +#else + return false; +#endif +} + +bool has_grid_renderer() +{ +#if defined(GRID_RENDERER) + return true; +#else + return false; +#endif +} + bool has_jpeg() { #if defined(HAVE_JPEG) @@ -376,6 +406,33 @@ bool has_jpeg() #endif } +bool has_png() +{ +#if defined(HAVE_PNG) + return true; +#else + return false; +#endif +} + +bool has_tiff() +{ +#if defined(HAVE_TIFF) + return true; +#else + return false; +#endif +} + +bool has_webp() +{ +#if defined(HAVE_WEBP) + return true; +#else + return false; +#endif +} + // indicator for cairo rendering support inside libmapnik bool has_cairo() { @@ -427,7 +484,6 @@ BOOST_PYTHON_MODULE(_mapnik) using mapnik::load_map_string; using mapnik::save_map; using mapnik::save_map_to_string; - using mapnik::render_grid; register_exception_translator(&standard_error_translator); register_exception_translator(&out_of_range_error_translator); @@ -448,8 +504,10 @@ BOOST_PYTHON_MODULE(_mapnik) export_image_view(); export_gamma_method(); export_scaling_method(); +#if defined(GRID_RENDERER) export_grid(); export_grid_view(); +#endif export_expression(); export_rule(); export_style(); @@ -487,7 +545,8 @@ BOOST_PYTHON_MODULE(_mapnik) ">>> clear_cache()\n" ); - def("render_grid",&render_grid, +#if defined(GRID_RENDERER) + def("render_grid",&mapnik::render_grid, ( arg("map"), arg("layer"), args("key")="__id__", @@ -495,6 +554,7 @@ BOOST_PYTHON_MODULE(_mapnik) arg("fields")=boost::python::list() ) ); +#endif def("render_to_file",&render_to_file1, "\n" @@ -579,9 +639,11 @@ BOOST_PYTHON_MODULE(_mapnik) (arg("map"),arg("image"),args("layer")) ); +#if defined(GRID_RENDERER) def("render_layer", &mapnik::render_layer_for_grid, (arg("map"),arg("grid"),args("layer"),arg("fields")=boost::python::list()) ); +#endif #if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO) def("render",&render3, @@ -739,7 +801,12 @@ BOOST_PYTHON_MODULE(_mapnik) def("save_map_to_string", &save_map_to_string, save_map_to_string_overloads()); def("mapnik_version", &mapnik_version,"Get the Mapnik version number"); def("mapnik_version_string", &mapnik_version_string,"Get the Mapnik version string"); + def("has_proj4", &has_proj4, "Get proj4 status"); def("has_jpeg", &has_jpeg, "Get jpeg read/write support status"); + def("has_png", &has_png, "Get png read/write support status"); + def("has_tiff", &has_jpeg, "Get tiff read/write support status"); + def("has_webp", &has_jpeg, "Get webp read/write support status"); + def("has_grid_renderer", &has_grid_renderer, "Get grid_renderer status"); def("has_cairo", &has_cairo, "Get cairo library status"); def("has_pycairo", &has_pycairo, "Get pycairo module status"); diff --git a/bindings/python/python_grid_utils.cpp b/bindings/python/python_grid_utils.cpp index e5bd5103e..7e9e1e52f 100644 --- a/bindings/python/python_grid_utils.cpp +++ b/bindings/python/python_grid_utils.cpp @@ -20,6 +20,8 @@ * *****************************************************************************/ +#if defined(GRID_RENDERER) + // boost #include #include @@ -472,3 +474,5 @@ boost::python::dict render_grid(mapnik::Map const& map, } } + +#endif diff --git a/include/build.py b/include/build.py index 02d3d0032..6fed90d9b 100644 --- a/include/build.py +++ b/include/build.py @@ -9,6 +9,9 @@ subdirs = ['','svg','wkt','grid','json','util','text_placements','formatting'] if env['SVG_RENDERER']: subdirs.append('svg/output') +if env['GRID_RENDERER']: + subdirs.append('grid') + if 'install' in COMMAND_LINE_TARGETS: for subdir in subdirs: pathdir = os.path.join(base,subdir,'*.hpp') diff --git a/include/mapnik/svg/svg_renderer_agg.hpp b/include/mapnik/svg/svg_renderer_agg.hpp index 326edebe0..cf76e0a99 100644 --- a/include/mapnik/svg/svg_renderer_agg.hpp +++ b/include/mapnik/svg/svg_renderer_agg.hpp @@ -27,7 +27,11 @@ #include #include #include + +#if defined(GRID_RENDERER) #include +#endif + #include // boost @@ -334,6 +338,7 @@ public: } } +#if defined(GRID_RENDERER) template void render_id(Rasterizer& ras, Scanline& sl, @@ -416,6 +421,7 @@ public: } } } +#endif private: diff --git a/src/build.py b/src/build.py index 7ffa2df54..0c91cba7f 100644 --- a/src/build.py +++ b/src/build.py @@ -292,21 +292,24 @@ if env['RUNTIME_LINK'] == "static": source += glob.glob('../deps/agg/src/' + '*.cpp') # grid backend -source += Split( - """ - grid/grid.cpp - grid/grid_renderer.cpp - grid/process_building_symbolizer.cpp - grid/process_line_pattern_symbolizer.cpp - grid/process_line_symbolizer.cpp - grid/process_markers_symbolizer.cpp - grid/process_point_symbolizer.cpp - grid/process_polygon_pattern_symbolizer.cpp - grid/process_polygon_symbolizer.cpp - grid/process_raster_symbolizer.cpp - grid/process_shield_symbolizer.cpp - grid/process_text_symbolizer.cpp - """) +if env['GRID_RENDERER']: # svg backend + source += Split( + """ + grid/grid.cpp + grid/grid_renderer.cpp + grid/process_building_symbolizer.cpp + grid/process_line_pattern_symbolizer.cpp + grid/process_line_symbolizer.cpp + grid/process_markers_symbolizer.cpp + grid/process_point_symbolizer.cpp + grid/process_polygon_pattern_symbolizer.cpp + grid/process_polygon_symbolizer.cpp + grid/process_raster_symbolizer.cpp + grid/process_shield_symbolizer.cpp + grid/process_text_symbolizer.cpp + """) + lib_env.Append(CPPDEFINES = '-DGRID_RENDERER') + libmapnik_defines.append('-DGRID_RENDERER') # https://github.com/mapnik/mapnik/issues/1438 if env['SVG_RENDERER']: # svg backend diff --git a/src/feature_style_processor.cpp b/src/feature_style_processor.cpp index 9deb275ab..fd79e13a7 100644 --- a/src/feature_style_processor.cpp +++ b/src/feature_style_processor.cpp @@ -24,8 +24,11 @@ #include #include #include + +#if defined(GRID_RENDERER) #include #include +#endif #if defined(HAVE_CAIRO) #include @@ -48,7 +51,10 @@ template class feature_style_processor >; template class feature_style_processor > >; #endif +#if defined(GRID_RENDERER) template class feature_style_processor >; +#endif + template class feature_style_processor >; } diff --git a/src/font_engine_freetype.cpp b/src/font_engine_freetype.cpp index 78db9f24a..f9b2f84e0 100644 --- a/src/font_engine_freetype.cpp +++ b/src/font_engine_freetype.cpp @@ -25,7 +25,11 @@ #include #include #include + +#if defined(GRID_RENDERER) #include +#endif + #include #include #include @@ -686,6 +690,7 @@ void text_renderer::render(pixel_position const& pos) } } +#if defined(GRID_RENDERER) template void text_renderer::render_id(mapnik::value_integer feature_id, pixel_position const& pos) @@ -715,6 +720,7 @@ void text_renderer::render_id(mapnik::value_integer feature_id, } } } +#endif #ifdef MAPNIK_THREADSAFE boost::mutex freetype_engine::mutex_; @@ -729,6 +735,7 @@ template text_renderer::text_renderer(image_32&, double); template box2dtext_renderer::prepare_glyphs(text_path const&); template void text_renderer::render(pixel_position const&); +#if defined(GRID_RENDERER) template void text_renderer::render_id(mapnik::value_integer, pixel_position const&); template text_renderer::text_renderer(grid&, @@ -736,4 +743,5 @@ template text_renderer::text_renderer(grid&, halo_rasterizer_e, composite_mode_e, double); template box2dtext_renderer::prepare_glyphs(text_path const& ); +#endif } diff --git a/tests/python_tests/buffer_clear_test.py b/tests/python_tests/buffer_clear_test.py index 2119d6d63..7a828f8ca 100644 --- a/tests/python_tests/buffer_clear_test.py +++ b/tests/python_tests/buffer_clear_test.py @@ -45,18 +45,18 @@ def make_map(): m.zoom_all() return m -def test_clearing_grid_data(): - g = mapnik.Grid(256,256) - utf = g.encode() - # make sure it equals itself - eq_(g.encode(),utf) - m = make_map() - mapnik.render_layer(m,g,layer=0,fields=['__id__','Name']) - eq_(g.encode()!=utf,True) - # clear grid, should now match original - g.clear() - eq_(g.encode(),utf) - +if mapnik.has_grid_renderer(): + def test_clearing_grid_data(): + g = mapnik.Grid(256,256) + utf = g.encode() + # make sure it equals itself + eq_(g.encode(),utf) + m = make_map() + mapnik.render_layer(m,g,layer=0,fields=['__id__','Name']) + eq_(g.encode()!=utf,True) + # clear grid, should now match original + g.clear() + eq_(g.encode(),utf) if __name__ == "__main__": setup() diff --git a/tests/python_tests/render_grid_test.py b/tests/python_tests/render_grid_test.py index f998735c0..ca0996707 100644 --- a/tests/python_tests/render_grid_test.py +++ b/tests/python_tests/render_grid_test.py @@ -15,370 +15,371 @@ def setup(): # from another directory we need to chdir() os.chdir(execution_path('.')) -def show_grids(name,g1,g2): - g1_file = '/tmp/mapnik-%s-actual.json' % name - open(g1_file,'w').write(json.dumps(g1,sort_keys=True)) - g2_file = '/tmp/mapnik-%s-expected.json' % name - open(g2_file,'w').write(json.dumps(g2,sort_keys=True)) - val = 'JSON does not match ->\n' - if g1['grid'] != g2['grid']: - val += ' X grid does not match\n' - else: - val += ' ✓ grid matches\n' - if g1['data'].keys() != g2['data'].keys(): - val += ' X data does not match\n' - else: - val += ' ✓ data matches\n' - if g1['keys'] != g2['keys']: - val += ' X keys do not\n' - else: - val += ' ✓ keys match\n' - val += '\n\t%s\n\t%s' % (g1_file,g2_file) - return val - -def show_grids2(name,g1,g2): - g2_expected = '../data/grids/mapnik-%s-actual.json' % name - if not os.path.exists(g2_expected): - # create test fixture based on actual results - open(g2_expected,'a+').write(json.dumps(g1,sort_keys=True)) - return - g1_file = '/tmp/mapnik-%s-actual.json' % name - open(g1_file,'w').write(json.dumps(g1,sort_keys=True)) - val = 'JSON does not match ->\n' - if g1['grid'] != g2['grid']: - val += ' X grid does not match\n' - else: - val += ' ✓ grid matches\n' - if g1['data'].keys() != g2['data'].keys(): - val += ' X data does not match\n' - else: - val += ' ✓ data matches\n' - if g1['keys'] != g2['keys']: - val += ' X keys do not\n' - else: - val += ' ✓ keys match\n' - val += '\n\t%s\n\t%s' % (g1_file,g2_expected) - return val - -# first pass impl where resolution is passed as render -# time rather than encoding time, likely will be deprecated soon -grid_correct_old = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!!!! ##### ", " !!!!! ##### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$$ %%%%% ", " $$$$$ %%%%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} - -# now using svg rendering -grid_correct_old2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} - -grid_correct_old3 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} - -# previous rendering using agg ellipse directly -grid_correct_new = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} - -# newer rendering using svg -grid_correct_new2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} - -grid_correct_new3 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} - -def resolve(grid,row,col): - """ Resolve the attributes for a given pixel in a grid. - """ - row = grid['grid'][row] - utf_val = row[col] - #http://docs.python.org/library/functions.html#ord - codepoint = ord(utf_val) - if (codepoint >= 93): - codepoint-=1 - if (codepoint >= 35): - codepoint-=1 - codepoint -= 32 - key = grid['keys'][codepoint] - return grid['data'].get(key) - - -def create_grid_map(width,height,sym): - ds = mapnik.MemoryDatasource() - context = mapnik.Context() - context.push('Name') - f = mapnik.Feature(context,1) - f['Name'] = 'South East' - f.add_geometries_from_wkt('POINT (143.10 -38.60)') - ds.add_feature(f) - - f = mapnik.Feature(context,2) - f['Name'] = 'South West' - f.add_geometries_from_wkt('POINT (142.48 -38.60)') - ds.add_feature(f) - - f = mapnik.Feature(context,3) - f['Name'] = 'North West' - f.add_geometries_from_wkt('POINT (142.48 -38.38)') - ds.add_feature(f) - - f = mapnik.Feature(context,4) - f['Name'] = 'North East' - f.add_geometries_from_wkt('POINT (143.10 -38.38)') - ds.add_feature(f) - s = mapnik.Style() - r = mapnik.Rule() - sym.allow_overlap = True - r.symbols.append(sym) - s.rules.append(r) - lyr = mapnik.Layer('Places') - lyr.datasource = ds - lyr.styles.append('places_labels') - m = mapnik.Map(width,height) - m.append_style('places_labels',s) - m.layers.append(lyr) - return m - -def test_render_grid_old(): - """ test old method """ - width,height = 256,256 - symb = mapnik.PointSymbolizer(mapnik.PathExpression('../data/images/dummy.png')) - sym = mapnik.MarkersSymbolizer() - sym.width = mapnik.Expression('10') - sym.height = mapnik.Expression('10') - m = create_grid_map(width,height,sym) - #print mapnik.save_map_to_string(m) - ul_lonlat = mapnik.Coord(142.30,-38.20) - lr_lonlat = mapnik.Coord(143.40,-38.80) - m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) - grid = mapnik.render_grid(m,0,key='Name',resolution=4,fields=['Name']) - eq_(grid,grid_correct_old3,show_grids('old-markers',grid,grid_correct_old3)) - eq_(resolve(grid,0,0),None) - - # check every pixel of the nw symbol - expected = {"Name": "North West"} - - # top row - eq_(resolve(grid,23,9),expected) - eq_(resolve(grid,23,10),expected) - eq_(resolve(grid,23,11),expected) - -def test_render_grid_new(): - """ test old against new""" - width,height = 256,256 - sym = mapnik.MarkersSymbolizer() - sym.width = mapnik.Expression('10') - sym.height = mapnik.Expression('10') - m = create_grid_map(width,height,sym) - ul_lonlat = mapnik.Coord(142.30,-38.20) - lr_lonlat = mapnik.Coord(143.40,-38.80) - m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) - - # new method - grid = mapnik.Grid(m.width,m.height,key='Name') - mapnik.render_layer(m,grid,layer=0,fields=['Name']) - utf1 = grid.encode('utf',resolution=4) - eq_(utf1,grid_correct_new3,show_grids('new-markers',utf1,grid_correct_new3)) - - # check a full view is the same as a full image - grid_view = grid.view(0,0,width,height) - # for kicks check at full res too - utf3 = grid.encode('utf',resolution=1) - utf4 = grid_view.encode('utf',resolution=1) - eq_(utf3['grid'],utf4['grid']) - eq_(utf3['keys'],utf4['keys']) - eq_(utf3['data'],utf4['data']) - - eq_(resolve(utf4,0,0),None) - - # resolve some center points in the - # resampled view - utf5 = grid_view.encode('utf',resolution=4) - eq_(resolve(utf5,25,10),{"Name": "North West"}) - eq_(resolve(utf5,25,46),{"Name": "North East"}) - eq_(resolve(utf5,38,10),{"Name": "South West"}) - eq_(resolve(utf5,38,46),{"Name": "South East"}) - - -grid_feat_id = {'keys': ['', '3', '4', '2', '1'], 'data': {'1': {'Name': 'South East'}, '3': {'Name': u'North West'}, '2': {'Name': 'South West'}, '4': {'Name': 'North East'}}, 'grid': [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' !! ## ', ' !!! ### ', ' !! ## ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' $$$ %% ', ' $$$ %%% ', ' $$ %% ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']} - -grid_feat_id2 = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} - -grid_feat_id3 = {"data": {"1": {"Name": "South East", "__id__": 1}, "2": {"Name": "South West", "__id__": 2}, "3": {"Name": "North West", "__id__": 3}, "4": {"Name": "North East", "__id__": 4}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} - -def test_render_grid3(): - """ test using feature id""" - width,height = 256,256 - sym = mapnik.MarkersSymbolizer() - sym.width = mapnik.Expression('10') - sym.height = mapnik.Expression('10') - m = create_grid_map(width,height,sym) - ul_lonlat = mapnik.Coord(142.30,-38.20) - lr_lonlat = mapnik.Coord(143.40,-38.80) - m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) - - grid = mapnik.Grid(m.width,m.height,key='__id__') - mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name']) - utf1 = grid.encode('utf',resolution=4) - eq_(utf1,grid_feat_id3,show_grids('id-markers',utf1,grid_feat_id3)) - # check a full view is the same as a full image - grid_view = grid.view(0,0,width,height) - # for kicks check at full res too - utf3 = grid.encode('utf',resolution=1) - utf4 = grid_view.encode('utf',resolution=1) - eq_(utf3['grid'],utf4['grid']) - eq_(utf3['keys'],utf4['keys']) - eq_(utf3['data'],utf4['data']) - - eq_(resolve(utf4,0,0),None) - - # resolve some center points in the - # resampled view - utf5 = grid_view.encode('utf',resolution=4) - eq_(resolve(utf5,25,10),{"Name": "North West","__id__": 3}) - eq_(resolve(utf5,25,46),{"Name": "North East","__id__": 4}) - eq_(resolve(utf5,38,10),{"Name": "South West","__id__": 2}) - eq_(resolve(utf5,38,46),{"Name": "South East","__id__": 1}) - - -def gen_grid_for_id(pixel_key): - ds = mapnik.MemoryDatasource() - context = mapnik.Context() - context.push('Name') - f = mapnik.Feature(context,pixel_key) - f['Name'] = str(pixel_key) - f.add_geometries_from_wkt('POLYGON ((0 0, 0 256, 256 256, 256 0, 0 0))') - ds.add_feature(f) - s = mapnik.Style() - r = mapnik.Rule() - symb = mapnik.PolygonSymbolizer() - r.symbols.append(symb) - s.rules.append(r) - lyr = mapnik.Layer('Places') - lyr.datasource = ds - lyr.styles.append('places_labels') - width,height = 256,256 - m = mapnik.Map(width,height) - m.append_style('places_labels',s) - m.layers.append(lyr) - m.zoom_all() - grid = mapnik.Grid(m.width,m.height,key='__id__') - mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name']) - return grid - -def test_negative_id(): - grid = gen_grid_for_id(-1) - eq_(grid.get_pixel(128,128),-1) - utf1 = grid.encode('utf',resolution=4) - eq_(utf1['keys'],['-1']) - -def test_32bit_int_id(): - int32 = 2147483647 - grid = gen_grid_for_id(int32) - eq_(grid.get_pixel(128,128),int32) - utf1 = grid.encode('utf',resolution=4) - eq_(utf1['keys'],[str(int32)]) - max_neg = -(int32) - grid = gen_grid_for_id(max_neg) - eq_(grid.get_pixel(128,128),max_neg) - utf1 = grid.encode('utf',resolution=4) - eq_(utf1['keys'],[str(max_neg)]) - -def test_64bit_int_id(): - int64 = 0x7FFFFFFFFFFFFFFF - grid = gen_grid_for_id(int64) - eq_(grid.get_pixel(128,128),int64) - utf1 = grid.encode('utf',resolution=4) - eq_(utf1['keys'],[str(int64)]) - max_neg = -(int64) - grid = gen_grid_for_id(max_neg) - eq_(grid.get_pixel(128,128),max_neg) - utf1 = grid.encode('utf',resolution=4) - eq_(utf1['keys'],[str(max_neg)]) - -def test_id_zero(): - grid = gen_grid_for_id(0) - eq_(grid.get_pixel(128,128),0) - utf1 = grid.encode('utf',resolution=4) - eq_(utf1['keys'],['0']) - -line_expected = {"keys": ["", "1"], "data": {"1": {"Name": "1"}}, "grid": [" !", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", "!! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! "]} - -def test_line_rendering(): - ds = mapnik.MemoryDatasource() - context = mapnik.Context() - context.push('Name') - pixel_key = 1 - f = mapnik.Feature(context,pixel_key) - f['Name'] = str(pixel_key) - f.add_geometries_from_wkt('LINESTRING (30 10, 10 30, 40 40)') - ds.add_feature(f) - s = mapnik.Style() - r = mapnik.Rule() - symb = mapnik.LineSymbolizer() - r.symbols.append(symb) - s.rules.append(r) - lyr = mapnik.Layer('Places') - lyr.datasource = ds - lyr.styles.append('places_labels') - width,height = 256,256 - m = mapnik.Map(width,height) - m.append_style('places_labels',s) - m.layers.append(lyr) - m.zoom_all() - #mapnik.render_to_file(m,'test.png') - grid = mapnik.Grid(m.width,m.height,key='__id__') - mapnik.render_layer(m,grid,layer=0,fields=['Name']) - utf1 = grid.encode() - eq_(utf1,line_expected,show_grids('line',utf1,line_expected)) - -point_expected = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} - -def test_point_symbolizer_grid(): - width,height = 256,256 - sym = mapnik.PointSymbolizer(mapnik.PathExpression('../data/images/dummy.png')) - m = create_grid_map(width,height,sym) - ul_lonlat = mapnik.Coord(142.30,-38.20) - lr_lonlat = mapnik.Coord(143.40,-38.80) - m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) - grid = mapnik.Grid(m.width,m.height) - mapnik.render_layer(m,grid,layer=0,fields=['Name']) - utf1 = grid.encode() - eq_(utf1,point_expected,show_grids('point-sym',utf1,point_expected)) - - -# should throw because this is a mis-usage -# https://github.com/mapnik/mapnik/issues/1325 -@raises(RuntimeError) -def test_render_to_grid_multiple_times(): - # create map with two layers - m = mapnik.Map(256,256) - s = mapnik.Style() - r = mapnik.Rule() - sym = mapnik.MarkersSymbolizer() - sym.allow_overlap = True - r.symbols.append(sym) - s.rules.append(r) - m.append_style('points',s) - - # NOTE: we use a csv datasource here - # because the memorydatasource fails silently for - # queries requesting fields that do not exist in the datasource - ds1 = mapnik.Datasource(**{"type":"csv","inline":''' - wkt,Name - "POINT (143.10 -38.60)",South East'''}) - lyr1 = mapnik.Layer('One') - lyr1.datasource = ds1 - lyr1.styles.append('points') - m.layers.append(lyr1) - - ds2 = mapnik.Datasource(**{"type":"csv","inline":''' - wkt,Value - "POINT (142.48 -38.60)",South West'''}) - lyr2 = mapnik.Layer('Two') - lyr2.datasource = ds2 - lyr2.styles.append('points') - m.layers.append(lyr2) - - ul_lonlat = mapnik.Coord(142.30,-38.20) - lr_lonlat = mapnik.Coord(143.40,-38.80) - m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) - grid = mapnik.Grid(m.width,m.height) - mapnik.render_layer(m,grid,layer=0,fields=['Name']) - # should throw right here since Name will be a property now on the `grid` object - # and it is not found on the second layer - mapnik.render_layer(m,grid,layer=1,fields=['Value']) - utf1 = grid.encode() +if mapnik.has_grid_renderer(): + def show_grids(name,g1,g2): + g1_file = '/tmp/mapnik-%s-actual.json' % name + open(g1_file,'w').write(json.dumps(g1,sort_keys=True)) + g2_file = '/tmp/mapnik-%s-expected.json' % name + open(g2_file,'w').write(json.dumps(g2,sort_keys=True)) + val = 'JSON does not match ->\n' + if g1['grid'] != g2['grid']: + val += ' X grid does not match\n' + else: + val += ' ✓ grid matches\n' + if g1['data'].keys() != g2['data'].keys(): + val += ' X data does not match\n' + else: + val += ' ✓ data matches\n' + if g1['keys'] != g2['keys']: + val += ' X keys do not\n' + else: + val += ' ✓ keys match\n' + val += '\n\t%s\n\t%s' % (g1_file,g2_file) + return val + + def show_grids2(name,g1,g2): + g2_expected = '../data/grids/mapnik-%s-actual.json' % name + if not os.path.exists(g2_expected): + # create test fixture based on actual results + open(g2_expected,'a+').write(json.dumps(g1,sort_keys=True)) + return + g1_file = '/tmp/mapnik-%s-actual.json' % name + open(g1_file,'w').write(json.dumps(g1,sort_keys=True)) + val = 'JSON does not match ->\n' + if g1['grid'] != g2['grid']: + val += ' X grid does not match\n' + else: + val += ' ✓ grid matches\n' + if g1['data'].keys() != g2['data'].keys(): + val += ' X data does not match\n' + else: + val += ' ✓ data matches\n' + if g1['keys'] != g2['keys']: + val += ' X keys do not\n' + else: + val += ' ✓ keys match\n' + val += '\n\t%s\n\t%s' % (g1_file,g2_expected) + return val + + # first pass impl where resolution is passed as render + # time rather than encoding time, likely will be deprecated soon + grid_correct_old = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!!!! ##### ", " !!!!! ##### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$$ %%%%% ", " $$$$$ %%%%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} + + # now using svg rendering + grid_correct_old2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} + + grid_correct_old3 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} + + # previous rendering using agg ellipse directly + grid_correct_new = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} + + # newer rendering using svg + grid_correct_new2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} + + grid_correct_new3 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} + + def resolve(grid,row,col): + """ Resolve the attributes for a given pixel in a grid. + """ + row = grid['grid'][row] + utf_val = row[col] + #http://docs.python.org/library/functions.html#ord + codepoint = ord(utf_val) + if (codepoint >= 93): + codepoint-=1 + if (codepoint >= 35): + codepoint-=1 + codepoint -= 32 + key = grid['keys'][codepoint] + return grid['data'].get(key) + + + def create_grid_map(width,height,sym): + ds = mapnik.MemoryDatasource() + context = mapnik.Context() + context.push('Name') + f = mapnik.Feature(context,1) + f['Name'] = 'South East' + f.add_geometries_from_wkt('POINT (143.10 -38.60)') + ds.add_feature(f) + + f = mapnik.Feature(context,2) + f['Name'] = 'South West' + f.add_geometries_from_wkt('POINT (142.48 -38.60)') + ds.add_feature(f) + + f = mapnik.Feature(context,3) + f['Name'] = 'North West' + f.add_geometries_from_wkt('POINT (142.48 -38.38)') + ds.add_feature(f) + + f = mapnik.Feature(context,4) + f['Name'] = 'North East' + f.add_geometries_from_wkt('POINT (143.10 -38.38)') + ds.add_feature(f) + s = mapnik.Style() + r = mapnik.Rule() + sym.allow_overlap = True + r.symbols.append(sym) + s.rules.append(r) + lyr = mapnik.Layer('Places') + lyr.datasource = ds + lyr.styles.append('places_labels') + m = mapnik.Map(width,height) + m.append_style('places_labels',s) + m.layers.append(lyr) + return m + + def test_render_grid_old(): + """ test old method """ + width,height = 256,256 + symb = mapnik.PointSymbolizer(mapnik.PathExpression('../data/images/dummy.png')) + sym = mapnik.MarkersSymbolizer() + sym.width = mapnik.Expression('10') + sym.height = mapnik.Expression('10') + m = create_grid_map(width,height,sym) + #print mapnik.save_map_to_string(m) + ul_lonlat = mapnik.Coord(142.30,-38.20) + lr_lonlat = mapnik.Coord(143.40,-38.80) + m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) + grid = mapnik.render_grid(m,0,key='Name',resolution=4,fields=['Name']) + eq_(grid,grid_correct_old3,show_grids('old-markers',grid,grid_correct_old3)) + eq_(resolve(grid,0,0),None) + + # check every pixel of the nw symbol + expected = {"Name": "North West"} + + # top row + eq_(resolve(grid,23,9),expected) + eq_(resolve(grid,23,10),expected) + eq_(resolve(grid,23,11),expected) + + def test_render_grid_new(): + """ test old against new""" + width,height = 256,256 + sym = mapnik.MarkersSymbolizer() + sym.width = mapnik.Expression('10') + sym.height = mapnik.Expression('10') + m = create_grid_map(width,height,sym) + ul_lonlat = mapnik.Coord(142.30,-38.20) + lr_lonlat = mapnik.Coord(143.40,-38.80) + m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) + + # new method + grid = mapnik.Grid(m.width,m.height,key='Name') + mapnik.render_layer(m,grid,layer=0,fields=['Name']) + utf1 = grid.encode('utf',resolution=4) + eq_(utf1,grid_correct_new3,show_grids('new-markers',utf1,grid_correct_new3)) + + # check a full view is the same as a full image + grid_view = grid.view(0,0,width,height) + # for kicks check at full res too + utf3 = grid.encode('utf',resolution=1) + utf4 = grid_view.encode('utf',resolution=1) + eq_(utf3['grid'],utf4['grid']) + eq_(utf3['keys'],utf4['keys']) + eq_(utf3['data'],utf4['data']) + + eq_(resolve(utf4,0,0),None) + + # resolve some center points in the + # resampled view + utf5 = grid_view.encode('utf',resolution=4) + eq_(resolve(utf5,25,10),{"Name": "North West"}) + eq_(resolve(utf5,25,46),{"Name": "North East"}) + eq_(resolve(utf5,38,10),{"Name": "South West"}) + eq_(resolve(utf5,38,46),{"Name": "South East"}) + + + grid_feat_id = {'keys': ['', '3', '4', '2', '1'], 'data': {'1': {'Name': 'South East'}, '3': {'Name': u'North West'}, '2': {'Name': 'South West'}, '4': {'Name': 'North East'}}, 'grid': [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' !! ## ', ' !!! ### ', ' !! ## ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' $$$ %% ', ' $$$ %%% ', ' $$ %% ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']} + + grid_feat_id2 = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} + + grid_feat_id3 = {"data": {"1": {"Name": "South East", "__id__": 1}, "2": {"Name": "South West", "__id__": 2}, "3": {"Name": "North West", "__id__": 3}, "4": {"Name": "North East", "__id__": 4}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} + + def test_render_grid3(): + """ test using feature id""" + width,height = 256,256 + sym = mapnik.MarkersSymbolizer() + sym.width = mapnik.Expression('10') + sym.height = mapnik.Expression('10') + m = create_grid_map(width,height,sym) + ul_lonlat = mapnik.Coord(142.30,-38.20) + lr_lonlat = mapnik.Coord(143.40,-38.80) + m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) + + grid = mapnik.Grid(m.width,m.height,key='__id__') + mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name']) + utf1 = grid.encode('utf',resolution=4) + eq_(utf1,grid_feat_id3,show_grids('id-markers',utf1,grid_feat_id3)) + # check a full view is the same as a full image + grid_view = grid.view(0,0,width,height) + # for kicks check at full res too + utf3 = grid.encode('utf',resolution=1) + utf4 = grid_view.encode('utf',resolution=1) + eq_(utf3['grid'],utf4['grid']) + eq_(utf3['keys'],utf4['keys']) + eq_(utf3['data'],utf4['data']) + + eq_(resolve(utf4,0,0),None) + + # resolve some center points in the + # resampled view + utf5 = grid_view.encode('utf',resolution=4) + eq_(resolve(utf5,25,10),{"Name": "North West","__id__": 3}) + eq_(resolve(utf5,25,46),{"Name": "North East","__id__": 4}) + eq_(resolve(utf5,38,10),{"Name": "South West","__id__": 2}) + eq_(resolve(utf5,38,46),{"Name": "South East","__id__": 1}) + + + def gen_grid_for_id(pixel_key): + ds = mapnik.MemoryDatasource() + context = mapnik.Context() + context.push('Name') + f = mapnik.Feature(context,pixel_key) + f['Name'] = str(pixel_key) + f.add_geometries_from_wkt('POLYGON ((0 0, 0 256, 256 256, 256 0, 0 0))') + ds.add_feature(f) + s = mapnik.Style() + r = mapnik.Rule() + symb = mapnik.PolygonSymbolizer() + r.symbols.append(symb) + s.rules.append(r) + lyr = mapnik.Layer('Places') + lyr.datasource = ds + lyr.styles.append('places_labels') + width,height = 256,256 + m = mapnik.Map(width,height) + m.append_style('places_labels',s) + m.layers.append(lyr) + m.zoom_all() + grid = mapnik.Grid(m.width,m.height,key='__id__') + mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name']) + return grid + + def test_negative_id(): + grid = gen_grid_for_id(-1) + eq_(grid.get_pixel(128,128),-1) + utf1 = grid.encode('utf',resolution=4) + eq_(utf1['keys'],['-1']) + + def test_32bit_int_id(): + int32 = 2147483647 + grid = gen_grid_for_id(int32) + eq_(grid.get_pixel(128,128),int32) + utf1 = grid.encode('utf',resolution=4) + eq_(utf1['keys'],[str(int32)]) + max_neg = -(int32) + grid = gen_grid_for_id(max_neg) + eq_(grid.get_pixel(128,128),max_neg) + utf1 = grid.encode('utf',resolution=4) + eq_(utf1['keys'],[str(max_neg)]) + + def test_64bit_int_id(): + int64 = 0x7FFFFFFFFFFFFFFF + grid = gen_grid_for_id(int64) + eq_(grid.get_pixel(128,128),int64) + utf1 = grid.encode('utf',resolution=4) + eq_(utf1['keys'],[str(int64)]) + max_neg = -(int64) + grid = gen_grid_for_id(max_neg) + eq_(grid.get_pixel(128,128),max_neg) + utf1 = grid.encode('utf',resolution=4) + eq_(utf1['keys'],[str(max_neg)]) + + def test_id_zero(): + grid = gen_grid_for_id(0) + eq_(grid.get_pixel(128,128),0) + utf1 = grid.encode('utf',resolution=4) + eq_(utf1['keys'],['0']) + + line_expected = {"keys": ["", "1"], "data": {"1": {"Name": "1"}}, "grid": [" !", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", " !! ", "!! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! ", " ! "]} + + def test_line_rendering(): + ds = mapnik.MemoryDatasource() + context = mapnik.Context() + context.push('Name') + pixel_key = 1 + f = mapnik.Feature(context,pixel_key) + f['Name'] = str(pixel_key) + f.add_geometries_from_wkt('LINESTRING (30 10, 10 30, 40 40)') + ds.add_feature(f) + s = mapnik.Style() + r = mapnik.Rule() + symb = mapnik.LineSymbolizer() + r.symbols.append(symb) + s.rules.append(r) + lyr = mapnik.Layer('Places') + lyr.datasource = ds + lyr.styles.append('places_labels') + width,height = 256,256 + m = mapnik.Map(width,height) + m.append_style('places_labels',s) + m.layers.append(lyr) + m.zoom_all() + #mapnik.render_to_file(m,'test.png') + grid = mapnik.Grid(m.width,m.height,key='__id__') + mapnik.render_layer(m,grid,layer=0,fields=['Name']) + utf1 = grid.encode() + eq_(utf1,line_expected,show_grids('line',utf1,line_expected)) + + point_expected = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} + + def test_point_symbolizer_grid(): + width,height = 256,256 + sym = mapnik.PointSymbolizer(mapnik.PathExpression('../data/images/dummy.png')) + m = create_grid_map(width,height,sym) + ul_lonlat = mapnik.Coord(142.30,-38.20) + lr_lonlat = mapnik.Coord(143.40,-38.80) + m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) + grid = mapnik.Grid(m.width,m.height) + mapnik.render_layer(m,grid,layer=0,fields=['Name']) + utf1 = grid.encode() + eq_(utf1,point_expected,show_grids('point-sym',utf1,point_expected)) + + + # should throw because this is a mis-usage + # https://github.com/mapnik/mapnik/issues/1325 + @raises(RuntimeError) + def test_render_to_grid_multiple_times(): + # create map with two layers + m = mapnik.Map(256,256) + s = mapnik.Style() + r = mapnik.Rule() + sym = mapnik.MarkersSymbolizer() + sym.allow_overlap = True + r.symbols.append(sym) + s.rules.append(r) + m.append_style('points',s) + + # NOTE: we use a csv datasource here + # because the memorydatasource fails silently for + # queries requesting fields that do not exist in the datasource + ds1 = mapnik.Datasource(**{"type":"csv","inline":''' + wkt,Name + "POINT (143.10 -38.60)",South East'''}) + lyr1 = mapnik.Layer('One') + lyr1.datasource = ds1 + lyr1.styles.append('points') + m.layers.append(lyr1) + + ds2 = mapnik.Datasource(**{"type":"csv","inline":''' + wkt,Value + "POINT (142.48 -38.60)",South West'''}) + lyr2 = mapnik.Layer('Two') + lyr2.datasource = ds2 + lyr2.styles.append('points') + m.layers.append(lyr2) + + ul_lonlat = mapnik.Coord(142.30,-38.20) + lr_lonlat = mapnik.Coord(143.40,-38.80) + m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) + grid = mapnik.Grid(m.width,m.height) + mapnik.render_layer(m,grid,layer=0,fields=['Name']) + # should throw right here since Name will be a property now on the `grid` object + # and it is not found on the second layer + mapnik.render_layer(m,grid,layer=1,fields=['Value']) + utf1 = grid.encode() if __name__ == "__main__": setup() diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 2be5c0bca..31072c4f1 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -21,7 +21,7 @@ defaults = { 'scales':[1.0,2.0], 'agg': True, 'cairo': mapnik.has_cairo(), - 'grid': True + 'grid': mapnik.has_grid_renderer() } cairo_threshold = 10 From 88b24144b6242c22c36b33ac1614dede7bbb6d0e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 19:58:13 -0400 Subject: [PATCH 23/48] ensure libmapnik DEFINES are propagated to the python bindings --- bindings/python/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bindings/python/build.py b/bindings/python/build.py index bf9b1b2f1..7526ea48d 100644 --- a/bindings/python/build.py +++ b/bindings/python/build.py @@ -46,6 +46,8 @@ target_path_deprecated = os.path.normpath(env['PYTHON_INSTALL_LOCATION'] + os.pa py_env = env.Clone() py_env.Append(CPPPATH = env['PYTHON_INCLUDES']) +py_env.Append(CPPDEFINES = env['LIBMAPNIK_DEFINES']) + py_env['LIBS'] = ['mapnik',env['BOOST_PYTHON_LIB']] link_all_libs = env['LINKING'] == 'static' or env['RUNTIME_LINK'] == 'static' or (env['PLATFORM'] == 'Darwin' and not env['PYTHON_DYNAMIC_LOOKUP']) From e24b89486862a41dcb6625a75463ca1dfffa4dfb Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 20:00:42 -0400 Subject: [PATCH 24/48] add regression test for #1952 --- ...expression-500-100-1.0-grid-reference.json | 34 ++++++++++++++ ...h-expression-500-100-1.0-agg-reference.png | Bin 0 -> 178 bytes ...expression-500-100-1.0-cairo-reference.png | Bin 0 -> 178 bytes ...h-expression-500-100-2.0-agg-reference.png | Bin 0 -> 195 bytes ...expression-500-100-2.0-cairo-reference.png | Bin 0 -> 248 bytes .../styles/marker-path-expression.xml | 42 ++++++++++++++++++ tests/visual_tests/test.py | 3 +- 7 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 tests/visual_tests/grids/marker-path-expression-500-100-1.0-grid-reference.json create mode 100644 tests/visual_tests/images/marker-path-expression-500-100-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/marker-path-expression-500-100-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/marker-path-expression-500-100-2.0-agg-reference.png create mode 100644 tests/visual_tests/images/marker-path-expression-500-100-2.0-cairo-reference.png create mode 100644 tests/visual_tests/styles/marker-path-expression.xml diff --git a/tests/visual_tests/grids/marker-path-expression-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/marker-path-expression-500-100-1.0-grid-reference.json new file mode 100644 index 000000000..20f9c8fc2 --- /dev/null +++ b/tests/visual_tests/grids/marker-path-expression-500-100-1.0-grid-reference.json @@ -0,0 +1,34 @@ +{ + "keys": [ + "", + "1" + ], + "data": {}, + "grid": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " !! ", + " !! ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/images/marker-path-expression-500-100-1.0-agg-reference.png b/tests/visual_tests/images/marker-path-expression-500-100-1.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..df73ebf66098e1de3fbdd68c325881f4cf20f6a0 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0y~yVEh7Pr?4;s$)=b0{eTo_fKP}kkp2tA3=CTNkJ*6~ zQ%R6tFhj#^Z9fShFU-@$F{EP7+dGcD3<^9fjs*;2AM}NMOdRr_)q7rdh+e%t?C+z% zgD*h_KtRgN&l20$?{KP8dfB-|ebPJ)3t5;HL&JU6{m1G}YMGOsu@-+0UcUe& Jvd$@?2>_i@GW7re literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/marker-path-expression-500-100-1.0-cairo-reference.png b/tests/visual_tests/images/marker-path-expression-500-100-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..7efffd4792ab7a92109e7d5f740377fc899c3b87 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0y~yVEh7Pr?4;s$)=b0{eTo_fKP}kkp2(E3=HYZop^v0 zQ%R6tFhj#^Z9fShFU-@$F{EP7+dGcD3<^9fjs*;2AM}NMOdRr_)q7rdh+e%t?C+z% zgD*h_KtRgN&l20$?{KP8dfB-|ebPJ)3t5;HL&JU6{m1G}YMGOsu@-+0UcUe& Jvd$@?2>^@OGSC13 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/marker-path-expression-500-100-2.0-agg-reference.png b/tests/visual_tests/images/marker-path-expression-500-100-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..fdf07eaae55e3ea25a0e85eb8ef12e2c987593d3 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0y~yVEh7Pr?4;s$)=b0{eTo_fKP}kkp2tA3=CTNkJ*6~ zQ%R6tFhj#^Z9fShFWu9{F{EP7+dCV185DU~9HSPn{?h-{$#L>e#IBitRPveS7T>wG zO5E2|Kh{Ka4j;%I5V&*Z@mJOTll5%7N?ryo@$OfOtrB^8X|cz2IaLc;h!O^dPb~XS gPm9}?u`2r)dv<{6$%)r*Tn5Q`y85}Sb4q9e0BFNKeE + + + + + ellipse + + csv + +x,y,FILENAME,FILETYPE +2.5,2.5,rect2,svg + + + + + + + + + + frame + + csv + +x,y +0,0 +5,0 +0,5 +5,5 + + + + + \ No newline at end of file diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 31072c4f1..bdce97563 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -167,7 +167,8 @@ files = { 'style-level-compositing-tiled-0,0':{'sizes':[(512,512)],'bbox':merc_z1_bboxes['0,0']}, 'style-level-compositing-tiled-1,0':{'sizes':[(512,512)],'bbox':merc_z1_bboxes['1,0']}, 'style-level-compositing-tiled-0,1':{'sizes':[(512,512)],'bbox':merc_z1_bboxes['0,1']}, - 'style-level-compositing-tiled-1,1':{'sizes':[(512,512)],'bbox':merc_z1_bboxes['1,1']} + 'style-level-compositing-tiled-1,1':{'sizes':[(512,512)],'bbox':merc_z1_bboxes['1,1']}, + 'marker-path-expression':{} } class Reporting: From 32464c5a4b6072aab218416db18b892327c80f1a Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 23 Jul 2013 20:02:37 -0400 Subject: [PATCH 25/48] Add expected test images --- ...ng-tiled-0,0-512-512-1.0-cairo-reference.png | Bin 0 -> 18830 bytes ...ng-tiled-0,0-512-512-2.0-cairo-reference.png | Bin 0 -> 18830 bytes ...ng-tiled-0,1-512-512-1.0-cairo-reference.png | Bin 0 -> 2583 bytes ...ng-tiled-0,1-512-512-2.0-cairo-reference.png | Bin 0 -> 2583 bytes ...ng-tiled-1,0-512-512-1.0-cairo-reference.png | Bin 0 -> 22729 bytes ...ng-tiled-1,0-512-512-2.0-cairo-reference.png | Bin 0 -> 22729 bytes ...ng-tiled-1,1-512-512-1.0-cairo-reference.png | Bin 0 -> 6432 bytes ...ng-tiled-1,1-512-512-2.0-cairo-reference.png | Bin 0 -> 6432 bytes 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/visual_tests/images/style-level-compositing-tiled-0,0-512-512-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/style-level-compositing-tiled-0,0-512-512-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/style-level-compositing-tiled-0,1-512-512-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/style-level-compositing-tiled-0,1-512-512-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/style-level-compositing-tiled-1,0-512-512-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/style-level-compositing-tiled-1,0-512-512-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/style-level-compositing-tiled-1,1-512-512-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/style-level-compositing-tiled-1,1-512-512-2.0-cairo-reference.png diff --git a/tests/visual_tests/images/style-level-compositing-tiled-0,0-512-512-1.0-cairo-reference.png b/tests/visual_tests/images/style-level-compositing-tiled-0,0-512-512-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6e6d63c629af51e980401b11cc558b27f38708 GIT binary patch literal 18830 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 KMNUMnLSTZ)GR}Vh literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/style-level-compositing-tiled-0,0-512-512-2.0-cairo-reference.png b/tests/visual_tests/images/style-level-compositing-tiled-0,0-512-512-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6e6d63c629af51e980401b11cc558b27f38708 GIT binary patch literal 18830 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 KMNUMnLSTZ)GR}Vh literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/style-level-compositing-tiled-0,1-512-512-1.0-cairo-reference.png b/tests/visual_tests/images/style-level-compositing-tiled-0,1-512-512-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..cc2ac3130de533a939c8a6a0cef01f74e655057d GIT binary patch literal 2583 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 xZN~tjulEHK)9i{sv-_j9LHy literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/style-level-compositing-tiled-0,1-512-512-2.0-cairo-reference.png b/tests/visual_tests/images/style-level-compositing-tiled-0,1-512-512-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..cc2ac3130de533a939c8a6a0cef01f74e655057d GIT binary patch literal 2583 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 xZN~tjulEHK)9i{sv-_j9LHy literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/style-level-compositing-tiled-1,0-512-512-1.0-cairo-reference.png b/tests/visual_tests/images/style-level-compositing-tiled-1,0-512-512-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..cdd138e47bf580573c435d10c3114c5bbdacf880 GIT binary patch literal 22729 zcmdQ~<8vhrtiA1RZ*5y!+unL>w_Dro*7mKTHjuCQUAs6oLGy+SG7A{m`xd96|<%`z2@il6F#HPtG$Rjc)sYYdfYjTP(6 z@jN4OJR|Wu{}Q=JlR3vzIL6c1)mCO5004~vX>k!XkIV}_1hKl&MRQbUPuV#( z#{ab@DjJW#ov*Ko{&5F9L$HXlchnL8Ry#eG(PBF1Sr-(UWGo@C)BZ>T#s4qoi&#d? z;Y4S|kvAzvqhEGQEOrb+t>Xe^_4isI^cCfB2pM)oOtHsF`-r2y6wy(M@k`=U!X8`v z-!JFsmTlkt7T79{t%v`M%M-|CT3V3ZlK8I@lL$)Ael;$iPG7J2+`soAg(D4aPG6h0 z>N&owrAs%GnS5n=nSf}TPQs{@qF*YPPM=nm{?V2GMfevVw9#z&7?z8X^F=aKL!=o9 zM?9!W892&AtMA+Rm_%D7q|8Cw&gp0{n38QfnrYZC@~TsM@+kv)OK&f>T4<3^$&b|V zZD)77u?U-sq!DqTaC&we)y~9cf02{ZN99D;PSvb-JSzz3amaPDG;= zteeQl{!w*@*xj}}X99B{Ksd?5$uh3KE)=M( zBqNGmoMPmR5c^Cw(@Az_D|kZ9J!{V+iTG`?`zf?6P8S(W+n0fRLf77 zC9mmQK~8Ae{EtPXfQi34uiNJ>sOg$#0%D3Cvfu7az5;zYYJBH$1`56>U=8N-kUl{V zi>70?Z*XdtW__bswB}S-J-lg_)JHvWKg@V**X;Df4NRDBOak}aF22$Qt=;*P!eboI z2*trrLH@11@}ZV0rn;Xw)M9s+PgArE$|i2=ilpI})J@%hQI&lAN-E>4tQ7Mxd6}P- zoiZjPSGNUgoDI%GEV9=*$=+Ke#I~z}p0d4CNy$bLoPD7|A_%(gEE5@Zq3m2eC-3!! z;>h4H5K`?damTomh6B_z6O`iUFalh*ZzOQkALJvBuJc5w%yTtLq)fAU5A?5GM=`La z7ZadWE+4_T*DZ5J%BXj)Ax#XiYU8_ev3}*NaLZ62E_iv^#a)*645P{j4T zr4XTe(JDBR^$4Tn7op?xPZ{S=lpIkoqXget=GC6|z|Zcwe6H|g_-*P)suf*)m%fmRq5^ksjD@qAyN5`?yv5?~k5z-57}Fg_Sv3tn7_I zw1nq=J@++x(hL;2-c=_$>^VFFe>%=<-%9%P<7=L^|7LWplFfHLY@?c=7R9?A-aiR) z%jx*6=ft8^7r~=s*FJiGU~IyQ$}_{tcQE|WIo{-AB?y+&1up6$alG5K1ggt@3^3KJ4%X}#r1EkeJY53Us%d@}nbuV#i4*iToc zXyj2;K33PcxB+}^epg#h7(=J$1cIAGm92D`J3!br5B=*F2j=+9!IVel1*9K4DBgv# z+Yt;QWMpBAe=Pl@Qdqsp80VrM9`2&464=ek0_X6+)39t{x7oEf?rBO{G9YFmfsyyW zO#H8%r2)w)xZL-#KWlCx4$d8q>^-M_PY)PMO-sfewjTwMnY@kcND0r6WA=|@r$TmY z+#q$#DdK0m?F)m$)4zt2|46Z+FOoYslJRMFjujAcfNuuIEZiT6EI<4!CiJsE>v(zC zACR=88CTFBXA%Fh{~nQdK|b!LjM6UD%cT3~ELyVpR%Cx3c-&v*K1(oYufwBqMKk3W z*Lq$L<#;V3uK4b7^43Ob-H+W!6{MJfVTS>H|HA$i^c7+b(2ZOd5z^Bs?vt>;3Iyaz zR{mRU{2bnPzONS$lI*<{PjiGr&rCfag7f zJlF8#6?#qVl@l=%Of3Wq7h)!M`1UG$`uvs=V-g=%E$1YxGXj;1y-$H@_vSh?hct1; zqUUjw0#H0I%@cEA@=0|#rjjo$HE+LdUu#K`C;9vfi^^}a{->{F(ALgawd+dhM~xd2J_Ru0KYu`Tm@tak$FmR;=If_Q)+{ zk13wlIkfQ7$jz-AiMcDOn!Wi&4u_g}W8!l!q-;etsOm=h+}l(8HN=m_D2WFM$tUIX znu^+Do29=eOh#)tfnLdXy5s%>!6#*Gi@<Bdgu0(VgbaQ%_halzMCCMkr)Xye&LubB4Bk%_pq`s0&+3cd%h>(YDd0I?t z#YI?kY%Q8kt3#l{$L75!AOvj1F8V6_I2>OP#_DO%k7QJQ`hmGq)+hbk8H4@_Rl!mX zzLa+!H4EV(+=~$Mr|15s2KKlhy1}2)L{c`#WDnhyCmiM|j1dj<8~pO~po*0C(|rRg zL}i}kD1`Iq)MkT}>qJN%mO8zBL4UEYPDplwooACBbJ2_RZRR0J*!4(2KQ*(&$3gR4 z_hDnN7S5nS>eB#BbI9uK*VCH6*Qyi6%VB^HbMfQ1vx`olRW4DNbg}RHuL^A9w99Ep z0g{rpf*;DG{D4ZlaSe5W{voGM!3!vdAhOgeyq*{tsg#p&PNfPH$u})6a z6%k;xVTwEGmb-xUNj>xnHmUAh0fG+8KQhW0usq!3H2*SYE2czztaz5}*sh^SlR0S5 z9^Spquzk9Q0QG^0sl0m$_Zr@=$O}9Bb*4xHw0*8$Epl;4vlg#zCYZGmmn#%qwXp`+4=~ObQMU5;G|=X$*V^ zA@xVQe&YhE`0=NBJw5x6g_-6bj=}CX+_Is}A-T%Mrfda65;sMXBF+U7{PQW#FpB+|2ce`)EroT;V)LoE(goV1+tClD3-#%$Ek0CgV6HgXx zE*Z};C0a2~|Jdk)o~*@DvqFF2ul$m(ji^ACjmdrtlO}{y%l43;q6wI?Vst1Ywvdhc zNzZWWD(&+^Zji@%JRu6&bezsPD}D)(gCB6MH&AoVK!aBq_w$-sDqnSq)szK7E+N_kjqHb5ZmAeeu{F z_sMd9o!0~9BXG%sPft!;dsEYJ#CHByl4!rnJRn;Wki)j|Ing}AJ90=D$R0QyD=U>o zHoQ0PJ~%VdRaXR4D)bH6M;b6i{Km&Ysh(LWsuSUR@2xr9b;{b~n6Mh@YVeS76$#u8 z3}if8FnKxKlDz4)S$=hUek1|KG|tleo8)d02WN|<*U&C?@yy(4MAu zyY%odrh{(V>M+z6pA%Z)TqGJvF%GycPg6G<*l1Lvg>}!g+vMrUeLK7Q;Xln);B=%A zm2OfYKc+<#*mBqE;oE_js)KWyPqQi5>Oh;aomOyMe)=Z&CzpOxHwV{;>~~>TT`cw6 zVo!j1x&aux#U3e$phgJCGP3W4BwCQ%lX61@(@sTn|1{iYfq%dA(?d13WBp1L&NFbQ zgv??G%IfE-*>EnLkOQh#vn)!>^kmLgvWn_8=l-ci85dpMc4_>tyiHzQmb4iJ z;Q?H*o`{N+x)Nwl#rLz)-~HNa5#at@7`h5KT0O9;eu68wolxk|!!LqM{Zdji-_Ke7 zS>t;ml79Km_^jSFGz5Ln+@3>YyVw`;qeQqg5h7A?iRG+pm*zc%+h92U)iP%3_ZdSW zd1(X8?;_iu=c{3F5cgQL(qtifp=CpOG`o9p`X%s2Nrz40|H!0xbe`T&p#jr!z;eZU z&Me0pK_OijLUb>K7+o|`@eZ0l6%S`_B}aECd&ulpfTxs$I*fL%sLz9+YD}9h>j?oB z^k}6*GxC3{b&8srH+$)(s)PR7SIwvowp0F5Ft)wj$bQXgLXgfLF6E)e$tjr-w+P)o z>^5C~I?j>M$awmG^%Orm4We_LXzPB{CxR*U@c0J1$XI_dxW#X$|G0oP?mH#?*|emj z>z!c9GXKhqpi}qm*`3c(_t?FNL$B+~ohXrEx$(jj|7rGX9LSpZnkSb(5#IIQJSBVi zh}$M+UeafVDizZ{)BdiSE~*DFxCT;sW&qRQ|1=Px%w7G76!_xb4#a>w!1<9~x>W%? zoKM;|4^({2SwMT$n6mlq$y$qZ`J4GD|I_b^(JQ8EiAhtm9k=|0ZyXlPzGx6+ckrOT zpuDT2ztc;BEF z!g+~Aw^6{NAGv78 zV814*SKgV&j+)J7ICv~>e-@Y#u)dl*!HlpZHDYE^dyDSvk5g4Fzm zglwe_f)tFCs|Atct4^WyZ>GUZhErwi$|Wo}_$SZZ6xHc}@eE*mjC~j;G)~+gC*%|1 zm7X@Jb%h+|8ed~jZC#_LepM;i{1CXIOSjX}r>Wk^x=4p1nhYhO?tSfC(wibiawf^R zQO|rfi+F5IMLs(aI>u~>5mr6i{nD}jN4jEMC-VInP-_Ab>-Yw9{FZ1pxfW_Qp!STJ zy?Nr@VWW_Yu|a4*@nQX%YK1f!7l!e7&7m_x=JKV-AHeqSF9j>Okx1c}sTzt8E=YCm zS7Y)GmcGR5RJ2`lySC8ptxu&KSZlGnq9LV

R6D#?~HYj)?v#yf?yVxGb!8Y+`PV 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

R6D#?~HYj)?v#yf?yVxGb!8Y+`PV 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 iRJB_B!ifQl+xkDTo^Yd=0*i_O0000 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 Date: Tue, 23 Jul 2013 20:03:00 -0400 Subject: [PATCH 26/48] scons: correctly propagate DEFINES across builds --- demo/c++/build.py | 1 + src/build.py | 2 +- tests/cpp_tests/build.py | 1 + utils/pgsql2sqlite/build.py | 1 + utils/svg2png/build.py | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/demo/c++/build.py b/demo/c++/build.py index 63f1db762..403576d4d 100644 --- a/demo/c++/build.py +++ b/demo/c++/build.py @@ -34,6 +34,7 @@ demo_env = env.Clone() demo_env['CXXFLAGS'] = copy(env['LIBMAPNIK_CXXFLAGS']) +demo_env.Append(CPPDEFINES = env['LIBMAPNIK_DEFINES']) if env['HAS_CAIRO']: demo_env.PrependUnique(CPPPATH=env['CAIRO_CPPPATHS']) diff --git a/src/build.py b/src/build.py index 0c91cba7f..057fae22c 100644 --- a/src/build.py +++ b/src/build.py @@ -292,7 +292,7 @@ if env['RUNTIME_LINK'] == "static": source += glob.glob('../deps/agg/src/' + '*.cpp') # grid backend -if env['GRID_RENDERER']: # svg backend +if env['GRID_RENDERER']: source += Split( """ grid/grid.cpp diff --git a/tests/cpp_tests/build.py b/tests/cpp_tests/build.py index f967b7a70..9e1315136 100644 --- a/tests/cpp_tests/build.py +++ b/tests/cpp_tests/build.py @@ -16,6 +16,7 @@ else: test_env.AppendUnique(LIBS='dl') test_env.AppendUnique(CXXFLAGS='-g') test_env['CXXFLAGS'] = copy(test_env['LIBMAPNIK_CXXFLAGS']) + test_env.Append(CPPDEFINES = env['LIBMAPNIK_DEFINES']) if test_env['HAS_CAIRO']: test_env.PrependUnique(CPPPATH=test_env['CAIRO_CPPPATHS']) test_env.Append(CPPDEFINES = '-DHAVE_CAIRO') diff --git a/utils/pgsql2sqlite/build.py b/utils/pgsql2sqlite/build.py index e304bed2b..ac0dea107 100644 --- a/utils/pgsql2sqlite/build.py +++ b/utils/pgsql2sqlite/build.py @@ -36,6 +36,7 @@ source = Split( ) program_env['CXXFLAGS'] = copy(env['LIBMAPNIK_CXXFLAGS']) +program_env.Append(CPPDEFINES = env['LIBMAPNIK_DEFINES']) if env['HAS_CAIRO']: program_env.PrependUnique(CPPPATH=env['CAIRO_CPPPATHS']) diff --git a/utils/svg2png/build.py b/utils/svg2png/build.py index c696c4d54..95195141a 100644 --- a/utils/svg2png/build.py +++ b/utils/svg2png/build.py @@ -34,6 +34,7 @@ source = Split( ) program_env['CXXFLAGS'] = copy(env['LIBMAPNIK_CXXFLAGS']) +program_env.Append(CPPDEFINES = env['LIBMAPNIK_DEFINES']) if env['HAS_CAIRO']: program_env.PrependUnique(CPPPATH=env['CAIRO_CPPPATHS']) From 7c63b666f989358aa3911bddaee1966d44e319df Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 24 Jul 2013 11:02:06 +0100 Subject: [PATCH 27/48] cast toff_t to std::streamoff remove 'C' style comments!! --- include/mapnik/tiff_io.hpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/include/mapnik/tiff_io.hpp b/include/mapnik/tiff_io.hpp index 9303aab5a..d3c52a5c4 100644 --- a/include/mapnik/tiff_io.hpp +++ b/include/mapnik/tiff_io.hpp @@ -2,7 +2,7 @@ * * This file is part of Mapnik (c++ mapping toolkit) * - * Copyright (C) 2011 Artem Pavlenko + * Copyright (C) 2013 Artem Pavlenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -86,7 +86,7 @@ static toff_t tiff_seek_proc(thandle_t fd, toff_t off, int whence) // grow std::stringstream buffer (re: libtiff/tif_stream.cxx) std::ios::pos_type pos = out->tellp(); // second check needed for clang (libcxx doesn't set failbit when seeking beyond the current buffer size - if( out->fail() || off != pos) + if( out->fail() || static_cast(off) != pos) { std::ios::iostate old_state; std::ios::pos_type origin; @@ -146,16 +146,16 @@ static toff_t tiff_size_proc(thandle_t fd) return (toff_t)len; } -static tsize_t tiff_dummy_read_proc(thandle_t /*fd*/, tdata_t /*buf*/, tsize_t /*size*/) +static tsize_t tiff_dummy_read_proc(thandle_t , tdata_t , tsize_t) { return 0; } -static void tiff_dummy_unmap_proc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/) +static void tiff_dummy_unmap_proc(thandle_t , tdata_t , toff_t) { } -static int tiff_dummy_map_proc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/) +static int tiff_dummy_map_proc(thandle_t , tdata_t*, toff_t* ) { return 0; } @@ -193,17 +193,16 @@ void save_as_tiff(T1 & file, T2 const& image) // TODO - handle palette images // std::vector const& palette - /* - unsigned short r[256], g[256], b[256]; - for (int i = 0; i < (1 << 24); ++i) - { - r[i] = (unsigned short)palette[i * 3 + 0] << 8; - g[i] = (unsigned short)palette[i * 3 + 1] << 8; - b[i] = (unsigned short)palette[i * 3 + 2] << 8; - } - TIFFSetField(output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE); - TIFFSetField(output, TIFFTAG_COLORMAP, r, g, b); - */ + + // unsigned short r[256], g[256], b[256]; + // for (int i = 0; i < (1 << 24); ++i) + // { + // r[i] = (unsigned short)palette[i * 3 + 0] << 8; + // g[i] = (unsigned short)palette[i * 3 + 1] << 8; + // b[i] = (unsigned short)palette[i * 3 + 2] << 8; + // } + // TIFFSetField(output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE); + // TIFFSetField(output, TIFFTAG_COLORMAP, r, g, b); #ifdef HAVE_GEOTIFF GTIF* geotiff = GTIFNew(output); From eaeccc3c3a87db70b8ab53866956cb9a5cecc8d6 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 24 Jul 2013 14:24:33 -0400 Subject: [PATCH 28/48] enforce valid hsla values - refs #1954 --- include/mapnik/image_filter_types.hpp | 14 +++++++++++++- .../data/broken_maps/invalid-scale-hsla-filter.xml | 4 ++++ ...er-with-background-image-and-hsla-transform.xml | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/data/broken_maps/invalid-scale-hsla-filter.xml diff --git a/include/mapnik/image_filter_types.hpp b/include/mapnik/image_filter_types.hpp index e5f0b5db6..f6918dccd 100644 --- a/include/mapnik/image_filter_types.hpp +++ b/include/mapnik/image_filter_types.hpp @@ -26,13 +26,17 @@ // mapnik #include #include +#include + // boost #include + // stl #include #include #include // for std::back_insert_iterator + namespace mapnik { namespace filter { struct blur {}; @@ -66,7 +70,15 @@ struct scale_hsla l0(_l0), l1(_l1), a0(_a0), - a1(_a1) {} + a1(_a1) { + if (h0 < 0 || h1 > 1 || + s0 < 0 || s1 > 1 || + l0 < 0 || l1 > 1 || + a0 < 0 || a1 > 1) + { + throw config_error("scale-hsla values must be between 0 and 1"); + } + } inline bool is_identity() const { return (h0 == 0 && h1 == 1 && diff --git a/tests/data/broken_maps/invalid-scale-hsla-filter.xml b/tests/data/broken_maps/invalid-scale-hsla-filter.xml new file mode 100644 index 000000000..fec9d9eb0 --- /dev/null +++ b/tests/data/broken_maps/invalid-scale-hsla-filter.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/tests/visual_tests/styles/marker-with-background-image-and-hsla-transform.xml b/tests/visual_tests/styles/marker-with-background-image-and-hsla-transform.xml index 4d691fbaa..96a0756a3 100644 --- a/tests/visual_tests/styles/marker-with-background-image-and-hsla-transform.xml +++ b/tests/visual_tests/styles/marker-with-background-image-and-hsla-transform.xml @@ -1,7 +1,7 @@