Skip to content
Browse files

new feature: transform expressions are now dynamic

(cherry picked from commit 173c402)

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
  • Loading branch information...
1 parent dad0bda commit bd9609c370dd8aed62f088e6470dd3f4b3390190 @lightmare lightmare committed
View
10 bindings/python/mapnik_svg.hpp
@@ -23,10 +23,14 @@
#define MAPNIK_PYTHON_BINDING_SVG_INCLUDED
// mapnik
+#include <mapnik/parse_transform.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/svg/svg_path_parser.hpp>
#include <mapnik/value_error.hpp>
+// boost
+#include <boost/make_shared.hpp>
+
// 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<transform_list>();
+ trans->push_back(matrix_node(tr));
+ symbolizer.set_image_transform(trans);
}
} // end of namespace mapnik
View
7 include/mapnik/agg_renderer.hpp
@@ -115,6 +115,13 @@ class MAPNIK_DECL agg_renderer : public feature_style_processor<agg_renderer<T>
void painted(bool painted);
+protected:
+ template <typename R>
+ void debug_draw_box(R& buf, box2d<double> const& extent,
+ double x, double y, double angle = 0.0);
+ void debug_draw_box(box2d<double> const& extent,
+ double x, double y, double angle = 0.0);
+
private:
buffer_type & pixmap_;
boost::shared_ptr<buffer_type> internal_buffer_;
View
48 include/mapnik/attribute_collector.hpp
@@ -25,6 +25,7 @@
// mapnik
#include <mapnik/rule.hpp>
+#include <mapnik/parse_transform.hpp>
// boost
#include <boost/utility.hpp>
#include <boost/variant.hpp>
@@ -35,9 +36,10 @@
namespace mapnik {
+template <typename Container>
struct expression_attributes : boost::static_visitor<void>
{
- explicit expression_attributes(std::set<std::string> & 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<void>
template <typename Tag>
void operator() (binary_node<Tag> 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 <typename Tag>
void operator() (unary_node<Tag> 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<std::string>& names_;
+ Container& names_;
};
struct symbolizer_attributes : public boost::static_visitor<>
{
symbolizer_attributes(std::set<std::string>& names)
- : names_(names) {}
+ : names_(names), f_attr(names) {}
template <typename T>
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<std::string>& names_;
+ expression_attributes<std::set<std::string> > 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<std::string>& names_;
+ expression_attributes<std::set<std::string> > f_attr;
public:
attribute_collector(std::set<std::string>& names)
- : names_(names) {}
+ : names_(names), f_attr(names) {}
template <typename RuleType>
void operator() (RuleType const& r)
@@ -207,7 +228,6 @@ class attribute_collector : public boost::noncopyable
}
expression_ptr const& expr = r.get_filter();
- expression_attributes f_attr(names_);
boost::apply_visitor(f_attr,*expr);
}
};
View
15 include/mapnik/marker.hpp
@@ -79,6 +79,21 @@ class marker
{
}
+ box2d<double> 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<double>(0, 0, width, height);
+ }
+ return box2d<double>();
+ }
+
inline double width() const
{
if (is_bitmap())
View
243 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 <mapnik/config.hpp>
+#include <mapnik/debug.hpp>
+#include <mapnik/attribute.hpp>
+#include <mapnik/feature.hpp>
+#include <mapnik/value.hpp>
+#include <mapnik/transform_expression.hpp>
+#include <mapnik/expression_evaluator.hpp>
+
+// boost
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/variant.hpp>
+#include <boost/foreach.hpp>
+
+// agg
+#include <agg_trans_affine.h>
+
+namespace mapnik {
+
+template <typename Container> struct expression_attributes;
+template <typename Iterator> 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<std::string::const_iterator> const& g);
+
+
+template <typename T>
+struct transform_processor
+{
+ typedef T feature_type;
+ typedef agg::trans_affine transform_type;
+
+ template <typename Container>
+ struct attribute_collector : boost::static_visitor<void>
+ {
+ expression_attributes<Container> 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<void>
+ {
+ 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<feature_type, value_type> 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 <typename Container>
+ static void collect_attributes(Container& names,
+ transform_list const& list)
+ {
+ attribute_collector<Container> 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<Feature> transform_processor_type;
+
+} // namespace mapnik
+
+#endif // MAPNIK_PARSE_TRANSFORM_HPP
View
6 include/mapnik/symbolizer.hpp
@@ -28,6 +28,7 @@
#include <mapnik/parse_path.hpp>
#include <mapnik/metawriter.hpp>
#include <mapnik/image_compositing.hpp>
+#include <mapnik/transform_expression.hpp>
// boost
#include <boost/array.hpp>
@@ -36,7 +37,10 @@
namespace mapnik
{
-typedef boost::array<double,6> 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;
View
15 include/mapnik/symbolizer_helpers.hpp
@@ -150,6 +150,21 @@ class shield_symbolizer_helper: public text_symbolizer_helper<FaceManagerT, Dete
init_marker();
}
+ box2d<double> 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;
View
181 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 <mapnik/expression_node.hpp>
+
+// boost
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/variant.hpp>
+
+// agg
+#include <agg_trans_affine.h>
+
+// stl
+#include <vector>
+
+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<expr_node> 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<expr_node> 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<expr_node> const& cx,
+ boost::optional<expr_node> 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>(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 <typename T>
+ transform_node(T const& val)
+ : base_(val) {}
+
+ template <typename T>
+ 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_node> transform_list;
+typedef boost::shared_ptr<transform_list> 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
View
121 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 <mapnik/expression_grammar.hpp>
+#include <mapnik/transform_expression.hpp>
+
+// spirit
+#include <boost/spirit/home/phoenix/object/construct.hpp>
+#include <boost/spirit/home/qi.hpp>
+
+namespace mapnik {
+
+ namespace qi = boost::spirit::qi;
+
+ template <typename Iterator>
+ struct transform_expression_grammar
+ : qi::grammar<Iterator, transform_list(), space_type>
+ {
+ explicit transform_expression_grammar(expression_grammar<Iterator> 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<matrix_node>(_1,_2,_3,_4,_5,_6) ];
+
+ translate = no_case[lit("translate")]
+ >> (lit('(')
+ >> expr >> -lit(',')
+ >> -expr >> lit(')'))
+ [ _val = construct<translate_node>(_1,_2) ];
+
+ scale = no_case[lit("scale")]
+ >> (lit('(')
+ >> expr >> -lit(',')
+ >> -expr >> lit(')'))
+ [ _val = construct<scale_node>(_1,_2) ];
+
+ rotate = no_case[lit("rotate")]
+ >> lit('(')
+ >> expr[_a = _1] >> -lit(',')
+ >> -(expr [_b = _1] >> -lit(',') >> expr[_c = _1])
+ >> lit(')')
+ [ _val = construct<rotate_node>(_a,_b,_c) ];
+
+ skewX = no_case[lit("skewX")]
+ >> lit('(')
+ >> expr [ _val = construct<skewX_node>(_1) ]
+ >> lit(')');
+
+ skewY = no_case[lit("skewY")]
+ >> lit('(')
+ >> expr [ _val = construct<skewY_node>(_1) ]
+ >> lit(')');
+
+ expr = g.expr.alias();
+ }
+
+ typedef qi::locals<expr_node, boost::optional<expr_node>,
+ boost::optional<expr_node>
+ > rotate_locals;
+ typedef qi::rule<Iterator, transform_node(), space_type> node_rule;
+ typedef qi::rule<Iterator, transform_list(), space_type> list_rule;
+
+ // rules
+ typename expression_grammar<Iterator>::rule_type expr;
+ qi::rule<Iterator, transform_list(), space_type> start;
+ qi::rule<Iterator, transform_node(), space_type> transform_;
+ qi::rule<Iterator, transform_node(), space_type> matrix;
+ qi::rule<Iterator, transform_node(), space_type> translate;
+ qi::rule<Iterator, transform_node(), space_type> scale;
+ qi::rule<Iterator, transform_node(), space_type, rotate_locals> rotate;
+ qi::rule<Iterator, transform_node(), space_type> skewX;
+ qi::rule<Iterator, transform_node(), space_type> skewY;
+ };
+
+} // namespace mapnik
+
+#endif // MAPNIK_TRANSFORM_EXPRESSION_GRAMMAR_HPP
View
38 include/mapnik/vertex_converters.hpp
@@ -120,9 +120,8 @@ struct converter_traits<T, mapnik::dash_tag>
template <typename Args>
static void setup(geometry_type & geom, Args const& args)
{
-
typename boost::mpl::at<Args,boost::mpl::int_<2> >::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<T, mapnik::stroke_tag>
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<T,mapnik::transform_tag>
template <typename Args>
static void setup(geometry_type & geom, Args const& args)
{
- typename boost::mpl::at<Args,boost::mpl::int_<3> >::type tr = boost::fusion::at_c<3>(args);
- typename boost::mpl::at<Args,boost::mpl::int_<4> >::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 <typename T>
struct converter_traits<T,mapnik::affine_transform_tag>
{
typedef T geometry_type;
+ typedef agg::conv_transform<geometry_type, agg::trans_affine const>
+ conv_base_type;
- struct conv_type : public agg::conv_transform<geometry_type>
+ struct conv_type : public conv_base_type
{
- agg::trans_affine trans_;
-
conv_type(geometry_type& geom)
- : agg::conv_transform<geometry_type>(geom, trans_) {}
+ : conv_base_type(geom, agg::trans_affine::identity) {}
};
template <typename Args>
static void setup(geometry_type & geom, Args & args)
{
- typename boost::mpl::at<Args,boost::mpl::int_<2> >::type sym = boost::fusion::at_c<2>(args);
- boost::array<double,6> 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 <typename B, typename R, typename S, typename P, typename T, typename C >
+template <typename B, typename R, typename S, typename T, typename P, typename A, typename C >
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 <typename Geometry>
void apply(Geometry & geom)
View
2 include/mapnik/xml_tree.hpp
@@ -26,6 +26,7 @@
#include <mapnik/xml_node.hpp>
#include <mapnik/expression_grammar.hpp>
#include <mapnik/path_expression_grammar.hpp>
+#include <mapnik/transform_expression_grammar.hpp>
// boost
#include <boost/format.hpp>
@@ -57,6 +58,7 @@ class xml_tree
mapnik::css_color_grammar<std::string::const_iterator> color_grammar;
mapnik::expression_grammar<std::string::const_iterator> expr_grammar;
path_expression_grammar<std::string::const_iterator> path_expr_grammar;
+ transform_expression_grammar<std::string::const_iterator> transform_expr_grammar;
};
} //ns mapnik
View
60 src/agg/agg_renderer.cpp
@@ -277,7 +277,7 @@ void agg_renderer<T>::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<svg_path_storage> stl_storage((*marker.get_vector_data())->source());
svg_path_adapter svg_path(stl_storage);
@@ -291,10 +291,12 @@ void agg_renderer<T>::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<T>::painted(bool painted)
pixmap_.painted(painted);
}
+template <typename T>
+void agg_renderer<T>::debug_draw_box(box2d<double> 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 <typename T> template <typename R>
+void agg_renderer<T>::debug_draw_box(R& buf, box2d<double> const& box,
+ double x, double y, double angle)
+{
+ typedef agg::pixfmt_rgba32 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+ typedef agg::renderer_scanline_aa_solid<renderer_base> 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<agg::path_storage> conv_transform;
+ typedef agg::conv_stroke<conv_transform> 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<image_32>;
+template void agg_renderer<image_32>::debug_draw_box<agg::rendering_buffer>(
+ agg::rendering_buffer& buf,
+ box2d<double> const& box,
+ double x, double y, double angle);
}
View
8 src/agg/process_line_pattern_symbolizer.cpp
@@ -94,9 +94,13 @@ void agg_renderer<T>::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<clip_line_tag,transform_tag,smooth_tag> conv_types;
- vertex_converter<box2d<double>,rasterizer_type,line_pattern_symbolizer, proj_transform, CoordTransform, conv_types>
- converter(ext,ras,sym,t_,prj_trans,scale_factor_);
+ vertex_converter<box2d<double>, 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<clip_line_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform
View
13 src/agg/process_line_symbolizer.cpp
@@ -76,6 +76,9 @@ void agg_renderer<T>::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_base> renderer_type;
@@ -89,8 +92,9 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
set_join_caps_aa(stroke_,ras);
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
- vertex_converter<box2d<double>,rasterizer_type,line_symbolizer, proj_transform, CoordTransform,conv_types>
- converter(query_extent_,ras,sym,t_,prj_trans,scaled);
+ vertex_converter<box2d<double>, 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<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform
@@ -111,8 +115,9 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
else
{
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
- vertex_converter<box2d<double>,rasterizer,line_symbolizer, proj_transform, CoordTransform,conv_types>
- converter(query_extent_,*ras_ptr,sym,t_,prj_trans,scale_factor_);
+ vertex_converter<box2d<double>, 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<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform
View
42 src/agg/process_markers_symbolizer.cpp
@@ -73,8 +73,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
renderer_base renb(pixf);
renderer_type ren(renb);
agg::trans_affine tr;
- boost::array<double,6> 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<T>::process(markers_symbolizer const& sym,
}
boost::optional<path_ptr> marker = (*mark)->get_vector_data();
box2d<double> 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();
-
- 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<double> extent(x1,y1,x2,y2);
+ coord2d const center = bbox.center();
+
+ agg::trans_affine_translation const recenter(-center.x, -center.y);
+ agg::trans_affine const recenter_tr = recenter * tr;
+ box2d<double> extent = bbox * recenter_tr;
+
using namespace mapnik::svg;
vertex_stl_adapter<svg_path_storage> stl_storage((*marker)->source());
svg_path_adapter svg_path(stl_storage);
@@ -132,7 +126,11 @@ void agg_renderer<T>::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<T>::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);
View
33 src/agg/process_point_symbolizer.cpp
@@ -63,26 +63,15 @@ void agg_renderer<T>::process(point_symbolizer const& sym,
if (marker)
{
- double w = (*marker)->width();
- double h = (*marker)->height();
+ box2d<double> const& bbox = (*marker)->bounding_box();
+ coord2d const center = bbox.center();
+
agg::trans_affine tr;
- boost::array<double,6> 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<double> 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<double> label_ext = bbox * recenter_tr;
for (unsigned i=0; i<feature->num_geometries(); ++i)
{
@@ -103,7 +92,11 @@ void agg_renderer<T>::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);
View
8 src/agg/process_polygon_pattern_symbolizer.cpp
@@ -136,10 +136,14 @@ void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
agg::span_allocator<agg::rgba8> sa;
renderer_type rp(renb,sa, sg);
+ agg::trans_affine tr;
+ evaluate_transform(tr, *feature, sym.get_transform());
+
box2d<double> inflated_extent = query_extent_ * 1.0;
typedef boost::mpl::vector<clip_poly_tag,transform_tag,smooth_tag> conv_types;
- vertex_converter<box2d<double>,rasterizer,polygon_pattern_symbolizer, proj_transform, CoordTransform,conv_types>
- converter(inflated_extent,*ras_ptr,sym,t_,prj_trans, scale_factor_);
+ vertex_converter<box2d<double>, 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<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform
View
8 src/agg/process_polygon_symbolizer.cpp
@@ -50,9 +50,13 @@ void agg_renderer<T>::process(polygon_symbolizer const& sym,
box2d<double> inflated_extent = query_extent_ * 1.0;
+ agg::trans_affine tr;
+ evaluate_transform(tr, *feature, sym.get_transform());
+
typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag> conv_types;
- vertex_converter<box2d<double>,rasterizer,polygon_symbolizer, proj_transform, CoordTransform,conv_types>
- converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,scale_factor_);
+ vertex_converter<box2d<double>, 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<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform
View
10 src/agg/process_shield_symbolizer.cpp
@@ -52,7 +52,15 @@ void agg_renderer<T>::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(),
View
2 src/build.py
@@ -110,6 +110,7 @@ def ldconfig(*args,**kwargs):
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 @@ def ldconfig(*args,**kwargs):
load_map.cpp
memory.cpp
parse_path.cpp
+ parse_transform.cpp
palette.cpp
placement_finder.cpp
plugin.cpp
View
34 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<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag> conv_types;
- vertex_converter<box2d<double>,cairo_context,polygon_symbolizer, proj_transform, CoordTransform, conv_types>
- converter(query_extent_,context,sym,t_,prj_trans,1.0);
+ vertex_converter<box2d<double>, 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<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //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<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag> conv_types;
- vertex_converter<box2d<double>,cairo_context,line_symbolizer, proj_transform, CoordTransform,conv_types>
- converter(query_extent_,context ,sym,t_,prj_trans,1.0);
+ vertex_converter<box2d<double>, 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<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform
@@ -1131,6 +1138,9 @@ void cairo_renderer_base::start_map_processing(Map const& map)
marker.reset(boost::make_shared<mapnik::marker>());
}
+ 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<double,6> 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<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag> conv_types;
- vertex_converter<box2d<double>,cairo_context,polygon_pattern_symbolizer, proj_transform, CoordTransform, conv_types>
- converter(query_extent_,context,sym,t_,prj_trans,1.0);
+ vertex_converter<box2d<double>, 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<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform
@@ -1371,8 +1381,8 @@ void cairo_renderer_base::start_map_processing(Map const& map)
typedef coord_transform<CoordTransform,clipped_geometry_type> path_type;
agg::trans_affine tr;
- boost::array<double,6> 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);
View
3 src/grid/process_markers_symbolizer.cpp
@@ -69,8 +69,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
ras_ptr->reset();
agg::trans_affine tr;
- boost::array<double,6> 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);
View
7 src/grid/process_point_symbolizer.cpp
@@ -55,6 +55,9 @@ void grid_renderer<T>::process(point_symbolizer const& sym,
if (marker)
{
+ agg::trans_affine tr;
+ evaluate_transform(tr, *feature, sym.get_image_transform());
+
for (unsigned i=0; i<feature->num_geometries(); ++i)
{
geometry_type const& geom = feature->get_geometry(i);
@@ -78,10 +81,6 @@ void grid_renderer<T>::process(point_symbolizer const& sym,
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
{
- agg::trans_affine tr;
- boost::array<double,6> const& m = sym.get_image_transform();
- tr.load_from(&m[0]);
-
render_marker(feature, pixmap_.get_resolution(),
pixel_position(px, py),
**marker, tr,
View
33 src/load_map.cpp
@@ -42,6 +42,7 @@
#include <mapnik/expression.hpp>
#include <mapnik/parse_path.hpp>
+#include <mapnik/parse_transform.hpp>
#include <mapnik/raster_colorizer.hpp>
#include <mapnik/svg/svg_path_parser.hpp>
@@ -838,8 +839,8 @@ void map_parser::parse_symbolizer_base(symbolizer_base &sym, xml_node const &pt)
optional<std::string> transform_wkt = pt.get_opt_attr<std::string>("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<mapnik::transform_list>();
+ 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<double,6> matrix;
- tr.store_to(&matrix[0]);
- sym.set_transform(matrix);
+ sym.set_transform(tl);
}
optional<boolean> clip = pt.get_opt_attr<boolean>("clip");
@@ -919,8 +918,8 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym)
optional<std::string> image_transform_wkt = sym.get_opt_attr<std::string>("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<mapnik::transform_list>();
+ 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<double,6> 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<std::string> image_transform_wkt = sym.get_opt_attr<std::string>("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<mapnik::transform_list>();
+ 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<double,6> matrix;
- tr.store_to(&matrix[0]);
- symbol.set_image_transform(matrix);
+ symbol.set_image_transform(tl);
}
optional<color> c = sym.get_opt_attr<color>("fill");
@@ -1257,8 +1252,8 @@ void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& sym)
optional<std::string> image_transform_wkt = sym.get_opt_attr<std::string>("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<mapnik::transform_list>();
+ 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<double,6> matrix;
- tr.store_to(&matrix[0]);
- shield_symbol.set_image_transform(matrix);
+ shield_symbol.set_image_transform(tl);
}
// shield displacement
View
46 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 <mapnik/parse_transform.hpp>
+#include <mapnik/transform_expression_grammar.hpp>
+
+#include <boost/make_shared.hpp>
+
+namespace mapnik {
+
+bool parse_transform(transform_list& transform,
+ std::string const & str,
+ transform_expression_grammar<std::string::const_iterator> 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);
+}
+
+}
View
30 src/save_map.cpp
@@ -244,8 +244,10 @@ class serialize_symbolizer : public boost::static_visitor<>
{
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 @@ class serialize_symbolizer : public boost::static_visitor<>
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 @@ class serialize_symbolizer : public boost::static_visitor<>
{
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 @@ class serialize_symbolizer : public boost::static_visitor<>
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 @@ class serialize_symbolizer : public boost::static_visitor<>
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 @@ class serialize_symbolizer : public boost::static_visitor<>
}
}
-
void add_stroke_attributes(ptree & node, const stroke & strk)
{
@@ -434,8 +438,8 @@ class serialize_symbolizer : public boost::static_visitor<>
}
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 @@ class serialize_symbolizer : public boost::static_visitor<>
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_;
View
61 src/symbolizer.cpp
@@ -23,9 +23,26 @@
//mapnik
#include <mapnik/symbolizer.hpp>
#include <mapnik/map.hpp>
+#include <mapnik/parse_transform.hpp>
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
View
3 src/symbolizer_helpers.cpp
@@ -350,8 +350,7 @@ template <typename FaceManagerT, typename DetectorT>
void shield_symbolizer_helper<FaceManagerT, DetectorT>::init_marker()
{
std::string filename = path_processor_type::evaluate(*sym_.get_filename(), this->feature_);
- boost::array<double,6> 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())
{
View
144 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 <mapnik/expression_string.hpp>
+#include <mapnik/transform_expression.hpp>
+
+// boost
+#include <boost/foreach.hpp>
+
+// stl
+#include <sstream>
+
+namespace mapnik {
+
+
+struct transform_node_to_expression_string
+ : public boost::static_visitor<void>
+{
+ 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
View
3 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
}

0 comments on commit bd9609c

Please sign in to comment.
Something went wrong with that request. Please try again.