From f39c3ad857815a62d7f471f4377543882abec94c Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 20 Nov 2012 17:58:39 -0800 Subject: [PATCH] add marker-multi-policy parameter to support user-configurable rendering behavior for multi-geometries when using either point or interior placement - closes #1573, refs #1555 --- CHANGELOG.md | 2 + bindings/python/mapnik_markers_symbolizer.cpp | 10 +++ include/mapnik/geom_util.hpp | 64 ++++++++++++++++++ include/mapnik/marker_helpers.hpp | 62 +++++++++++++++++ include/mapnik/markers_symbolizer.hpp | 12 ++++ include/mapnik/vertex_converters.hpp | 10 +++ src/agg/process_markers_symbolizer.cpp | 17 ++--- src/cairo_renderer.cpp | 15 +--- src/grid/process_markers_symbolizer.cpp | 15 +--- src/load_map.cpp | 4 ++ src/markers_symbolizer.cpp | 32 ++++++++- src/save_map.cpp | 4 ++ src/xml_tree.cpp | 1 + tests/cpp_tests/label_algo_test.cpp | 3 + tests/python_tests/object_test.py | 5 ++ .../marker-multi-policy-600-reference.png | Bin 0 -> 12896 bytes tests/visual_tests/test.py | 2 +- 17 files changed, 217 insertions(+), 41 deletions(-) create mode 100644 tests/visual_tests/images/marker-multi-policy-600-reference.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 233108c57..4cbd9b924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ For a complete change history, see the git log. ## Future +- Added support for controlling rendering behavior of markers on multi-geometries `marker-multi-policy` (#1555,#1573) + - Added alternative PNG/ZLIB implementation (`miniz`) that can be enabled with `e=miniz` (#1554) - Added support for setting zlib `Z_FIXED` strategy with format string: `png:z=fixed` diff --git a/bindings/python/mapnik_markers_symbolizer.cpp b/bindings/python/mapnik_markers_symbolizer.cpp index 708aa1929..260db15e3 100644 --- a/bindings/python/mapnik_markers_symbolizer.cpp +++ b/bindings/python/mapnik_markers_symbolizer.cpp @@ -89,6 +89,12 @@ void export_markers_symbolizer() .value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT) ; + mapnik::enumeration_("marker_multi_policy") + .value("EACH",mapnik::MARKER_EACH_MULTI) + .value("WHOLE",mapnik::MARKER_WHOLE_MULTI) + .value("LARGEST",mapnik::MARKER_LARGEST_MULTI) + ; + class_("MarkersSymbolizer", init<>("Default Markers Symbolizer - circle")) .def (init("")) @@ -143,6 +149,10 @@ void export_markers_symbolizer() &markers_symbolizer::get_marker_placement, &markers_symbolizer::set_marker_placement, "Set/get the marker placement") + .add_property("multi_policy", + &markers_symbolizer::get_marker_multi_policy, + &markers_symbolizer::set_marker_multi_policy, + "Set/get the marker multi geometry rendering policy") .add_property("comp_op", &markers_symbolizer::comp_op, &markers_symbolizer::set_comp_op, diff --git a/include/mapnik/geom_util.hpp b/include/mapnik/geom_util.hpp index 7e90a7625..b3eeee7bd 100644 --- a/include/mapnik/geom_util.hpp +++ b/include/mapnik/geom_util.hpp @@ -329,6 +329,70 @@ bool centroid(PathType & path, double & x, double & y) return true; } +// Compute centroid over a set of paths +template +bool centroid_geoms(Iter start, Iter end, double & x, double & y) +{ + double x0 = 0.0; + double y0 = 0.0; + double x1 = 0.0; + double y1 = 0.0; + double start_x = x0; + double start_y = y0; + + bool empty = true; + + double atmp = 0.0; + double xtmp = 0.0; + double ytmp = 0.0; + unsigned count = 1; + + while (start!=end) + { + typename Iter::value_type & path = *start++; + path.rewind(0); + unsigned command = path.vertex(&x0, &y0); + if (command == SEG_END) continue; + empty = false; + + while (SEG_END != (command = path.vertex(&x1, &y1))) + { + double dx0 = x0 - start_x; + double dy0 = y0 - start_y; + double dx1 = x1 - start_x; + double dy1 = y1 - start_y; + double ai = dx0 * dy1 - dx1 * dy0; + atmp += ai; + xtmp += (dx1 + dx0) * ai; + ytmp += (dy1 + dy0) * ai; + x0 = x1; + y0 = y1; + ++count; + } + } + + if ( empty ) return false; + + if (count <= 2) { + x = (start_x + x0) * 0.5; + y = (start_y + y0) * 0.5; + return true; + } + + if (atmp != 0) + { + x = (xtmp/(3*atmp)) + start_x; + y = (ytmp/(3*atmp)) + start_y; + } + else + { + x = x0; + y = y0; + } + + return true; +} + template bool hit_test(PathType & path, double x, double y, double tol) { diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index 9677753ca..fd1a923d8 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -398,6 +398,68 @@ void setup_transform_scaling(agg::trans_affine & tr, box2d const& bbox, } } +// Apply markers to a feature with multiple geometries +template +void apply_markers_multi(feature_impl & feature, Converter& converter, markers_symbolizer const& sym) +{ + std::size_t geom_count = feature.paths().size(); + if (geom_count == 1) + { + converter.apply(feature.paths()[0]); + } + else if (geom_count > 1) + { + marker_multi_policy_e multi_policy = sym.get_marker_multi_policy(); + marker_placement_e placement = sym.get_marker_placement(); + if (placement == MARKER_POINT_PLACEMENT && + multi_policy == MARKER_WHOLE_MULTI) + { + double x, y; + if (label::centroid_geoms(feature.paths().begin(), feature.paths().end(), x, y)) + { + geometry_type pt(Point); + pt.move_to(x, y); + // unset any clipping since we're now dealing with a point + converter.template unset(); + converter.apply(pt); + } + } + else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) && + multi_policy == MARKER_LARGEST_MULTI) + { + // Only apply to path with largest envelope area + // TODO: consider using true area for polygon types + double maxarea = 0; + geometry_type* largest = 0; + BOOST_FOREACH(geometry_type & geom, feature.paths()) + { + const box2d& env = geom.envelope(); + double area = env.width() * env.height(); + if (area > maxarea) + { + maxarea = area; + largest = &geom; + } + } + if (largest) + { + converter.apply(*largest); + } + } + else + { + if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT) + { + MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'"; + } + BOOST_FOREACH(geometry_type & path, feature.paths()) + { + converter.apply(path); + } + } + } +} + } #endif //MAPNIK_MARKER_HELPERS_HPP diff --git a/include/mapnik/markers_symbolizer.hpp b/include/mapnik/markers_symbolizer.hpp index 1dd832de6..ddcb6a8f2 100644 --- a/include/mapnik/markers_symbolizer.hpp +++ b/include/mapnik/markers_symbolizer.hpp @@ -46,6 +46,15 @@ enum marker_placement_enum { DEFINE_ENUM( marker_placement_e, marker_placement_enum ); +enum marker_multi_policy_enum { + MARKER_EACH_MULTI, // each component in a multi gets its marker + MARKER_WHOLE_MULTI, // consider all components of a multi as a whole + MARKER_LARGEST_MULTI, // only the largest component of a multi gets a marker + marker_multi_policy_enum_MAX +}; + +DEFINE_ENUM( marker_multi_policy_e, marker_multi_policy_enum ); + struct MAPNIK_DECL markers_symbolizer : public symbolizer_with_image, public symbolizer_base { @@ -74,6 +83,8 @@ public: boost::optional get_stroke() const; void set_marker_placement(marker_placement_e marker_p); marker_placement_e get_marker_placement() const; + void set_marker_multi_policy(marker_multi_policy_e marker_p); + marker_multi_policy_e get_marker_multi_policy() const; private: expression_ptr width_; expression_ptr height_; @@ -86,6 +97,7 @@ private: boost::optional opacity_; boost::optional stroke_; marker_placement_e marker_p_; + marker_multi_policy_e marker_mp_; }; } diff --git a/include/mapnik/vertex_converters.hpp b/include/mapnik/vertex_converters.hpp index c49112d1f..5070c0ad9 100644 --- a/include/mapnik/vertex_converters.hpp +++ b/include/mapnik/vertex_converters.hpp @@ -365,6 +365,16 @@ struct vertex_converter : private boost::noncopyable disp_.vec_[index]=1; } + template + void unset() + { + typedef typename boost::mpl::find::type iter; + typedef typename boost::mpl::end::type end; + std::size_t index = boost::mpl::distance::value - 1; + if (index < disp_.vec_.size()) + disp_.vec_[index]=0; + } + detail::dispatcher disp_; }; diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index cef6f1694..a2d371cd5 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -136,10 +137,7 @@ void agg_renderer::process(markers_symbolizer const& sym, } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } else { @@ -172,10 +170,7 @@ void agg_renderer::process(markers_symbolizer const& sym, } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } } else // raster markers @@ -207,11 +202,7 @@ void agg_renderer::process(markers_symbolizer const& sym, } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter - - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } } } diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index 417e41f46..7f43ba5cf 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -1762,10 +1762,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym, } converter.set(); //always transform if (sym.smooth() > 0.0) converter.set(); // optional smooth converter - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } else { @@ -1790,10 +1787,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym, } converter.set(); //always transform if (sym.smooth() > 0.0) converter.set(); // optional smooth converter - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } } else // raster markers @@ -1823,10 +1817,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym, } converter.set(); //always transform if (sym.smooth() > 0.0) converter.set(); // optional smooth converter - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } } } diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index 5d2b2fc6c..ef8792e1a 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -163,10 +163,7 @@ void grid_renderer::process(markers_symbolizer const& sym, } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } else { @@ -208,10 +205,7 @@ void grid_renderer::process(markers_symbolizer const& sym, } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } } else // raster markers @@ -256,10 +250,7 @@ void grid_renderer::process(markers_symbolizer const& sym, } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter - BOOST_FOREACH(geometry_type & geom, feature.paths()) - { - converter.apply(geom); - } + apply_markers_multi(feature, converter, sym); } } } diff --git a/src/load_map.cpp b/src/load_map.cpp index e1f005bfa..ef58168ff 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -1050,6 +1050,10 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& sym) marker_placement_e placement = sym.get_attr("placement",symbol.get_marker_placement()); symbol.set_marker_placement(placement); + + marker_multi_policy_e mpolicy = sym.get_attr("multi-policy",symbol.get_marker_multi_policy()); + symbol.set_marker_multi_policy(mpolicy); + parse_symbolizer_base(symbol, sym); rule.append(symbol); } diff --git a/src/markers_symbolizer.cpp b/src/markers_symbolizer.cpp index e992ef59e..cf9d47e2a 100644 --- a/src/markers_symbolizer.cpp +++ b/src/markers_symbolizer.cpp @@ -37,6 +37,15 @@ static const char * marker_placement_strings[] = { IMPLEMENT_ENUM( marker_placement_e, marker_placement_strings ) +static const char * marker_multi_policy_strings[] = { + "each", + "whole", + "largest", + "" +}; + +IMPLEMENT_ENUM( marker_multi_policy_e, marker_multi_policy_strings ) + markers_symbolizer::markers_symbolizer() : symbolizer_with_image(parse_path("shape://ellipse")), symbolizer_base(), @@ -46,7 +55,10 @@ markers_symbolizer::markers_symbolizer() allow_overlap_(false), spacing_(100.0), max_error_(0.2), - marker_p_(MARKER_POINT_PLACEMENT) { } + marker_p_(MARKER_POINT_PLACEMENT), + // TODO: consider defaulting to MARKER_WHOLE_MULTI, + // for backward compatibility with 2.0.0 + marker_mp_(MARKER_EACH_MULTI) { } markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename) : symbolizer_with_image(filename), @@ -57,7 +69,10 @@ markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename) allow_overlap_(false), spacing_(100.0), max_error_(0.2), - marker_p_(MARKER_POINT_PLACEMENT) { } + marker_p_(MARKER_POINT_PLACEMENT), + // TODO: consider defaulting to MARKER_WHOLE_MULTI, + // for backward compatibility with 2.0.0 + marker_mp_(MARKER_EACH_MULTI) { } markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs) : symbolizer_with_image(rhs), @@ -71,7 +86,8 @@ markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs) fill_(rhs.fill_), fill_opacity_(rhs.fill_opacity_), stroke_(rhs.stroke_), - marker_p_(rhs.marker_p_) {} + marker_p_(rhs.marker_p_), + marker_mp_(rhs.marker_mp_) {} void markers_symbolizer::set_ignore_placement(bool ignore_placement) { @@ -173,4 +189,14 @@ marker_placement_e markers_symbolizer::get_marker_placement() const return marker_p_; } +void markers_symbolizer::set_marker_multi_policy(marker_multi_policy_e marker_mp) +{ + marker_mp_ = marker_mp; +} + +marker_multi_policy_e markers_symbolizer::get_marker_multi_policy() const +{ + return marker_mp_; +} + } diff --git a/src/save_map.cpp b/src/save_map.cpp index 55996e471..5b330b5e7 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -319,6 +319,10 @@ public: { set_attr( sym_node, "placement", sym.get_marker_placement() ); } + if ( sym.get_marker_multi_policy() != dfl.get_marker_multi_policy() || explicit_defaults_ ) + { + set_attr( sym_node, "multi-policy", sym.get_marker_multi_policy() ); + } if (sym.get_image_transform()) { std::string tr_str = sym.get_image_transform_string(); diff --git a/src/xml_tree.cpp b/src/xml_tree.cpp index 36f032ccb..ed1b9bc08 100644 --- a/src/xml_tree.cpp +++ b/src/xml_tree.cpp @@ -471,6 +471,7 @@ compile_get_attr(std::string); compile_get_attr(filter_mode_e); compile_get_attr(point_placement_e); compile_get_attr(marker_placement_e); +compile_get_attr(marker_multi_policy_e); compile_get_attr(pattern_alignment_e); compile_get_attr(line_rasterizer_e); compile_get_attr(colorizer_mode); diff --git a/tests/cpp_tests/label_algo_test.cpp b/tests/cpp_tests/label_algo_test.cpp index d43651afc..cf7a0d414 100644 --- a/tests/cpp_tests/label_algo_test.cpp +++ b/tests/cpp_tests/label_algo_test.cpp @@ -31,6 +31,9 @@ int main( int, char*[] ) BOOST_TEST( x == 25 ); BOOST_TEST( y == 25 ); + // TODO - centroid and interior should be equal but they appear not to be (check largest) + // MULTIPOLYGON(((-52 40,-60 32,-68 40,-60 48,-52 40)),((-60 50,-80 30,-100 49.9999999999999,-80.0000000000001 70,-60 50)),((-52 60,-60 52,-68 60,-60 68,-52 60))) + if (!::boost::detail::test_errors()) { std::clog << "C++ label algorithms: \x1b[1;32m✓ \x1b[0m\n"; #if BOOST_VERSION >= 104600 diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index 52a428bcd..98ecf8b99 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -221,6 +221,7 @@ def test_markers_symbolizer(): eq_(p.fill_opacity,None) eq_(p.filename,'shape://ellipse') eq_(p.placement,mapnik.marker_placement.POINT_PLACEMENT) + eq_(p.multi_policy,mapnik.marker_multi_policy.EACH) eq_(p.fill,None) eq_(p.ignore_placement,False) eq_(p.spacing,100) @@ -251,10 +252,14 @@ def test_markers_symbolizer(): p.allow_overlap = True p.opacity = 0.5 p.fill_opacity = 0.5 + p.placement = mapnik.marker_placement.LINE_PLACEMENT + p.multi_policy = mapnik.marker_multi_policy.WHOLE eq_(p.allow_overlap, True) eq_(p.opacity, 0.5) eq_(p.fill_opacity, 0.5) + eq_(p.multi_policy,mapnik.marker_multi_policy.WHOLE) + eq_(p.placement,mapnik.marker_placement.LINE_PLACEMENT) #https://github.com/mapnik/mapnik/issues/1285 #https://github.com/mapnik/mapnik/issues/1427 diff --git a/tests/visual_tests/images/marker-multi-policy-600-reference.png b/tests/visual_tests/images/marker-multi-policy-600-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..0233625a5097c35656a1cfdfaee33f46d3aa22c2 GIT binary patch literal 12896 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�a=*{w7YY6AkN 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 TW5FK{Kp?j?^)xEgY#;wWRpzq> literal 0 HcmV?d00001 diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 93f684619..ef0c82e0d 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -37,7 +37,7 @@ files = [ {'name': "lines-2", 'sizes': sizes_few_square,'bbox':default_text_box}, {'name': "lines-3", 'sizes': sizes_few_square,'bbox':default_text_box}, {'name': "lines-shield", 'sizes': sizes_few_square,'bbox':default_text_box}, - #{'name': "marker-multi-policy", 'sizes':[(600,400)]}, + {'name': "marker-multi-policy", 'sizes':[(600,400)]}, {'name': "simple-E", 'bbox':mapnik.Box2d(-0.05, -0.01, 0.95, 0.01)}, {'name': "simple-NE",'bbox':default_text_box}, {'name': "simple-NW",'bbox':default_text_box},