Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Add support for "multi-policy" in marker symbolizer (#1555) #1573

Closed
wants to merge 4 commits into from

2 participants

@strk

Implements "largest" and "whole" policies with "point" placement
in a new apply_markers helper function. Uses it from AGG.

strk added some commits
@strk strk Add support for "multi-policy" in marker symbolizer (#1555)
Implements "largest" and "whole" policies with "point" placement
in a new apply_markers helper function. Uses it from AGG.
e84f63c
@strk strk Use apply_markers from cairo renderer (#1555) 7233d0c
@springmeyer
Owner

It appears this is missing modifications to src/grid/process_markers_symbolizer.cpp. Am I seeing that right?.

@strk

Probably. Testing those modifications locally (I hope "make test" is enough to trust them)

@strk

Uhm, I get a lot of failures on "make test", is that expected ?

Ran 507 tests in 21.814s

FAILED (TODO=5, errors=4, failures=27)
@strk

Well I get those failures before the modification too, so yes, it's expected.
Pushed new commit implementing the grid part (no testcase for it yet)

@springmeyer
Owner

Can you post a gist of all the test output you get?

@springmeyer
Owner

Those failures are bad - looks to me like you are running tests against a completely different mapnik version, likely quite old.

@springmeyer
Owner

tests are passing for me. How about you not worry about this for now (tonight). I know its late for you. I will take a look now. Check back in the morning.

@strk
@springmeyer springmeyer closed this pull request from a commit
@springmeyer springmeyer add marker-multi-policy parameter to support user-configurable render…
…ing behavior for multi-geometries when using either point or interior placement - closes #1573, refs #1555
f39c3ad
@springmeyer
Owner

In f39c3ad I:

  • forced clipping off for MARKER_WHOLE_MULTI to avoid clipping a point (which may result in bogus results)
  • changed apply_markers slightly so that non-multi geometries would not incur any overhead
  • added python bindings
  • added python object tests
  • fixed indentation (4 spaces) and other c++ style issues as per https://github.com/mapnik/mapnik/blob/master/docs/contributing.markdown
  • enabled MARKER_LARGEST_MULTI to work along with MARKER_INTERIOR_PLACEMENT - as it seems reasonable that a user setting interior would still want the point on surface of the largest polygon
@strk

Thank you Dane !
Should we file another issue to see this ported to 2.1.x ? Or could master possibly be re-targetted to 2.1.1 ?

@springmeyer
Owner

Yes. It will need to be very carefully ported to 2.1.x branch (soon to be 2.1.1), if that is what you need. Master has enough new stuff and is a long way towards 2.2.

@strk strk referenced this pull request from a commit in strk/mapnik
@strk strk Add marker-multi-policy parameter
This is to support user-configurable rendering behavior for
multi-geometries when using either point or interior placement
See #1573 and #1555
4a0933e
@strk

I found a problem with this, due to:

marker_multi_policy != 'each' has no effect with marker_placement != 'point'

Basically when specifying marker-multi-policy:whole and marker-placement:line the renderer simply doesn't render anything !

@springmeyer
Owner

If I'm understanding correctly I should ignore the above comment, as the issue is actually different and tracked at #1591?

@strk

Yes, ignore the comment. Sorry for the noise.

@PetrDlouhy PetrDlouhy referenced this pull request from a commit in PetrDlouhy/mapnik
@springmeyer springmeyer add marker-multi-policy parameter to support user-configurable render…
…ing behavior for multi-geometries when using either point or interior placement - closes #1573, refs #1555
0f8e061
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 13, 2012
  1. @strk

    Add support for "multi-policy" in marker symbolizer (#1555)

    strk authored
    Implements "largest" and "whole" policies with "point" placement
    in a new apply_markers helper function. Uses it from AGG.
  2. @strk
  3. @strk
Commits on Nov 20, 2012
  1. @strk
This page is out of date. Refresh to see the latest.
View
119 include/mapnik/marker_helpers.hpp
@@ -398,6 +398,125 @@ void setup_transform_scaling(agg::trans_affine & tr, box2d<double> const& bbox,
}
}
+// Compute centroid over a set of paths
+template <typename Iter>
+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)
+ {
+ geometry_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;
+}
+
+// Apply markers to a feature, dealing with marker-multi-policy
+template <typename Converter>
+void apply_markers(feature_impl & feature, Converter& converter, markers_symbolizer const& sym)
+{
+ 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 ( centroid_geoms(feature.paths().begin(), feature.paths().end(), x, y) ) {
+ std::auto_ptr<geometry_type> pt(new geometry_type(Point)); // TODO : allocate on stack
+ pt->move_to(x, y);
+ converter.apply(*pt);
+ } // else it is empty
+ }
+ else if ( placement == MARKER_POINT_PLACEMENT &&
+ multi_policy == MARKER_LARGEST_MULTI )
+ {
+ // Only apply to path with largest envelope
+ // WARNING: it won't necessarely be the one with
+ // largest area !!
+ double maxarea = 0;
+ geometry_type* largest = 0;
+ BOOST_FOREACH(geometry_type & geom, feature.paths())
+ {
+ const box2d<double>& env = geom.envelope();
+ double area = env.width() * env.height();
+ if ( area > maxarea ) {
+ maxarea = area;
+ largest = &geom;
+ }
+ }
+ if ( largest ) converter.apply(*largest); // else empty
+ }
+ 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'";
+ // TODO: should we error out instead ?
+ // throw config_error(std::string("Could not create datasource. No plugin ")
+ }
+
+ BOOST_FOREACH(geometry_type & path, feature.paths())
+ {
+ converter.apply(path);
+ }
+ }
+}
+
}
#endif //MAPNIK_MARKER_HELPERS_HPP
View
13 include/mapnik/markers_symbolizer.hpp
@@ -46,6 +46,16 @@ enum marker_placement_enum {
DEFINE_ENUM( marker_placement_e, marker_placement_enum );
+// TODO - consider merging with other symbolizers
+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 +84,8 @@ struct MAPNIK_DECL markers_symbolizer :
boost::optional<stroke> 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 +98,7 @@ struct MAPNIK_DECL markers_symbolizer :
boost::optional<float> opacity_;
boost::optional<stroke> stroke_;
marker_placement_e marker_p_;
+ marker_multi_policy_e marker_mp_;
};
}
View
18 src/agg/process_markers_symbolizer.cpp
@@ -26,6 +26,7 @@
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/debug.hpp>
+#include <mapnik/feature.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/vertex_converters.hpp>
@@ -136,10 +137,8 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+
+ apply_markers(feature, converter, sym);
}
else
{
@@ -172,10 +171,8 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+
+ apply_markers(feature, converter, sym);
}
}
else // raster markers
@@ -208,10 +205,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+ apply_markers(feature, converter, sym);
}
}
}
View
15 src/cairo_renderer.cpp
@@ -1762,10 +1762,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
}
converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+ apply_markers(feature, converter, sym);
}
else
{
@@ -1790,10 +1787,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
}
converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+ apply_markers(feature, converter, sym);
}
}
else // raster markers
@@ -1823,10 +1817,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
}
converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+ apply_markers(feature, converter, sym);
}
}
}
View
15 src/grid/process_markers_symbolizer.cpp
@@ -163,10 +163,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+ apply_markers(feature, converter, sym);
}
else
{
@@ -208,10 +205,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+ apply_markers(feature, converter, sym);
}
}
else // raster markers
@@ -256,10 +250,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
- BOOST_FOREACH(geometry_type & geom, feature.paths())
- {
- converter.apply(geom);
- }
+ apply_markers(feature, converter, sym);
}
}
}
View
4 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<marker_placement_e>("placement",symbol.get_marker_placement());
symbol.set_marker_placement(placement);
+
+ marker_multi_policy_e mpolicy = sym.get_attr<marker_multi_policy_e>("multi-policy",symbol.get_marker_multi_policy());
+ symbol.set_marker_multi_policy(mpolicy);
+
parse_symbolizer_base(symbol, sym);
rule.append(symbol);
}
View
32 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_;
+}
+
}
View
4 src/save_map.cpp
@@ -319,6 +319,10 @@ class serialize_symbolizer : public boost::static_visitor<>
{
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();
View
1  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);
View
3  tests/visual_tests/data/marker-multi-policy.csv
@@ -0,0 +1,3 @@
+i|wkt
+1|MULTIPOLYGON(((-10 -50,-21.7157287525381 -78.2842712474619,-49.9999999999999 -90,-78.2842712474618 -78.284271247462,-90 -50.0000000000001,-78.284271247462 -21.7157287525382,-50.0000000000002 -10,-21.7157287525383 -21.7157287525379,-10 -50)),((90 -50,78.2842712474619 -78.2842712474619,50.0000000000001 -90,21.7157287525382 -78.284271247462,10 -50.0000000000001,21.715728752538 -21.7157287525382,49.9999999999998 -10,78.2842712474617 -21.7157287525379,90 -50)),((90 50,50.0000000000001 10,10 49.9999999999999,49.9999999999998 90,90 50)))
+2|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)))
View
BIN  tests/visual_tests/images/marker-multi-policy-600-reference.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
36 tests/visual_tests/styles/marker-multi-policy.xml
@@ -0,0 +1,36 @@
+<Map maximum-extent="-100,-100,100,100">
+<Style name="each" filter-mode="first" >
+ <Rule>
+ <Filter>[i] = 1</Filter>
+ <MarkersSymbolizer placement="point" fill="red" width="20" />
+ </Rule>
+</Style>
+<Style name="whole" filter-mode="first" allow-overlap="true" >
+ <Rule>
+ <Filter>[i] = 1</Filter>
+ <MarkersSymbolizer placement="point" multi-policy="whole" fill="yellow" width="10" />
+ </Rule>
+</Style>
+<Style name="largest" filter-mode="first" allow-overlap="true" >
+ <Rule>
+ <Filter>[i] = 2</Filter>
+ <MarkersSymbolizer placement="point" multi-policy="largest" fill="blue" width="20" />
+ </Rule>
+</Style>
+<Style name="boundary" filter-mode="first" >
+ <Rule>
+ <LineSymbolizer />
+ </Rule>
+</Style>
+<Layer name="multi">
+ <StyleName>boundary</StyleName>
+ <StyleName>each</StyleName>
+ <StyleName>whole</StyleName>
+ <StyleName>largest</StyleName>
+ <Datasource>
+ <Parameter name="type"><![CDATA[csv]]></Parameter>
+ <Parameter name="file"><![CDATA[../data/marker-multi-policy.csv]]></Parameter>
+ <Parameter name="separator"><![CDATA[|]]></Parameter>
+ </Datasource>
+ </Layer>
+</Map>
View
1  tests/visual_tests/test.py
@@ -32,6 +32,7 @@
{'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': "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},
Something went wrong with that request. Please try again.