Skip to content
Browse files

backport markers_symbolizer fixes from master to 2.0.x - closes #1163

  • Loading branch information...
1 parent c9a610d commit b93c760b33f69d2e015e2e1d86760f582a580b58 @springmeyer springmeyer committed Apr 4, 2012
View
5 CHANGELOG
@@ -14,6 +14,11 @@ For a complete change history, see the SVN log.
Mapnik 2.0.1
------------
+- Cairo: Add full rendering support for markers to match AGG renderer functionality (#1071)
+
+- Fix Markers rendering so that ellipse height/width units are pixels (previously were unintentially radii) (#1134)
+
+- Added 'ignore-placement` attribute to markers-symbolizer (#1135)
- Switched back to "libmapnik" and "import mapnik" rather than "mapnik2" (#941)
View
17 bindings/python/mapnik_markers_symbolizer.cpp
@@ -60,7 +60,8 @@ struct markers_symbolizer_pickle_suite : boost::python::pickle_suite
static boost::python::tuple
getstate(markers_symbolizer const& p)
{
- return boost::python::make_tuple(p.get_allow_overlap());//,p.get_opacity());
+ return boost::python::make_tuple(p.get_allow_overlap(),
+ p.get_ignore_placement());//,p.get_opacity());
}
static void
@@ -77,8 +78,7 @@ struct markers_symbolizer_pickle_suite : boost::python::pickle_suite
}
p.set_allow_overlap(extract<bool>(state[0]));
- //p.set_opacity(extract<float>(state[1]));
-
+ p.set_ignore_placement(extract<bool>(state[1]));
}
};
@@ -108,8 +108,19 @@ void export_markers_symbolizer()
&markers_symbolizer::get_opacity,
&markers_symbolizer::set_opacity,
"Set/get the text opacity")
+ .add_property("ignore_placement",
+ &markers_symbolizer::get_ignore_placement,
+ &markers_symbolizer::set_ignore_placement)
.add_property("transform",
&mapnik::get_svg_transform<markers_symbolizer>,
&mapnik::set_svg_transform<markers_symbolizer>)
+ .add_property("width",
+ &markers_symbolizer::get_width,
+ &markers_symbolizer::set_width,
+ "Set/get the marker width")
+ .add_property("height",
+ &markers_symbolizer::get_height,
+ &markers_symbolizer::set_height,
+ "Set/get the marker height")
;
}
View
2 include/mapnik/cairo_renderer.hpp
@@ -131,7 +131,7 @@ class MAPNIK_DECL cairo_renderer_base : private boost::noncopyable
}
protected:
- void render_marker(const int x, const int y, marker &marker, const agg::trans_affine & mtx, double opacity=1.0);
+ void render_marker(const int x, const int y, marker &marker, const agg::trans_affine & mtx, double opacity=1.0, bool recenter=true);
Map const& m_;
Cairo::RefPtr<Cairo::Context> context_;
View
3 include/mapnik/markers_symbolizer.hpp
@@ -58,6 +58,8 @@ struct MAPNIK_DECL markers_symbolizer :
explicit markers_symbolizer();
markers_symbolizer(path_expression_ptr filename);
markers_symbolizer(markers_symbolizer const& rhs);
+ void set_ignore_placement(bool ignore_placement);
+ bool get_ignore_placement() const;
void set_allow_overlap(bool overlap);
bool get_allow_overlap() const;
void set_spacing(double spacing);
@@ -78,6 +80,7 @@ struct MAPNIK_DECL markers_symbolizer :
marker_type_e get_marker_type() const;
private:
+ bool ignore_placement_;
bool allow_overlap_;
color fill_;
double spacing_;
View
11 src/agg/process_markers_symbolizer.cpp
@@ -142,7 +142,9 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
unsigned s_a=col.alpha();
double w = sym.get_width();
double h = sym.get_height();
-
+ double rx = w/2.0;
+ double ry = h/2.0;
+
arrow arrow_;
box2d<double> extent;
@@ -196,7 +198,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
{
- agg::ellipse c(x, y, w, h);
+ agg::ellipse c(x, y, rx, ry);
marker.concat_path(c);
ras_ptr->add_path(marker);
ren.color(agg::rgba8(r, g, b, int(a*sym.get_opacity())));
@@ -215,7 +217,8 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
ren.color(agg::rgba8(s_r, s_g, s_b, int(s_a*stroke_.get_opacity())));
agg::render_scanlines(*ras_ptr, sl_line, ren);
}
- detector_.insert(label_ext);
+ if (!sym.get_ignore_placement())
+ detector_.insert(label_ext);
if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second);
}
}
@@ -239,7 +242,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
if (marker_type == ELLIPSE)
{
// todo proper bbox - this is buggy
- agg::ellipse c(x_t, y_t, w, h);
+ agg::ellipse c(x_t, y_t, rx, ry);
marker.concat_path(c);
agg::trans_affine matrix;
matrix *= agg::trans_affine_translation(-x_t,-y_t);
View
261 src/cairo_renderer.cpp
@@ -40,6 +40,9 @@
#include <mapnik/warp.hpp>
#include <mapnik/config.hpp>
+#include "agg_path_storage.h" // for markers_symbolizer
+#include "agg_ellipse.h" // for markers_symbolizer
+
// cairo
#include <cairomm/context.h>
#include <cairomm/surface.h>
@@ -908,7 +911,7 @@ void cairo_renderer_base::process(line_symbolizer const& sym,
}
}
-void cairo_renderer_base::render_marker(const int x, const int y, marker &marker, const agg::trans_affine & tr, double opacity)
+void cairo_renderer_base::render_marker(const int x, const int y, marker &marker, const agg::trans_affine & tr, double opacity, bool recenter)
{
cairo_context context(context_);
@@ -917,15 +920,21 @@ void cairo_renderer_base::render_marker(const int x, const int y, marker &marker
box2d<double> bbox;
bbox = (*marker.get_vector_data())->bounding_box();
- coord<double,2> c = bbox.center();
- // center the svg marker on '0,0'
- agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y);
- // apply symbol transformation to get to map space
- mtx *= tr;
- // render the marker at the center of the marker box
- mtx.translate(x+0.5 * marker.width(), y+0.5 * marker.height());
+ agg::trans_affine mtx = tr;
+
+ if (recenter)
+ {
+ coord<double,2> c = bbox.center();
+ // center the svg marker on '0,0'
+ mtx = agg::trans_affine_translation(-c.x,-c.y);
+ // apply symbol transformation to get to map space
+ mtx *= tr;
+ // render the marker at the center of the marker box
+ mtx.translate(x+0.5 * marker.width(), y+0.5 * marker.height());
+ }
typedef coord_transform2<CoordTransform,geometry_type> path_type;
+ agg::trans_affine transform;
mapnik::path_ptr vmarker = *marker.get_vector_data();
agg::pod_bvector<path_attributes> const & attributes_ = vmarker->attributes();
@@ -937,10 +946,10 @@ void cairo_renderer_base::render_marker(const int x, const int y, marker &marker
context.save();
- agg::trans_affine transform = attr.transform;
+ transform = attr.transform;
transform *= mtx;
- if (transform.is_valid() && !transform.is_identity())
+ if (/*transform.is_valid() &&*/ !transform.is_identity())
{
double m[6];
transform.store_to(m);
@@ -1423,34 +1432,236 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
- typedef coord_transform2<CoordTransform,geometry_type> path_type;
- arrow arrow_;
cairo_context context(context_);
- color const& fill_ = sym.get_fill();
- context.set_color(fill_.red(), fill_.green(), fill_.blue(), fill_.alpha());
+ double scale_factor_(1);
- for (unsigned i = 0; i < feature.num_geometries(); ++i)
+ typedef coord_transform2<CoordTransform,mapnik::geometry_type> path_type;
+
+ agg::trans_affine tr;
+ boost::array<double,6> const& m = sym.get_transform();
+ tr.load_from(&m[0]);
+ // TODO - use this?
+ //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();
+ marker_type_e marker_type = sym.get_marker_type();
+ metawriter_with_properties writer = sym.get_metawriter();
+
+ if (!filename.empty())
{
- geometry_type const& geom = feature.get_geometry(i);
+ boost::optional<marker_ptr> mark = mapnik::marker_cache::instance()->find(filename, true);
+ if (mark && *mark)
+ {
+ if (!(*mark)->is_vector()) {
+ std::clog << "### Warning only svg markers are supported in the markers_symbolizer\n";
+ return;
+ }
+ 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);
+ using namespace mapnik::svg;
+
+ for (unsigned i=0; i<feature.num_geometries(); ++i)
+ {
+ geometry_type const& geom = feature.get_geometry(i);
+ // TODO - merge this code with point_symbolizer rendering
+ if (placement_method == MARKER_POINT_PLACEMENT || geom.num_points() <= 1)
+ {
+ double x;
+ double y;
+ double z=0;
+ geom.label_interior_position(&x, &y);
+ prj_trans.backward(x,y,z);
+ t_.forward(&x,&y);
+ extent.re_center(x,y);
+
+ if (sym.get_allow_overlap() ||
+ detector_.has_placement(extent))
+ {
+ render_marker(x - 0.5 * w, y - 0.5 * h,**mark, tr, sym.get_opacity());
- if (geom.num_points() > 1)
+ // TODO - impl this for markers?
+ //if (!sym.get_ignore_placement())
+ // detector_.insert(label_ext);
+ metawriter_with_properties writer = sym.get_metawriter();
+ if (writer.first) writer.first->add_box(extent, feature, t_, writer.second);
+ }
+ }
+ else
+ {
+ path_type path(t_,geom,prj_trans);
+ markers_placement<path_type, label_collision_detector4> placement(path, extent, detector_,
+ sym.get_spacing() * scale_factor_,
+ sym.get_max_error(),
+ sym.get_allow_overlap());
+ double x, y, angle;
+
+ while (placement.get_point(&x, &y, &angle))
+ {
+ agg::trans_affine matrix = recenter * tr * agg::trans_affine_rotation(angle) * agg::trans_affine_translation(x, y);
+ render_marker(x - 0.5 * w, y - 0.5 * h, **mark, matrix, sym.get_opacity(),false);
+
+ if (writer.first)
+ //writer.first->add_box(label_ext, feature, t_, writer.second);
+ std::clog << "### Warning metawriter not yet supported for LINE placement\n";
+ }
+ }
+ context.fill();
+ }
+ }
+ }
+ else
+ {
+ color const& fill_ = sym.get_fill();
+ stroke const& stroke_ = sym.get_stroke();
+ color const& col = stroke_.get_color();
+ double strk_width = stroke_.get_width();
+ double w = sym.get_width();
+ double h = sym.get_height();
+ double rx = w/2.0;
+ double ry = h/2.0;
+
+ arrow arrow_;
+ box2d<double> extent;
+
+ double dx = w + (2*strk_width);
+ double dy = h + (2*strk_width);
+
+ if (marker_type == ARROW)
{
- path_type path(t_, geom, prj_trans);
+ extent = arrow_.extent();
+ double x1 = extent.minx();
+ double y1 = extent.miny();
+ double x2 = extent.maxx();
+ double y2 = extent.maxy();
+ tr.transform(&x1,&y1);
+ tr.transform(&x2,&y2);
+ extent.init(x1,y1,x2,y2);
+ }
+ else
+ {
+ double x1 = -1 *(dx);
+ double y1 = -1 *(dy);
+ double x2 = dx;
+ double y2 = dy;
+ tr.transform(&x1,&y1);
+ tr.transform(&x2,&y2);
+ extent.init(x1,y1,x2,y2);
+ }
- markers_placement<path_type, label_collision_detector4> placement(path, arrow_.extent(), detector_, sym.get_spacing(), sym.get_max_error(), sym.get_allow_overlap());
+ double x;
+ double y;
+ double z=0;
- double x, y, angle;
- while (placement.get_point(&x, &y, &angle)) {
- Cairo::Matrix matrix = Cairo::rotation_matrix(angle) * Cairo::translation_matrix(x,y) ;
- context.set_matrix(matrix);
- context.add_path(arrow_);
+ agg::path_storage marker;
+
+ for (unsigned i=0; i<feature.num_geometries(); ++i)
+ {
+ geometry_type const& geom = feature.get_geometry(i);
+ if (placement_method == MARKER_POINT_PLACEMENT || geom.num_points() <= 1)
+ {
+ geom.label_position(&x,&y);
+ prj_trans.backward(x,y,z);
+ t_.forward(&x,&y);
+ int px = int(floor(x - 0.5 * dx));
+ int py = int(floor(y - 0.5 * dy));
+ box2d<double> label_ext (px, py, px + dx +1, py + dy +1);
+ if (sym.get_allow_overlap() ||
+ detector_.has_placement(label_ext))
+ {
+ agg::ellipse c(x, y, rx, ry);
+ marker.concat_path(c);
+ context.set_color(fill_,sym.get_opacity());
+ context.add_agg_path(marker);
+ context.fill();
+
+ if (strk_width)
+ {
+ //context.restore();
+ context.set_color(col,stroke_.get_opacity());
+ context.set_line_width(stroke_.get_width());
+ if (stroke_.has_dash())
+ {
+ context.set_dash(stroke_.get_dash_array());
+ }
+ context.add_agg_path(marker);
+ context.stroke();
+ }
+ if (!sym.get_ignore_placement())
+ detector_.insert(label_ext);
+ if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second);
+ }
+ }
+ else
+ {
+ if (marker_type == ARROW)
+ marker.concat_path(arrow_);
+
+ path_type path(t_,geom,prj_trans);
+ markers_placement<path_type, label_collision_detector4> placement(path, extent, detector_,
+ sym.get_spacing() * scale_factor_,
+ sym.get_max_error(),
+ sym.get_allow_overlap());
+ double x_t, y_t, angle;
+
+ while (placement.get_point(&x_t, &y_t, &angle))
+ {
+ agg::trans_affine matrix;
+
+ if (marker_type == ELLIPSE)
+ {
+ agg::ellipse c(x_t, y_t, rx, ry);
+ marker.concat_path(c);
+ agg::trans_affine matrix;
+ matrix *= agg::trans_affine_translation(-x_t,-y_t);
+ matrix *= agg::trans_affine_rotation(angle);
+ matrix *= agg::trans_affine_translation(x_t,y_t);
+ marker.transform(matrix);
+ }
+ else
+ {
+ matrix = tr * agg::trans_affine_rotation(angle) * agg::trans_affine_translation(x_t, y_t);
+ }
+
+ // TODO
+ if (writer.first)
+ //writer.first->add_box(label_ext, feature, t_, writer.second);
+ std::clog << "### Warning metawriter not yet supported for LINE placement\n";
+
+ agg::conv_transform<agg::path_storage, agg::trans_affine> trans(marker, matrix);
+ context.set_color(fill_,sym.get_opacity());
+ context.add_agg_path(trans);
+ context.fill();
+
+ if (strk_width)
+ {
+ context.set_color(col,stroke_.get_opacity());
+ context.set_line_width(stroke_.get_width());
+ if (stroke_.has_dash())
+ {
+ context.set_dash(stroke_.get_dash_array());
+ }
+ context.add_agg_path(trans);
+ context.stroke();
+ }
+ }
}
}
- context.fill();
}
}
+
void cairo_renderer_base::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
View
9 src/grid/process_markers_symbolizer.cpp
@@ -145,7 +145,10 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
w = sym.get_width()/res;
h = sym.get_height()/res;
}
-
+
+ double rx = w/2.0;
+ double ry = h/2.0;
+
arrow arrow_;
box2d<double> extent;
@@ -193,7 +196,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
{
- agg::ellipse c(x, y, w, h);
+ agg::ellipse c(x, y, rx, ry);
agg::path_storage marker;
marker.concat_path(c);
ras_ptr->add_path(marker);
@@ -230,7 +233,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
if (marker_type == ELLIPSE)
{
// todo proper bbox - this is buggy
- agg::ellipse c(x_t, y_t, w, h);
+ agg::ellipse c(x_t, y_t, rx, ry);
marker.concat_path(c);
agg::trans_affine matrix;
matrix *= agg::trans_affine_translation(-x_t,-y_t);
View
2 src/load_map.cpp
@@ -1047,7 +1047,9 @@ void map_parser::parse_markers_symbolizer( rule & rule, ptree const & sym )
optional<double> max_error = get_opt_attr<double>(sym, "max-error");
if (max_error) symbol.set_max_error(*max_error);
optional<boolean> allow_overlap = get_opt_attr<boolean>(sym, "allow-overlap");
+ optional<boolean> ignore_placement = get_opt_attr<boolean>(sym, "ignore-placement");
if (allow_overlap) symbol.set_allow_overlap(*allow_overlap);
+ if (ignore_placement) symbol.set_ignore_placement(*ignore_placement);
optional<double> w = get_opt_attr<double>(sym, "width");
optional<double> h = get_opt_attr<double>(sym, "height");
View
27 src/markers_symbolizer.cpp
@@ -47,11 +47,12 @@ markers_symbolizer::markers_symbolizer()
: symbolizer_with_image(path_expression_ptr(new path_expression)),
symbolizer_base(),
allow_overlap_(false),
+ ignore_placement_(false),
fill_(color(0,0,255)),
spacing_(100.0),
max_error_(0.2),
- width_(5.0),
- height_(5.0),
+ width_(10.0),
+ height_(10.0),
stroke_(),
marker_p_(MARKER_LINE_PLACEMENT),
marker_type_(ARROW) {}
@@ -60,11 +61,12 @@ markers_symbolizer::markers_symbolizer(path_expression_ptr filename)
: symbolizer_with_image(filename),
symbolizer_base(),
allow_overlap_(false),
+ ignore_placement_(false),
fill_(color(0,0,255)),
spacing_(100.0),
max_error_(0.2),
- width_(5.0),
- height_(5.0),
+ width_(10.0),
+ height_(10.0),
stroke_(),
marker_p_(MARKER_LINE_PLACEMENT),
marker_type_(ARROW) {}
@@ -73,15 +75,26 @@ markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs)
: symbolizer_with_image(rhs),
symbolizer_base(rhs),
allow_overlap_(rhs.allow_overlap_),
- fill_(rhs.fill_),
- spacing_(rhs.spacing_),
+ ignore_placement_(rhs.ignore_placement_),
+ fill_(rhs.fill_),
+ spacing_(rhs.spacing_),
max_error_(rhs.max_error_),
width_(rhs.width_),
height_(rhs.height_),
stroke_(rhs.stroke_),
marker_p_(rhs.marker_p_),
marker_type_(rhs.marker_type_) {}
-
+
+void markers_symbolizer::set_ignore_placement(bool ignore_placement)
+{
+ ignore_placement_ = ignore_placement;
+}
+
+bool markers_symbolizer::get_ignore_placement() const
+{
+ return ignore_placement_;
+}
+
void markers_symbolizer::set_allow_overlap(bool overlap)
{
allow_overlap_ = overlap;
View
4 src/save_map.cpp
@@ -261,6 +261,10 @@ class serialize_symbolizer : public boost::static_visitor<>
{
set_attr( sym_node, "allow-overlap", sym.get_allow_overlap() );
}
+ if (sym.get_ignore_placement() != dfl.get_ignore_placement() || explicit_defaults_)
+ {
+ set_attr( sym_node, "ignore-placement", sym.get_ignore_placement() );
+ }
if (sym.get_spacing() != dfl.get_spacing() || explicit_defaults_)
{
set_attr( sym_node, "spacing", sym.get_spacing() );
View
2 tests/python_tests/render_grid_test.py
@@ -44,6 +44,8 @@ def create_grid_map(width,height):
r = mapnik.Rule()
#symb = mapnik.PointSymbolizer()
symb = mapnik.MarkersSymbolizer()
+ symb.width = 10
+ symb.height = 10
symb.allow_overlap = True
r.symbols.append(symb)
label = mapnik.TextSymbolizer(mapnik.Expression('[Name]'),
View
2 tests/python_tests/render_test.py
@@ -127,6 +127,8 @@ def test_render_grid():
r = mapnik.Rule()
#symb = mapnik.PointSymbolizer()
symb = mapnik.MarkersSymbolizer()
+ symb.width = 10
+ symb.height = 10
symb.allow_overlap = True
r.symbols.append(symb)
label = mapnik.TextSymbolizer(mapnik.Expression('[Name]'),

0 comments on commit b93c760

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