From bd9609c370dd8aed62f088e6470dd3f4b3390190 Mon Sep 17 00:00:00 2001 From: Mickey Rose Date: Sun, 27 May 2012 23:50:09 +0200 Subject: [PATCH] new feature: transform expressions are now dynamic (cherry picked from commit 173c402b5c142310087246b0ea54dbec54edcac8) Conflicts: include/mapnik/vertex_converters.hpp src/agg/process_markers_symbolizer.cpp src/agg/process_point_symbolizer.cpp src/agg/process_polygon_pattern_symbolizer.cpp src/load_map.cpp --- bindings/python/mapnik_svg.hpp | 10 +- include/mapnik/agg_renderer.hpp | 7 + include/mapnik/attribute_collector.hpp | 48 +++- include/mapnik/marker.hpp | 15 ++ include/mapnik/parse_transform.hpp | 243 ++++++++++++++++++ include/mapnik/symbolizer.hpp | 6 +- include/mapnik/symbolizer_helpers.hpp | 15 ++ include/mapnik/transform_expression.hpp | 181 +++++++++++++ .../mapnik/transform_expression_grammar.hpp | 121 +++++++++ include/mapnik/vertex_converters.hpp | 38 +-- include/mapnik/xml_tree.hpp | 2 + src/agg/agg_renderer.cpp | 62 ++++- src/agg/process_line_pattern_symbolizer.cpp | 8 +- src/agg/process_line_symbolizer.cpp | 13 +- src/agg/process_markers_symbolizer.cpp | 40 ++- src/agg/process_point_symbolizer.cpp | 33 +-- .../process_polygon_pattern_symbolizer.cpp | 8 +- src/agg/process_polygon_symbolizer.cpp | 8 +- src/agg/process_shield_symbolizer.cpp | 10 +- src/build.py | 2 + src/cairo_renderer.cpp | 34 ++- src/grid/process_markers_symbolizer.cpp | 3 +- src/grid/process_point_symbolizer.cpp | 7 +- src/load_map.cpp | 33 +-- src/parse_transform.cpp | 46 ++++ src/save_map.cpp | 30 ++- src/symbolizer.cpp | 61 +++-- src/symbolizer_helpers.cpp | 3 +- src/transform_expression.cpp | 144 +++++++++++ src/xml_tree.cpp | 3 +- 30 files changed, 1071 insertions(+), 163 deletions(-) create mode 100644 include/mapnik/parse_transform.hpp create mode 100644 include/mapnik/transform_expression.hpp create mode 100644 include/mapnik/transform_expression_grammar.hpp create mode 100644 src/parse_transform.cpp create mode 100644 src/transform_expression.cpp diff --git a/bindings/python/mapnik_svg.hpp b/bindings/python/mapnik_svg.hpp index 123abed19..b380bf39b 100644 --- a/bindings/python/mapnik_svg.hpp +++ b/bindings/python/mapnik_svg.hpp @@ -23,10 +23,14 @@ #define MAPNIK_PYTHON_BINDING_SVG_INCLUDED // mapnik +#include #include #include #include +// boost +#include + // agg #include "agg_trans_affine.h" @@ -51,9 +55,9 @@ void set_svg_transform(T& symbolizer, std::string const& transform_wkt) << "', expected SVG transform attribute"; throw mapnik::value_error(ss.str()); } - mapnik::transform_type matrix; - tr.store_to(&matrix[0]); - symbolizer.set_image_transform(matrix); + transform_list_ptr trans = boost::make_shared(); + trans->push_back(matrix_node(tr)); + symbolizer.set_image_transform(trans); } } // end of namespace mapnik diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 3fc78e961..c7a718341 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -115,6 +115,13 @@ public: void painted(bool painted); +protected: + template + void debug_draw_box(R& buf, box2d const& extent, + double x, double y, double angle = 0.0); + void debug_draw_box(box2d const& extent, + double x, double y, double angle = 0.0); + private: buffer_type & pixmap_; boost::shared_ptr internal_buffer_; diff --git a/include/mapnik/attribute_collector.hpp b/include/mapnik/attribute_collector.hpp index ce0f16a08..694765841 100644 --- a/include/mapnik/attribute_collector.hpp +++ b/include/mapnik/attribute_collector.hpp @@ -25,6 +25,7 @@ // mapnik #include +#include // boost #include #include @@ -35,9 +36,10 @@ namespace mapnik { +template struct expression_attributes : boost::static_visitor { - explicit expression_attributes(std::set & names) + explicit expression_attributes(Container& names) : names_(names) {} void operator() (value_type const& x) const @@ -53,35 +55,35 @@ struct expression_attributes : boost::static_visitor template void operator() (binary_node const& x) const { - boost::apply_visitor(expression_attributes(names_),x.left); - boost::apply_visitor(expression_attributes(names_),x.right); + boost::apply_visitor(*this, x.left); + boost::apply_visitor(*this, x.right); } template void operator() (unary_node const& x) const { - boost::apply_visitor(expression_attributes(names_),x.expr); + boost::apply_visitor(*this, x.expr); } void operator() (regex_match_node const& x) const { - boost::apply_visitor(expression_attributes(names_),x.expr); + boost::apply_visitor(*this, x.expr); } void operator() (regex_replace_node const& x) const { - boost::apply_visitor(expression_attributes(names_),x.expr); + boost::apply_visitor(*this, x.expr); } private: - std::set& names_; + Container& names_; }; struct symbolizer_attributes : public boost::static_visitor<> { symbolizer_attributes(std::set& names) - : names_(names) {} + : names_(names), f_attr(names) {} template void operator () (T const&) const {} @@ -91,12 +93,12 @@ struct symbolizer_attributes : public boost::static_visitor<> expression_set::const_iterator it; expression_set expressions; sym.get_placement_options()->add_expressions(expressions); - expression_attributes f_attr(names_); for (it=expressions.begin(); it != expressions.end(); it++) { if (*it) boost::apply_visitor(f_attr, **it); } collect_metawriter(sym); + collect_transform(sym.get_transform()); } void operator () (point_symbolizer const& sym) @@ -107,12 +109,14 @@ struct symbolizer_attributes : public boost::static_visitor<> path_processor_type::collect_attributes(*filename_expr,names_); } collect_metawriter(sym); - + collect_transform(sym.get_image_transform()); + collect_transform(sym.get_transform()); } void operator () (line_symbolizer const& sym) { collect_metawriter(sym); + collect_transform(sym.get_transform()); } void operator () (line_pattern_symbolizer const& sym) @@ -123,11 +127,14 @@ struct symbolizer_attributes : public boost::static_visitor<> path_processor_type::collect_attributes(*filename_expr,names_); } collect_metawriter(sym); + collect_transform(sym.get_image_transform()); + collect_transform(sym.get_transform()); } void operator () (polygon_symbolizer const& sym) { collect_metawriter(sym); + collect_transform(sym.get_transform()); } void operator () (polygon_pattern_symbolizer const& sym) @@ -138,6 +145,8 @@ struct symbolizer_attributes : public boost::static_visitor<> path_processor_type::collect_attributes(*filename_expr,names_); } collect_metawriter(sym); + collect_transform(sym.get_image_transform()); + collect_transform(sym.get_transform()); } void operator () (shield_symbolizer const& sym) @@ -145,7 +154,6 @@ struct symbolizer_attributes : public boost::static_visitor<> expression_set::const_iterator it; expression_set expressions; sym.get_placement_options()->add_expressions(expressions); - expression_attributes f_attr(names_); for (it=expressions.begin(); it != expressions.end(); it++) { if (*it) boost::apply_visitor(f_attr, **it); @@ -157,11 +165,15 @@ struct symbolizer_attributes : public boost::static_visitor<> path_processor_type::collect_attributes(*filename_expr,names_); } collect_metawriter(sym); + collect_transform(sym.get_image_transform()); + collect_transform(sym.get_transform()); } void operator () (markers_symbolizer const& sym) { collect_metawriter(sym); + collect_transform(sym.get_image_transform()); + collect_transform(sym.get_transform()); } void operator () (building_symbolizer const& sym) @@ -169,20 +181,28 @@ struct symbolizer_attributes : public boost::static_visitor<> expression_ptr const& height_expr = sym.height(); if (height_expr) { - expression_attributes f_attr(names_); boost::apply_visitor(f_attr,*height_expr); } collect_metawriter(sym); + collect_transform(sym.get_transform()); } // TODO - support remaining syms private: std::set& names_; + expression_attributes > f_attr; void collect_metawriter(symbolizer_base const& sym) { metawriter_properties const& properties = sym.get_metawriter_properties(); names_.insert(properties.begin(), properties.end()); } + void collect_transform(transform_list_ptr const& trans_expr) + { + if (trans_expr) + { + transform_processor_type::collect_attributes(names_, *trans_expr); + } + } }; @@ -190,10 +210,11 @@ class attribute_collector : public boost::noncopyable { private: std::set& names_; + expression_attributes > f_attr; public: attribute_collector(std::set& names) - : names_(names) {} + : names_(names), f_attr(names) {} template void operator() (RuleType const& r) @@ -207,7 +228,6 @@ public: } expression_ptr const& expr = r.get_filter(); - expression_attributes f_attr(names_); boost::apply_visitor(f_attr,*expr); } }; diff --git a/include/mapnik/marker.hpp b/include/mapnik/marker.hpp index 0f6d316e2..d8b181ad7 100644 --- a/include/mapnik/marker.hpp +++ b/include/mapnik/marker.hpp @@ -79,6 +79,21 @@ public: { } + box2d bounding_box() const + { + if (is_vector()) + { + return (*vector_data_)->bounding_box(); + } + if (is_bitmap()) + { + double width = (*bitmap_data_)->width(); + double height = (*bitmap_data_)->height(); + return box2d(0, 0, width, height); + } + return box2d(); + } + inline double width() const { if (is_bitmap()) diff --git a/include/mapnik/parse_transform.hpp b/include/mapnik/parse_transform.hpp new file mode 100644 index 000000000..363ecc8fd --- /dev/null +++ b/include/mapnik/parse_transform.hpp @@ -0,0 +1,243 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2012 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_PARSE_TRANSFORM_HPP +#define MAPNIK_PARSE_TRANSFORM_HPP + +// mapnik +#include +#include +#include +#include +#include +#include +#include + +// boost +#include +#include +#include +#include + +// agg +#include + +namespace mapnik { + +template struct expression_attributes; +template struct transform_expression_grammar; + +MAPNIK_DECL transform_list_ptr parse_transform(std::string const & str); +MAPNIK_DECL bool parse_transform(transform_list& list, + std::string const & str, + transform_expression_grammar const& g); + + +template +struct transform_processor +{ + typedef T feature_type; + typedef agg::trans_affine transform_type; + + template + struct attribute_collector : boost::static_visitor + { + expression_attributes collect_; + + attribute_collector(Container& names) + : collect_(names) {} + + void operator() (identity_node const& node) const + { + boost::ignore_unused_variable_warning(node); + } + + void operator() (matrix_node const& node) const + { + boost::apply_visitor(collect_, node.a_); + boost::apply_visitor(collect_, node.b_); + boost::apply_visitor(collect_, node.c_); + boost::apply_visitor(collect_, node.d_); + boost::apply_visitor(collect_, node.e_); + boost::apply_visitor(collect_, node.f_); + } + + void operator() (translate_node const& node) const + { + boost::apply_visitor(collect_, node.tx_); + boost::apply_visitor(collect_, node.ty_); + } + + void operator() (scale_node const& node) const + { + boost::apply_visitor(collect_, node.sx_); + boost::apply_visitor(collect_, node.sy_); + } + + void operator() (rotate_node const& node) const + { + boost::apply_visitor(collect_, node.angle_); + boost::apply_visitor(collect_, node.cx_); + boost::apply_visitor(collect_, node.cy_); + } + + void operator() (skewX_node const& node) const + { + boost::apply_visitor(collect_, node.angle_); + } + + void operator() (skewY_node const& node) const + { + boost::apply_visitor(collect_, node.angle_); + } + }; + + struct node_evaluator : boost::static_visitor + { + node_evaluator(transform_type& tr, feature_type const& feat) + : transform_(tr), feature_(feat) {} + + void operator() (identity_node const& node) + { + boost::ignore_unused_variable_warning(node); + } + + void operator() (matrix_node const& node) + { + double a = eval(node.a_); + double b = eval(node.b_); + double c = eval(node.c_); + double d = eval(node.d_); + double e = eval(node.e_); + double f = eval(node.f_); + transform_.multiply(agg::trans_affine(a, b, c, d, e, f)); + } + + void operator() (translate_node const& node) + { + double tx = eval(node.tx_); + double ty = eval(node.ty_, 0.0); + transform_.translate(tx, ty); + } + + void operator() (scale_node const& node) + { + double sx = eval(node.sx_); + double sy = eval(node.sy_, sx); + transform_.scale(sx, sy); + } + + void operator() (rotate_node const& node) + { + double angle = deg2rad(eval(node.angle_)); + double cx = eval(node.cx_, 0.0); + double cy = eval(node.cy_, 0.0); + transform_.translate(-cx, -cy); + transform_.rotate(angle); + transform_.translate(cx, cy); + } + + void operator() (skewX_node const& node) + { + double angle = deg2rad(eval(node.angle_)); + transform_.multiply(agg::trans_affine_skewing(angle, 0.0)); + } + + void operator() (skewY_node const& node) + { + double angle = deg2rad(eval(node.angle_)); + transform_.multiply(agg::trans_affine_skewing(0.0, angle)); + } + + private: + + static double deg2rad(double d) + { + return d * M_PI / 180.0; + } + + double eval(expr_node const& x) const + { + mapnik::evaluate e(feature_); + return boost::apply_visitor(e, x).to_double(); + } + + double eval(expr_node const& x, double def) const + { + return is_null(x) ? def : eval(x); + } + + transform_type& transform_; + feature_type const& feature_; + }; + + template + static void collect_attributes(Container& names, + transform_list const& list) + { + attribute_collector collect(names); + + BOOST_FOREACH (transform_node const& node, list) + { + boost::apply_visitor(collect, *node); + } + } + + static void evaluate(transform_type& tr, feature_type const& feat, + transform_list const& list) + { + node_evaluator eval(tr, feat); + + #ifdef MAPNIK_LOG + MAPNIK_LOG_DEBUG(transform) << "transform: begin with " << to_string(matrix_node(tr)); + #endif + + BOOST_REVERSE_FOREACH (transform_node const& node, list) + { + boost::apply_visitor(eval, *node); + #ifdef MAPNIK_LOG + MAPNIK_LOG_DEBUG(transform) << "transform: apply " << to_string(*node); + MAPNIK_LOG_DEBUG(transform) << "transform: result " << to_string(matrix_node(tr)); + #endif + } + + #ifdef MAPNIK_LOG + MAPNIK_LOG_DEBUG(transform) << "transform: end"; + #endif + } + + static std::string to_string(transform_node const& node) + { + return to_expression_string(node); + } + + static std::string to_string(transform_list const& list) + { + return to_expression_string(list); + } +}; + +typedef mapnik::transform_processor transform_processor_type; + +} // namespace mapnik + +#endif // MAPNIK_PARSE_TRANSFORM_HPP diff --git a/include/mapnik/symbolizer.hpp b/include/mapnik/symbolizer.hpp index 08aab02b4..2141d4833 100644 --- a/include/mapnik/symbolizer.hpp +++ b/include/mapnik/symbolizer.hpp @@ -28,6 +28,7 @@ #include #include #include +#include // boost #include @@ -36,7 +37,10 @@ namespace mapnik { -typedef boost::array transform_type; +typedef transform_list_ptr transform_type; + +MAPNIK_DECL void evaluate_transform(agg::trans_affine& tr, Feature const& feature, + transform_type const& trans_expr); class Map; diff --git a/include/mapnik/symbolizer_helpers.hpp b/include/mapnik/symbolizer_helpers.hpp index 1f5980dfb..aafb8864b 100644 --- a/include/mapnik/symbolizer_helpers.hpp +++ b/include/mapnik/symbolizer_helpers.hpp @@ -150,6 +150,21 @@ public: init_marker(); } + box2d const& get_marker_extent() const + { + return marker_ext_; + } + + double get_marker_height() const + { + return marker_h_; + } + + double get_marker_width() const + { + return marker_w_; + } + bool next(); pixel_position get_marker_position(text_path const& p); marker & get_marker() const; diff --git a/include/mapnik/transform_expression.hpp b/include/mapnik/transform_expression.hpp new file mode 100644 index 000000000..b55a872ba --- /dev/null +++ b/include/mapnik/transform_expression.hpp @@ -0,0 +1,181 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2012 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_TRANSFORM_EXPRESSION_HPP +#define MAPNIK_TRANSFORM_EXPRESSION_HPP + +// mapnik +#include + +// boost +#include +#include +#include + +// agg +#include + +// stl +#include + +namespace mapnik { + +struct identity_node {}; + +struct matrix_node +{ + expr_node a_; + expr_node b_; + expr_node c_; + expr_node d_; + expr_node e_; + expr_node f_; + + explicit matrix_node(double const* m) + : a_(m[0]), b_(m[1]), c_(m[2]), d_(m[3]), e_(m[4]), f_(m[5]) {} + + explicit matrix_node(agg::trans_affine const& m) + : a_(m.sx), b_(m.shy), c_(m.shx), d_(m.sy), e_(m.tx), f_(m.ty) {} + + matrix_node(expr_node const& a, expr_node const& b, expr_node const& c, + expr_node const& d, expr_node const& e, expr_node const& f) + : a_(a), b_(b), c_(c), d_(d), e_(e), f_(f) {} +}; + +struct translate_node +{ + expr_node tx_; + expr_node ty_; + + translate_node(expr_node const& tx, + boost::optional const& ty) + : tx_(tx) + , ty_(ty ? *ty : value_null()) {} +}; + +struct scale_node +{ + expr_node sx_; + expr_node sy_; + + scale_node(expr_node const& sx, + boost::optional const& sy) + : sx_(sx) + , sy_(sy ? *sy : value_null()) {} +}; + +struct rotate_node +{ + expr_node angle_; + expr_node cx_; + expr_node cy_; + + rotate_node(expr_node const& angle, + boost::optional const& cx, + boost::optional const& cy) + : angle_(angle) + , cx_(cx ? *cx : value_null()) + , cy_(cy ? *cy : value_null()) {} +}; + +struct skewX_node +{ + expr_node angle_; + + explicit skewX_node(expr_node const& angle) + : angle_(angle) {} +}; + +struct skewY_node +{ + expr_node angle_; + + explicit skewY_node(expr_node const& angle) + : angle_(angle) {} +}; + +namespace detail { + + // boost::spirit::traits::clear(T& val) [with T = boost::variant<...>] + // attempts to assign to the variant's current value a default-constructed + // value ot the same type, which not only requires that each value-type is + // default-constructible, but also makes little sense with our variant of + // transform nodes... + + typedef boost::variant< identity_node + , matrix_node + , translate_node + , scale_node + , rotate_node + , skewX_node + , skewY_node + > transform_variant; + + // ... thus we wrap the variant-type in a distinct type and provide + // a custom clear overload, which resets the value to identity_node + + struct transform_node + { + transform_variant base_; + + transform_node() + : base_() {} + + template + transform_node(T const& val) + : base_(val) {} + + template + transform_node& operator= (T const& val) + { + base_ = val; + return *this; + } + + transform_variant const& operator* () const + { + return base_; + } + + transform_variant& operator* () + { + return base_; + } + }; + + inline void clear(transform_node& val) + { + val.base_ = identity_node(); + } + +} // namespace detail + +typedef detail::transform_node transform_node; +typedef std::vector transform_list; +typedef boost::shared_ptr transform_list_ptr; + +MAPNIK_DECL std::string to_expression_string(transform_node const& node); +MAPNIK_DECL std::string to_expression_string(transform_list const& list); + +} // namespace mapnik + +#endif // MAPNIK_TRANSFORM_EXPRESSION_HPP diff --git a/include/mapnik/transform_expression_grammar.hpp b/include/mapnik/transform_expression_grammar.hpp new file mode 100644 index 000000000..fe09f4e5c --- /dev/null +++ b/include/mapnik/transform_expression_grammar.hpp @@ -0,0 +1,121 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2012 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_TRANSFORM_EXPRESSION_GRAMMAR_HPP +#define MAPNIK_TRANSFORM_EXPRESSION_GRAMMAR_HPP + +// mapnik +#include +#include + +// spirit +#include +#include + +namespace mapnik { + + namespace qi = boost::spirit::qi; + + template + struct transform_expression_grammar + : qi::grammar + { + explicit transform_expression_grammar(expression_grammar const& g) + : transform_expression_grammar::base_type(start) + { + using boost::phoenix::construct; + using qi::_a; using qi::_1; using qi::_4; + using qi::_b; using qi::_2; using qi::_5; + using qi::_c; using qi::_3; using qi::_6; + using qi::_val; + using qi::char_; + using qi::lit; + using qi::no_case; + using qi::no_skip; + + start = transform_ % no_skip[char_(", ")] ; + + transform_ = matrix | translate | scale | rotate | skewX | skewY ; + + matrix = no_case[lit("matrix")] + >> (lit('(') + >> expr >> -lit(',') + >> expr >> -lit(',') + >> expr >> -lit(',') + >> expr >> -lit(',') + >> expr >> -lit(',') + >> expr >> lit(')')) + [ _val = construct(_1,_2,_3,_4,_5,_6) ]; + + translate = no_case[lit("translate")] + >> (lit('(') + >> expr >> -lit(',') + >> -expr >> lit(')')) + [ _val = construct(_1,_2) ]; + + scale = no_case[lit("scale")] + >> (lit('(') + >> expr >> -lit(',') + >> -expr >> lit(')')) + [ _val = construct(_1,_2) ]; + + rotate = no_case[lit("rotate")] + >> lit('(') + >> expr[_a = _1] >> -lit(',') + >> -(expr [_b = _1] >> -lit(',') >> expr[_c = _1]) + >> lit(')') + [ _val = construct(_a,_b,_c) ]; + + skewX = no_case[lit("skewX")] + >> lit('(') + >> expr [ _val = construct(_1) ] + >> lit(')'); + + skewY = no_case[lit("skewY")] + >> lit('(') + >> expr [ _val = construct(_1) ] + >> lit(')'); + + expr = g.expr.alias(); + } + + typedef qi::locals, + boost::optional + > rotate_locals; + typedef qi::rule node_rule; + typedef qi::rule list_rule; + + // rules + typename expression_grammar::rule_type expr; + qi::rule start; + qi::rule transform_; + qi::rule matrix; + qi::rule translate; + qi::rule scale; + qi::rule rotate; + qi::rule skewX; + qi::rule skewY; + }; + +} // namespace mapnik + +#endif // MAPNIK_TRANSFORM_EXPRESSION_GRAMMAR_HPP diff --git a/include/mapnik/vertex_converters.hpp b/include/mapnik/vertex_converters.hpp index 9a6aaa420..ed57a5a03 100644 --- a/include/mapnik/vertex_converters.hpp +++ b/include/mapnik/vertex_converters.hpp @@ -120,9 +120,8 @@ struct converter_traits template static void setup(geometry_type & geom, Args const& args) { - typename boost::mpl::at >::type sym = boost::fusion::at_c<2>(args); - double scale_factor = boost::fusion::at_c<5>(args); + double scale_factor = boost::fusion::at_c<6>(args); stroke const& stroke_ = sym.get_stroke(); dash_array const& d = stroke_.get_dash_array(); dash_array::const_iterator itr = d.begin(); @@ -149,7 +148,7 @@ struct converter_traits stroke const& stroke_ = sym.get_stroke(); set_join_caps(stroke_,geom); geom.generator().miter_limit(stroke_.get_miterlimit()); - double scale_factor = boost::fusion::at_c<5>(args); + double scale_factor = boost::fusion::at_c<6>(args); geom.generator().width(stroke_.get_width() * scale_factor); } }; @@ -179,10 +178,8 @@ struct converter_traits template static void setup(geometry_type & geom, Args const& args) { - typename boost::mpl::at >::type tr = boost::fusion::at_c<3>(args); - typename boost::mpl::at >::type prj_trans = boost::fusion::at_c<4>(args); - geom.set_proj_trans(prj_trans); - geom.set_trans(tr); + geom.set_proj_trans(boost::fusion::at_c<4>(args)); + geom.set_trans(boost::fusion::at_c<3>(args)); } }; @@ -191,21 +188,19 @@ template struct converter_traits { typedef T geometry_type; + typedef agg::conv_transform + conv_base_type; - struct conv_type : public agg::conv_transform + struct conv_type : public conv_base_type { - agg::trans_affine trans_; - conv_type(geometry_type& geom) - : agg::conv_transform(geom, trans_) {} + : conv_base_type(geom, agg::trans_affine::identity) {} }; template static void setup(geometry_type & geom, Args & args) { - typename boost::mpl::at >::type sym = boost::fusion::at_c<2>(args); - boost::array const& m = sym.get_transform(); - geom.trans_.load_from(&m[0]); + geom.transformer(boost::fusion::at_c<5>(args)); } }; @@ -303,15 +298,16 @@ struct dispatcher -template +template struct vertex_converter : private boost::noncopyable { typedef C conv_types; typedef B bbox_type; typedef R rasterizer_type; typedef S symbolizer_type; - typedef P proj_trans_type; typedef T trans_type; + typedef P proj_trans_type; + typedef A affine_trans_type; typedef typename boost::fusion::vector < bbox_type const&, @@ -319,16 +315,20 @@ struct vertex_converter : private boost::noncopyable symbolizer_type const&, trans_type const&, proj_trans_type const&, + affine_trans_type const&, double //scale-factor > args_type; vertex_converter(bbox_type const& b, rasterizer_type & ras, symbolizer_type const& sym, trans_type & tr, proj_trans_type const& prj_trans, + affine_trans_type const& affine_trans, double scale_factor) - : disp_(args_type(boost::cref(b),boost::ref(ras), - boost::cref(sym),boost::cref(tr), - boost::cref(prj_trans),scale_factor)) {} + : disp_(args_type(boost::cref(b), boost::ref(ras), + boost::cref(sym), boost::cref(tr), + boost::cref(prj_trans), + boost::cref(affine_trans), + scale_factor)) {} template void apply(Geometry & geom) diff --git a/include/mapnik/xml_tree.hpp b/include/mapnik/xml_tree.hpp index d5beb9614..72dfd2c5b 100644 --- a/include/mapnik/xml_tree.hpp +++ b/include/mapnik/xml_tree.hpp @@ -26,6 +26,7 @@ #include #include #include +#include // boost #include @@ -57,6 +58,7 @@ public: mapnik::css_color_grammar color_grammar; mapnik::expression_grammar expr_grammar; path_expression_grammar path_expr_grammar; + transform_expression_grammar transform_expr_grammar; }; } //ns mapnik diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index f2bf6f0d4..0b16b3e46 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -277,7 +277,7 @@ void agg_renderer::render_marker(pixel_position const& pos, marker const& mar mtx *= tr; mtx *= agg::trans_affine_scaling(scale_factor_); // render the marker at the center of the marker box - mtx.translate(pos.x+0.5 * marker.width(), pos.y+0.5 * marker.height()); + mtx.translate(pos.x, pos.y); using namespace mapnik::svg; vertex_stl_adapter stl_storage((*marker.get_vector_data())->source()); svg_path_adapter svg_path(stl_storage); @@ -291,10 +291,12 @@ void agg_renderer::render_marker(pixel_position const& pos, marker const& mar } else { + double cx = 0.5 * (*marker.get_bitmap_data())->width(); + double cy = 0.5 * (*marker.get_bitmap_data())->height(); composite(current_buffer_->data(), **marker.get_bitmap_data(), comp_op, opacity, - boost::math::iround(pos.x), - boost::math::iround(pos.y), + boost::math::iround(pos.x - cx), + boost::math::iround(pos.y - cy), false, false); } } @@ -305,5 +307,57 @@ void agg_renderer::painted(bool painted) pixmap_.painted(painted); } -template class agg_renderer; +template +void agg_renderer::debug_draw_box(box2d const& box, + double x, double y, double angle) +{ + agg::rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_ * 4); + debug_draw_box(buf, box, x, y, angle); +} + +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::renderer_base renderer_base; + typedef agg::renderer_scanline_aa_solid renderer_type; + + agg::scanline_p8 sl_line; + pixfmt pixf(buf); + renderer_base renb(pixf); + renderer_type ren(renb); + + // compute tranformation matrix + agg::trans_affine_rotation tr(angle); + tr.translate(x, y); + + // prepare path + agg::path_storage pbox; + pbox.start_new_path(); + pbox.move_to(box.minx(), box.miny()); + pbox.line_to(box.maxx(), box.miny()); + pbox.line_to(box.maxx(), box.maxy()); + pbox.line_to(box.minx(), box.maxy()); + pbox.line_to(box.minx(), box.miny()); + + // prepare stroke with applied transformation + typedef agg::conv_transform conv_transform; + typedef agg::conv_stroke conv_stroke; + conv_transform tbox(pbox, tr); + conv_stroke sbox(tbox); + sbox.generator().width(1.0 * scale_factor_); + + // render the outline + ras_ptr->reset(); + ras_ptr->add_path(sbox); + ren.color(agg::rgba8(0x33, 0x33, 0xff, 0xcc)); // blue is fine + agg::render_scanlines(*ras_ptr, sl_line, ren); +} + +template class agg_renderer; +template void agg_renderer::debug_draw_box( + agg::rendering_buffer& buf, + box2d const& box, + double x, double y, double angle); } diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index c33cedb02..72ad65bd1 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -94,9 +94,13 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, renderer_type ren(ren_base, pattern); rasterizer_type ras(ren); + agg::trans_affine tr; + evaluate_transform(tr, *feature, sym.get_transform()); + typedef boost::mpl::vector conv_types; - vertex_converter,rasterizer_type,line_pattern_symbolizer, proj_transform, CoordTransform, conv_types> - converter(ext,ras,sym,t_,prj_trans,scale_factor_); + vertex_converter, rasterizer_type, line_pattern_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(ext,ras,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index 32924a998..4966d4907 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -76,6 +76,9 @@ void agg_renderer::process(line_symbolizer const& sym, pixfmt_comp_type pixf(buf); renderer_base renb(pixf); + agg::trans_affine tr; + evaluate_transform(tr, *feature, sym.get_transform()); + if (sym.get_rasterizer() == RASTERIZER_FAST) { typedef agg::renderer_outline_aa renderer_type; @@ -89,8 +92,9 @@ void agg_renderer::process(line_symbolizer const& sym, set_join_caps_aa(stroke_,ras); typedef boost::mpl::vector conv_types; - vertex_converter,rasterizer_type,line_symbolizer, proj_transform, CoordTransform,conv_types> - converter(query_extent_,ras,sym,t_,prj_trans,scaled); + vertex_converter, rasterizer_type, line_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(query_extent_,ras,sym,t_,prj_trans,tr,scaled); if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform @@ -111,8 +115,9 @@ void agg_renderer::process(line_symbolizer const& sym, else { typedef boost::mpl::vector conv_types; - vertex_converter,rasterizer,line_symbolizer, proj_transform, CoordTransform,conv_types> - converter(query_extent_,*ras_ptr,sym,t_,prj_trans,scale_factor_); + vertex_converter, rasterizer, line_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index df498436c..cd23e8431 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -73,8 +73,7 @@ void agg_renderer::process(markers_symbolizer const& sym, renderer_base renb(pixf); renderer_type ren(renb); agg::trans_affine tr; - boost::array const& m = sym.get_image_transform(); - tr.load_from(&m[0]); + evaluate_transform(tr, *feature, sym.get_image_transform()); tr = agg::trans_affine_scaling(scale_factor_) * tr; std::string filename = path_processor_type::evaluate(*sym.get_filename(), *feature); marker_placement_e placement_method = sym.get_marker_placement(); @@ -94,17 +93,12 @@ void agg_renderer::process(markers_symbolizer const& sym, } boost::optional marker = (*mark)->get_vector_data(); box2d const& bbox = (*marker)->bounding_box(); - double x1 = bbox.minx(); - double y1 = bbox.miny(); - double x2 = bbox.maxx(); - double y2 = bbox.maxy(); - double w = (*mark)->width(); - double h = (*mark)->height(); + coord2d const center = bbox.center(); + + agg::trans_affine_translation const recenter(-center.x, -center.y); + agg::trans_affine const recenter_tr = recenter * tr; + box2d extent = bbox * recenter_tr; - agg::trans_affine recenter = agg::trans_affine_translation(-0.5*(x1+x2),-0.5*(y1+y2)); - tr.transform(&x1,&y1); - tr.transform(&x2,&y2); - box2d extent(x1,y1,x2,y2); using namespace mapnik::svg; vertex_stl_adapter stl_storage((*marker)->source()); svg_path_adapter svg_path(stl_storage); @@ -132,7 +126,11 @@ void agg_renderer::process(markers_symbolizer const& sym, detector_->has_placement(extent)) { - render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, sym.get_opacity(), sym.comp_op()); + render_marker(pixel_position(x, y), **mark, tr, sym.get_opacity(), sym.comp_op()); + + if (/* DEBUG */ 0) { + debug_draw_box(buf, extent, 0, 0, 0.0); + } // TODO - impl this for markers? //if (!sym.get_ignore_placement()) @@ -154,8 +152,22 @@ void agg_renderer::process(markers_symbolizer const& sym, while (placement.get_point(&x, &y, &angle)) { - agg::trans_affine matrix = recenter * tr *agg::trans_affine_rotation(angle) * agg::trans_affine_translation(x, y); + agg::trans_affine matrix = recenter_tr; + matrix.rotate(angle); + matrix.translate(x, y); svg_renderer.render(*ras_ptr, sl, renb, matrix, sym.get_opacity(),bbox); + + if (/* DEBUG */ 0) { + agg::trans_affine_rotation r(angle); + debug_draw_box(buf, extent * r, x, y, 0.0); + // note: debug_draw_box(buf, extent, x, y, angle) + // would draw a rotated box showing the proper + // bounds of the marker, while the above will + // draw the box used for collision detection, + // which embraces the rotated extent but isn't + // rotated itself + } + if (writer.first) { //writer.first->add_box(label_ext, feature, t_, writer.second); diff --git a/src/agg/process_point_symbolizer.cpp b/src/agg/process_point_symbolizer.cpp index 6d545e214..28acacc44 100644 --- a/src/agg/process_point_symbolizer.cpp +++ b/src/agg/process_point_symbolizer.cpp @@ -63,26 +63,15 @@ void agg_renderer::process(point_symbolizer const& sym, if (marker) { - double w = (*marker)->width(); - double h = (*marker)->height(); + box2d const& bbox = (*marker)->bounding_box(); + coord2d const center = bbox.center(); + agg::trans_affine tr; - boost::array const& m = sym.get_image_transform(); - tr.load_from(&m[0]); - double px0 = - 0.5 * w; - double py0 = - 0.5 * h; - double px1 = 0.5 * w; - double py1 = 0.5 * h; - double px2 = px1; - double py2 = py0; - double px3 = px0; - double py3 = py1; - tr.transform(&px0,&py0); - tr.transform(&px1,&py1); - tr.transform(&px2,&py2); - tr.transform(&px3,&py3); - box2d label_ext (px0, py0, px1, py1); - label_ext.expand_to_include(px2, py2); - label_ext.expand_to_include(px3, py3); + evaluate_transform(tr, *feature, sym.get_image_transform()); + + agg::trans_affine_translation const recenter(-center.x, -center.y); + agg::trans_affine const recenter_tr = recenter * tr; + box2d label_ext = bbox * recenter_tr; for (unsigned i=0; inum_geometries(); ++i) { @@ -103,7 +92,11 @@ void agg_renderer::process(point_symbolizer const& sym, detector_->has_placement(label_ext)) { - render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**marker,tr, sym.get_opacity(), sym.comp_op()); + render_marker(pixel_position(x, y), **marker, tr, sym.get_opacity(), sym.comp_op()); + + if (/* DEBUG */ 0) { + debug_draw_box(label_ext, 0, 0, 0.0); + } if (!sym.get_ignore_placement()) detector_->insert(label_ext); diff --git a/src/agg/process_polygon_pattern_symbolizer.cpp b/src/agg/process_polygon_pattern_symbolizer.cpp index 62a382174..6833c1aa3 100644 --- a/src/agg/process_polygon_pattern_symbolizer.cpp +++ b/src/agg/process_polygon_pattern_symbolizer.cpp @@ -136,10 +136,14 @@ void agg_renderer::process(polygon_pattern_symbolizer const& sym, agg::span_allocator sa; renderer_type rp(renb,sa, sg); + agg::trans_affine tr; + evaluate_transform(tr, *feature, sym.get_transform()); + box2d inflated_extent = query_extent_ * 1.0; typedef boost::mpl::vector conv_types; - vertex_converter,rasterizer,polygon_pattern_symbolizer, proj_transform, CoordTransform,conv_types> - converter(inflated_extent,*ras_ptr,sym,t_,prj_trans, scale_factor_); + vertex_converter, rasterizer, polygon_pattern_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform diff --git a/src/agg/process_polygon_symbolizer.cpp b/src/agg/process_polygon_symbolizer.cpp index 7e7c92c95..9998cecae 100644 --- a/src/agg/process_polygon_symbolizer.cpp +++ b/src/agg/process_polygon_symbolizer.cpp @@ -50,9 +50,13 @@ void agg_renderer::process(polygon_symbolizer const& sym, box2d inflated_extent = query_extent_ * 1.0; + agg::trans_affine tr; + evaluate_transform(tr, *feature, sym.get_transform()); + typedef boost::mpl::vector conv_types; - vertex_converter,rasterizer,polygon_symbolizer, proj_transform, CoordTransform,conv_types> - converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,scale_factor_); + vertex_converter, rasterizer, polygon_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index db38b5778..a8d8ccf46 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -52,7 +52,15 @@ void agg_renderer::process(shield_symbolizer const& sym, placements_type &placements = helper.placements(); for (unsigned int ii = 0; ii < placements.size(); ++ii) { - render_marker(helper.get_marker_position(placements[ii]), + // get_marker_position returns (minx,miny) corner position, + // while (currently only) agg_renderer::render_marker newly + // expects center position; + // until all renderers and shield_symbolizer_helper are + // modified accordingly, we must adjust the position here + pixel_position pos = helper.get_marker_position(placements[ii]); + pos.x += 0.5 * helper.get_marker_width(); + pos.y += 0.5 * helper.get_marker_height(); + render_marker(pos, helper.get_marker(), helper.get_image_transform(), sym.get_opacity(), diff --git a/src/build.py b/src/build.py index a75c0bc13..2e0e8183f 100644 --- a/src/build.py +++ b/src/build.py @@ -110,6 +110,7 @@ source = Split( deepcopy.cpp expression_string.cpp expression.cpp + transform_expression.cpp feature_kv_iterator.cpp feature_type_style.cpp font_engine_freetype.cpp @@ -126,6 +127,7 @@ source = Split( load_map.cpp memory.cpp parse_path.cpp + parse_transform.cpp palette.cpp placement_finder.cpp plugin.cpp diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index 1c8e87536..958faa4ee 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -833,9 +833,13 @@ void cairo_renderer_base::start_map_processing(Map const& map) context.set_operator(sym.comp_op()); context.set_color(sym.get_fill(), sym.get_opacity()); + agg::trans_affine tr; + evaluate_transform(tr, *feature, sym.get_transform()); + typedef boost::mpl::vector conv_types; - vertex_converter,cairo_context,polygon_symbolizer, proj_transform, CoordTransform, conv_types> - converter(query_extent_,context,sym,t_,prj_trans,1.0); + vertex_converter, cairo_context, polygon_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(query_extent_,context,sym,t_,prj_trans,tr,1.0); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform @@ -980,10 +984,13 @@ void cairo_renderer_base::start_map_processing(Map const& map) context.set_dash(stroke_.get_dash_array()); } + agg::trans_affine tr; + evaluate_transform(tr, *feature, sym.get_transform()); typedef boost::mpl::vector conv_types; - vertex_converter,cairo_context,line_symbolizer, proj_transform, CoordTransform,conv_types> - converter(query_extent_,context ,sym,t_,prj_trans,1.0); + vertex_converter, cairo_context, line_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(query_extent_,context,sym,t_,prj_trans,tr,1.0); if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform @@ -1131,6 +1138,9 @@ void cairo_renderer_base::start_map_processing(Map const& map) marker.reset(boost::make_shared()); } + agg::trans_affine mtx; + evaluate_transform(mtx, *feature, sym.get_image_transform()); + if (marker) { for (unsigned i = 0; i < feature->num_geometries(); ++i) @@ -1157,10 +1167,6 @@ void cairo_renderer_base::start_map_processing(Map const& map) if (sym.get_allow_overlap() || detector_.has_placement(label_ext)) { - agg::trans_affine mtx; - boost::array const& m = sym.get_image_transform(); - mtx.load_from(&m[0]); - render_marker(pixel_position(px,py),**marker, mtx, sym.get_opacity()); if (!sym.get_ignore_placement()) @@ -1293,9 +1299,13 @@ void cairo_renderer_base::start_map_processing(Map const& map) context.set_pattern(pattern); + agg::trans_affine tr; + evaluate_transform(tr, *feature, sym.get_transform()); + typedef boost::mpl::vector conv_types; - vertex_converter,cairo_context,polygon_pattern_symbolizer, proj_transform, CoordTransform, conv_types> - converter(query_extent_,context,sym,t_,prj_trans,1.0); + vertex_converter, cairo_context, polygon_pattern_symbolizer, + CoordTransform, proj_transform, agg::trans_affine, conv_types> + converter(query_extent_,context,sym,t_,prj_trans,tr,1.0); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform @@ -1371,8 +1381,8 @@ void cairo_renderer_base::start_map_processing(Map const& map) typedef coord_transform path_type; agg::trans_affine tr; - boost::array const& m = sym.get_image_transform(); - tr.load_from(&m[0]); + evaluate_transform(tr, *feature, sym.get_image_transform()); + // TODO - use this? //tr = agg::trans_affine_scaling(scale_factor_) * tr; std::string filename = path_processor_type::evaluate(*sym.get_filename(), *feature); diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index 4d83e521a..b41bfc1c1 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -69,8 +69,7 @@ void grid_renderer::process(markers_symbolizer const& sym, ras_ptr->reset(); agg::trans_affine tr; - boost::array const& m = sym.get_image_transform(); - tr.load_from(&m[0]); + evaluate_transform(tr, *feature, sym.get_image_transform()); unsigned int res = pixmap_.get_resolution(); tr = agg::trans_affine_scaling(scale_factor_*(1.0/res)) * tr; std::string filename = path_processor_type::evaluate(*sym.get_filename(), *feature); diff --git a/src/grid/process_point_symbolizer.cpp b/src/grid/process_point_symbolizer.cpp index b5667469e..a17a86842 100644 --- a/src/grid/process_point_symbolizer.cpp +++ b/src/grid/process_point_symbolizer.cpp @@ -55,6 +55,9 @@ void grid_renderer::process(point_symbolizer const& sym, if (marker) { + agg::trans_affine tr; + evaluate_transform(tr, *feature, sym.get_image_transform()); + for (unsigned i=0; inum_geometries(); ++i) { geometry_type const& geom = feature->get_geometry(i); @@ -78,10 +81,6 @@ void grid_renderer::process(point_symbolizer const& sym, if (sym.get_allow_overlap() || detector_.has_placement(label_ext)) { - agg::trans_affine tr; - boost::array const& m = sym.get_image_transform(); - tr.load_from(&m[0]); - render_marker(feature, pixmap_.get_resolution(), pixel_position(px, py), **marker, tr, diff --git a/src/load_map.cpp b/src/load_map.cpp index 27f1ea84c..bc9a1e5f3 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -838,8 +839,8 @@ void map_parser::parse_symbolizer_base(symbolizer_base &sym, xml_node const &pt) optional transform_wkt = pt.get_opt_attr("transform"); if (transform_wkt) { - agg::trans_affine tr; - if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr)) + mapnik::transform_list_ptr tl = boost::make_shared(); + if (!mapnik::parse_transform(*tl, *transform_wkt, pt.get_tree().transform_expr_grammar)) { std::stringstream ss; ss << "Could not parse transform from '" << transform_wkt << "', expected SVG transform attribute"; @@ -848,9 +849,7 @@ void map_parser::parse_symbolizer_base(symbolizer_base &sym, xml_node const &pt) else std::clog << "### WARNING: " << ss.str() << endl; } - boost::array matrix; - tr.store_to(&matrix[0]); - sym.set_transform(matrix); + sym.set_transform(tl); } optional clip = pt.get_opt_attr("clip"); @@ -919,8 +918,8 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym) optional image_transform_wkt = sym.get_opt_attr("image-transform"); if (image_transform_wkt) { - agg::trans_affine tr; - if (!mapnik::svg::parse_transform((*image_transform_wkt).c_str(),tr)) + mapnik::transform_list_ptr tl = boost::make_shared(); + if (!mapnik::parse_transform(*tl, *image_transform_wkt, sym.get_tree().transform_expr_grammar)) { std::stringstream ss; ss << "Could not parse transform from '" << *image_transform_wkt @@ -934,9 +933,7 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym) MAPNIK_LOG_WARN(load_map) << "map_parser: " << ss; } } - boost::array matrix; - tr.store_to(&matrix[0]); - symbol.set_image_transform(matrix); + symbol.set_image_transform(tl); } } catch (image_reader_exception const & ex) @@ -1014,8 +1011,8 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& sym) optional image_transform_wkt = sym.get_opt_attr("image-transform"); if (image_transform_wkt) { - agg::trans_affine tr; - if (!mapnik::svg::parse_transform((*image_transform_wkt).c_str(),tr)) + mapnik::transform_list_ptr tl = boost::make_shared(); + if (!mapnik::parse_transform(*tl, *image_transform_wkt, sym.get_tree().transform_expr_grammar)) { std::stringstream ss; ss << "Could not parse transform from '" << *image_transform_wkt @@ -1029,9 +1026,7 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& sym) MAPNIK_LOG_WARN(load_map) << "map_parser: " << ss; } } - boost::array matrix; - tr.store_to(&matrix[0]); - symbol.set_image_transform(matrix); + symbol.set_image_transform(tl); } optional c = sym.get_opt_attr("fill"); @@ -1257,8 +1252,8 @@ void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& sym) optional image_transform_wkt = sym.get_opt_attr("image-transform"); if (image_transform_wkt) { - agg::trans_affine tr; - if (!mapnik::svg::parse_transform((*image_transform_wkt).c_str(),tr)) + mapnik::transform_list_ptr tl = boost::make_shared(); + if (!mapnik::parse_transform(*tl, *image_transform_wkt, sym.get_tree().transform_expr_grammar)) { std::stringstream ss; ss << "Could not parse transform from '" << *image_transform_wkt @@ -1272,9 +1267,7 @@ void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& sym) MAPNIK_LOG_WARN(load_map) << "map_parser: " << ss; } } - boost::array matrix; - tr.store_to(&matrix[0]); - shield_symbol.set_image_transform(matrix); + shield_symbol.set_image_transform(tl); } // shield displacement diff --git a/src/parse_transform.cpp b/src/parse_transform.cpp new file mode 100644 index 000000000..3b4ede837 --- /dev/null +++ b/src/parse_transform.cpp @@ -0,0 +1,46 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2012 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include +#include + +#include + +namespace mapnik { + +bool parse_transform(transform_list& transform, + std::string const & str, + transform_expression_grammar const& g) +{ + std::string::const_iterator itr = str.begin(); + std::string::const_iterator end = str.end(); + bool r = qi::phrase_parse(itr, end, g, space_type(), transform); + + #ifdef MAPNIK_LOG + MAPNIK_LOG_DEBUG(load_map) << "map_parser: Parsed transform [ " + << transform_processor_type::to_string(transform) << " ]"; + #endif + + return (r && itr==end); +} + +} diff --git a/src/save_map.cpp b/src/save_map.cpp index 6b30aa03d..15e9cf127 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -244,8 +244,10 @@ public: { set_attr( sym_node, "fill-opacity", sym.get_opacity() ); } - - set_attr( sym_node, "height", to_expression_string(*sym.height()) ); + if (sym.height()) + { + set_attr( sym_node, "height", mapnik::to_expression_string(*sym.height()) ); + } add_metawriter_attributes(sym_node, sym); } @@ -255,8 +257,9 @@ public: ptree & sym_node = rule_.push_back( ptree::value_type("MarkersSymbolizer", ptree()))->second; markers_symbolizer dfl(parse_path("")); //TODO: Parameter? - std::string const& filename = path_processor_type::to_string( *sym.get_filename()); - if ( ! filename.empty() ) { + if (sym.get_filename()) + { + std::string filename = path_processor_type::to_string(*sym.get_filename()); set_attr( sym_node, "file", filename ); } if (sym.get_allow_overlap() != dfl.get_allow_overlap() || explicit_defaults_) @@ -299,9 +302,9 @@ public: { set_attr( sym_node, "placement", sym.get_marker_placement() ); } - std::string tr_str = sym.get_image_transform_string(); - if (tr_str != "matrix(1, 0, 0, 1, 0, 0)" || explicit_defaults_ ) + if (sym.get_image_transform()) { + std::string tr_str = sym.get_image_transform_string(); set_attr( sym_node, "image-transform", tr_str ); } @@ -349,8 +352,9 @@ private: void add_image_attributes(ptree & node, const symbolizer_with_image & sym) { - std::string const& filename = path_processor_type::to_string( *sym.get_filename()); - if ( ! filename.empty() ) { + if (sym.get_filename()) + { + std::string filename = path_processor_type::to_string( *sym.get_filename()); set_attr( node, "file", filename ); } if (sym.get_opacity() != 1.0 || explicit_defaults_ ) @@ -358,6 +362,7 @@ private: set_attr( node, "opacity", sym.get_opacity() ); } } + void add_font_attributes(ptree & node, const text_symbolizer & sym) { text_placements_ptr p = sym.get_placement_options(); @@ -386,7 +391,6 @@ private: } } - void add_stroke_attributes(ptree & node, const stroke & strk) { @@ -434,8 +438,8 @@ private: } set_attr( node, "stroke-dasharray", os.str() ); } - } + void add_metawriter_attributes(ptree & node, symbolizer_base const& sym) { if (!sym.get_metawriter_name().empty() || explicit_defaults_) { @@ -444,13 +448,11 @@ private: if (!sym.get_metawriter_properties_overrides().empty() || explicit_defaults_) { set_attr(node, "meta-output", sym.get_metawriter_properties_overrides().to_string()); } - - std::string tr_str = sym.get_transform_string(); - if (tr_str != "matrix(1, 0, 0, 1, 0, 0)" || explicit_defaults_ ) // FIXME !! + if (sym.get_transform()) { + std::string tr_str = sym.get_transform_string(); set_attr( node, "transform", tr_str ); } - } ptree & rule_; diff --git a/src/symbolizer.cpp b/src/symbolizer.cpp index 3245001aa..d23b85e09 100644 --- a/src/symbolizer.cpp +++ b/src/symbolizer.cpp @@ -23,9 +23,26 @@ //mapnik #include #include +#include namespace mapnik { +void evaluate_transform(agg::trans_affine& tr, Feature const& feature, + transform_list_ptr const& trans_expr) +{ + #ifdef MAPNIK_LOG + MAPNIK_LOG_DEBUG(transform) << "transform: evaluate " + << (trans_expr + ? transform_processor_type::to_string(*trans_expr) + : std::string("null")); + #endif + + if (trans_expr) + { + transform_processor_type::evaluate(tr, feature, *trans_expr); + } +} + // default ctor symbolizer_base::symbolizer_base() : properties_(), @@ -36,12 +53,6 @@ symbolizer_base::symbolizer_base() clip_(true), smooth_value_(0.0) { - affine_transform_[0] = 1.0; - affine_transform_[1] = 0.0; - affine_transform_[2] = 0.0; - affine_transform_[3] = 1.0; - affine_transform_[4] = 0.0; - affine_transform_[5] = 0.0; } // copy ctor @@ -107,6 +118,13 @@ composite_mode_e symbolizer_base::comp_op() const void symbolizer_base::set_transform(transform_type const& affine_transform) { affine_transform_ = affine_transform; + + #ifdef MAPNIK_LOG + MAPNIK_LOG_DEBUG(load_map) << "map_parser: set_transform: " + << (affine_transform_ + ? transform_processor_type::to_string(*affine_transform_) + : std::string("null")); + #endif } transform_type const& symbolizer_base::get_transform() const @@ -116,11 +134,10 @@ transform_type const& symbolizer_base::get_transform() const std::string symbolizer_base::get_transform_string() const { - std::stringstream ss; - ss << "matrix(" << affine_transform_[0] << ", " << affine_transform_[1] << ", " - << affine_transform_[2] << ", " << affine_transform_[3] << ", " - << affine_transform_[4] << ", " << affine_transform_[5] << ")"; - return ss.str(); + if (affine_transform_) + return transform_processor_type::to_string(*affine_transform_); + else + return std::string(); } void symbolizer_base::set_clip(bool clip) @@ -149,12 +166,6 @@ symbolizer_with_image::symbolizer_with_image(path_expression_ptr file) : image_filename_( file ), image_opacity_(1.0f) { - image_transform_[0] = 1.0; - image_transform_[1] = 0.0; - image_transform_[2] = 0.0; - image_transform_[3] = 1.0; - image_transform_[4] = 0.0; - image_transform_[5] = 0.0; } symbolizer_with_image::symbolizer_with_image( symbolizer_with_image const& rhs) @@ -187,6 +198,13 @@ float symbolizer_with_image::get_opacity() const void symbolizer_with_image::set_image_transform(transform_type const& tr) { image_transform_ = tr; + + #ifdef MAPNIK_LOG + MAPNIK_LOG_DEBUG(load_map) << "map_parser: set_image_transform: " + << (image_transform_ + ? transform_processor_type::to_string(*image_transform_) + : std::string("null")); + #endif } transform_type const& symbolizer_with_image::get_image_transform() const @@ -196,11 +214,10 @@ transform_type const& symbolizer_with_image::get_image_transform() const std::string symbolizer_with_image::get_image_transform_string() const { - std::stringstream ss; - ss << "matrix(" << image_transform_[0] << ", " << image_transform_[1] << ", " - << image_transform_[2] << ", " << image_transform_[3] << ", " - << image_transform_[4] << ", " << image_transform_[5] << ")"; - return ss.str(); + if (image_transform_) + return transform_processor_type::to_string(*image_transform_); + else + return std::string(); } } // end of namespace mapnik diff --git a/src/symbolizer_helpers.cpp b/src/symbolizer_helpers.cpp index f2a3f2be0..695325cb9 100644 --- a/src/symbolizer_helpers.cpp +++ b/src/symbolizer_helpers.cpp @@ -350,8 +350,7 @@ template void shield_symbolizer_helper::init_marker() { std::string filename = path_processor_type::evaluate(*sym_.get_filename(), this->feature_); - boost::array const& m = sym_.get_image_transform(); - image_transform_.load_from(&m[0]); + evaluate_transform(image_transform_, feature_, sym_.get_image_transform()); marker_.reset(); if (!filename.empty()) { diff --git a/src/transform_expression.cpp b/src/transform_expression.cpp new file mode 100644 index 000000000..157dee202 --- /dev/null +++ b/src/transform_expression.cpp @@ -0,0 +1,144 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2012 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +// mapnik +#include +#include + +// boost +#include + +// stl +#include + +namespace mapnik { + + +struct transform_node_to_expression_string + : public boost::static_visitor +{ + std::ostringstream& os_; + + transform_node_to_expression_string(std::ostringstream& os) + : os_(os) {} + + void operator() (identity_node const& node) const + { + boost::ignore_unused_variable_warning(node); + } + + void operator() (matrix_node const& node) + { + os_ << "matrix(" + << to_expression_string(node.a_) << ", " + << to_expression_string(node.b_) << ", " + << to_expression_string(node.c_) << ", " + << to_expression_string(node.d_) << ", " + << to_expression_string(node.e_) << ", " + << to_expression_string(node.f_) << ")"; + } + + void operator() (translate_node const& node) + { + if (is_null(node.ty_)) + { + os_ << "translate(" + << to_expression_string(node.tx_) << ")"; + } + else + { + os_ << "translate(" + << to_expression_string(node.tx_) << ", " + << to_expression_string(node.ty_) << ")"; + } + } + + void operator() (scale_node const& node) + { + if (is_null(node.sy_)) + { + os_ << "scale(" + << to_expression_string(node.sx_) << ")"; + } + else + { + os_ << "scale(" + << to_expression_string(node.sx_) << ", " + << to_expression_string(node.sy_) << ")"; + } + } + + void operator() (rotate_node const& node) + { + if (is_null(node.cy_) || is_null(node.cy_)) + { + os_ << "rotate(" + << to_expression_string(node.angle_) << ")"; + } + else + { + os_ << "rotate(" + << to_expression_string(node.angle_) << ", " + << to_expression_string(node.cx_) << ", " + << to_expression_string(node.cy_) << ")"; + } + } + + void operator() (skewX_node const& node) + { + os_ << "skewX(" + << to_expression_string(node.angle_) << ")"; + } + + void operator() (skewY_node const& node) + { + os_ << "skewY(" + << to_expression_string(node.angle_) << ")"; + } +}; + + +std::string to_expression_string(transform_node const& node) +{ + std::ostringstream os; // FIXME set precision? + transform_node_to_expression_string to_string(os); + boost::apply_visitor(to_string, *node); + return os.str(); +} + + +std::string to_expression_string(transform_list const& list) +{ + std::ostringstream os; // FIXME set precision? + std::streamsize first = 1; + transform_node_to_expression_string to_string(os); + + BOOST_FOREACH (transform_node const& node, list) + { + os.write(" ", first ? (first = 0) : 1); + boost::apply_visitor(to_string, *node); + } + return os.str(); +} + + +} // namespace mapnik diff --git a/src/xml_tree.cpp b/src/xml_tree.cpp index 4d4593681..b8aaff9b3 100644 --- a/src/xml_tree.cpp +++ b/src/xml_tree.cpp @@ -172,7 +172,8 @@ xml_tree::xml_tree(std::string const& encoding) tr_(encoding), color_grammar(), expr_grammar(tr_), - path_expr_grammar() + path_expr_grammar(), + transform_expr_grammar(expr_grammar) { node_.set_processed(true); //root node is always processed }