From 8a5f03b9230f5ec20adee6db2d2029f8998f8c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20B=C3=B6sch-Plepelits?= Date: Tue, 4 Jun 2013 09:06:26 +0200 Subject: [PATCH 1/5] Visual Tests for issue mapnik/mapnik#1900 Conflicts: tests/visual_tests/test.py --- ...formatting-5-500-100-1.0-agg-reference.png | Bin 0 -> 3566 bytes ...rmatting-5-500-100-1.0-cairo-reference.png | Bin 0 -> 2085 bytes ...formatting-5-500-100-2.0-agg-reference.png | Bin 0 -> 8590 bytes ...rmatting-5-500-100-2.0-cairo-reference.png | Bin 0 -> 4649 bytes ...formatting-6-500-100-1.0-agg-reference.png | Bin 0 -> 3566 bytes ...rmatting-6-500-100-1.0-cairo-reference.png | Bin 0 -> 2085 bytes ...formatting-6-500-100-2.0-agg-reference.png | Bin 0 -> 8590 bytes ...rmatting-6-500-100-2.0-cairo-reference.png | Bin 0 -> 4649 bytes tests/visual_tests/styles/formatting-5.xml | 23 +++++++++++++++++ tests/visual_tests/styles/formatting-6.xml | 24 ++++++++++++++++++ tests/visual_tests/test.py | 2 ++ 11 files changed, 49 insertions(+) create mode 100644 tests/visual_tests/images/formatting-5-500-100-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/formatting-5-500-100-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/formatting-5-500-100-2.0-agg-reference.png create mode 100644 tests/visual_tests/images/formatting-5-500-100-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/formatting-6-500-100-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/formatting-6-500-100-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/formatting-6-500-100-2.0-agg-reference.png create mode 100644 tests/visual_tests/images/formatting-6-500-100-2.0-cairo-reference.png create mode 100644 tests/visual_tests/styles/formatting-5.xml create mode 100644 tests/visual_tests/styles/formatting-6.xml diff --git a/tests/visual_tests/images/formatting-5-500-100-1.0-agg-reference.png b/tests/visual_tests/images/formatting-5-500-100-1.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..b64b808beefafa0fb91f1ef929e949f5819668e7 GIT binary patch literal 3566 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 C>+c)@ literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/formatting-5-500-100-1.0-cairo-reference.png b/tests/visual_tests/images/formatting-5-500-100-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..6e735965e0bffde1d76d5aaf408977c82890c3a2 GIT binary patch literal 2085 zcmc(g`#%$k8^ds45Q}3j(+o>)mmj&>j9h41GkRgZ%;Hn`V|8w`@8R|>ga#=#pIC>TqPa(G1^shX5+wgOlF0VJ|dx-7Ik zy`!(!_bz|?>ei=kg`37q%8SNQa)1L1x!!WRS#(|fIX36~^Ud$Zt^KfB_f_A5*k&8+ za--g8?FPLD+0ZGd!}U*;N>FVGC=*$+1MqHlh!E+IB0O#d+m{kNw^GFvLaZ?6**m2L zQw8gBkqMXQckT5pXUiESZdtWA+m1xBpvdiw1vV_Kb2*a}QSlwi#IikF+U#v!=@l3W zqcwnuSI+HCK*ZK|Dpu%9-SMT?KZgFKG#!Vvf6^u8-Mntsf`tF&xs$I8{`MR0^f--1fVf2}Js(mZP{qbS9I>MF2hF68r z0p=!c%HMAv;=PAcGRA&Uo)4~E4$jMQ>GT5@0w+BoWT1wO=(^zmmO`S1e(7 zT6nA3gfH0y^Hyn9@JP`EV3l5ZlcP#CUZ+k+X2z9K{Ce#~O}kEpQ&KQ(>FIQ2KHS2*A~IOyJkSb##97v=~(?VDd;0!6co%TBa!)0VcTd&a78x{-4BAf{}Oy*hY0ZpZ)`y}Olr zD}m7|u*i*$Ko67!8X94-Z_Znlu!vSP-zN8bsMksetxqTD?s5qCICeTFTVGIW_^au` z9}cwWn_#`F3pNOAx_IcjLbd^$R%pwEkkNs-W zCE4g?`>bxa&(Pwg`&kJk$%U=}@pjoMAl9UK2*o8OUruNV_hq_Ciyf2TW) z9sjKbkis<{PCxa@8#S=cY?T?agugUVxZOgkDp#L&MH1ejLjEYhbdGyuWRwvFT*Q2{(V=IzZ7Nu1K|dF4>**nbg&x}VHLd~Tw(ef zZ8K;1ee=2K zpVSDibNchH!rHFA@_mK0sI`$fqgSQXrf}lec{5GTd2l4%$8z-1!A#snbuo*flr^E8 z{NIiplgr%E@STvoZjRH!OLEqNC;EfUJEie>Qufsl77i?IjE83mG&xN z2ne@wFu+7-*32Yq4TcV%Rb4+@T|16YSmEs!N4}FSr#I7c#mB=P-)MEGr>0A_Kg`oz z-c3>j?YcO=KJ?war+};*FpRdW!0Dwe76=)9z0tQtQ5JVw{GA_Ml_1xAOn^D{S2uth zj&UKjpc-Btz-(`m8h=K)Of<~V{Pr7LW`6oX+Kmn4yrX%GIA25fcEJy1;;DW1lRgSx zgH9NAk~Y0C&p*!PL2#Jau|~*GYAq%yZ9YD6PwJnt!7)sg(VlSxlPCDH-rIfhq=gDm z3>ohg-h``#e;=-wCH>C%`C*m1q*cz_?v<3xO|<3gd?32XP(WS?ZJ%kPbY;wz-=}=& ogB*~X9o7VA?*a4v&nLBcNIG%0zC%pkk^Fmry9>gZ0*}c02jk4{mjD0& literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/formatting-5-500-100-2.0-agg-reference.png b/tests/visual_tests/images/formatting-5-500-100-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..da0140c7276261330d64bbbf3a980ff4accfc54c GIT binary patch literal 8590 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 Onu>zv$7+CO@c#f=aA~*z literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/formatting-5-500-100-2.0-cairo-reference.png b/tests/visual_tests/images/formatting-5-500-100-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..6ef13146f5c66742a875a850430422a0f2e3d3b7 GIT binary patch literal 4649 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 C>+c)@ literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/formatting-6-500-100-1.0-cairo-reference.png b/tests/visual_tests/images/formatting-6-500-100-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..6e735965e0bffde1d76d5aaf408977c82890c3a2 GIT binary patch literal 2085 zcmc(g`#%$k8^ds45Q}3j(+o>)mmj&>j9h41GkRgZ%;Hn`V|8w`@8R|>ga#=#pIC>TqPa(G1^shX5+wgOlF0VJ|dx-7Ik zy`!(!_bz|?>ei=kg`37q%8SNQa)1L1x!!WRS#(|fIX36~^Ud$Zt^KfB_f_A5*k&8+ za--g8?FPLD+0ZGd!}U*;N>FVGC=*$+1MqHlh!E+IB0O#d+m{kNw^GFvLaZ?6**m2L zQw8gBkqMXQckT5pXUiESZdtWA+m1xBpvdiw1vV_Kb2*a}QSlwi#IikF+U#v!=@l3W zqcwnuSI+HCK*ZK|Dpu%9-SMT?KZgFKG#!Vvf6^u8-Mntsf`tF&xs$I8{`MR0^f--1fVf2}Js(mZP{qbS9I>MF2hF68r z0p=!c%HMAv;=PAcGRA&Uo)4~E4$jMQ>GT5@0w+BoWT1wO=(^zmmO`S1e(7 zT6nA3gfH0y^Hyn9@JP`EV3l5ZlcP#CUZ+k+X2z9K{Ce#~O}kEpQ&KQ(>FIQ2KHS2*A~IOyJkSb##97v=~(?VDd;0!6co%TBa!)0VcTd&a78x{-4BAf{}Oy*hY0ZpZ)`y}Olr zD}m7|u*i*$Ko67!8X94-Z_Znlu!vSP-zN8bsMksetxqTD?s5qCICeTFTVGIW_^au` z9}cwWn_#`F3pNOAx_IcjLbd^$R%pwEkkNs-W zCE4g?`>bxa&(Pwg`&kJk$%U=}@pjoMAl9UK2*o8OUruNV_hq_Ciyf2TW) z9sjKbkis<{PCxa@8#S=cY?T?agugUVxZOgkDp#L&MH1ejLjEYhbdGyuWRwvFT*Q2{(V=IzZ7Nu1K|dF4>**nbg&x}VHLd~Tw(ef zZ8K;1ee=2K zpVSDibNchH!rHFA@_mK0sI`$fqgSQXrf}lec{5GTd2l4%$8z-1!A#snbuo*flr^E8 z{NIiplgr%E@STvoZjRH!OLEqNC;EfUJEie>Qufsl77i?IjE83mG&xN z2ne@wFu+7-*32Yq4TcV%Rb4+@T|16YSmEs!N4}FSr#I7c#mB=P-)MEGr>0A_Kg`oz z-c3>j?YcO=KJ?war+};*FpRdW!0Dwe76=)9z0tQtQ5JVw{GA_Ml_1xAOn^D{S2uth zj&UKjpc-Btz-(`m8h=K)Of<~V{Pr7LW`6oX+Kmn4yrX%GIA25fcEJy1;;DW1lRgSx zgH9NAk~Y0C&p*!PL2#Jau|~*GYAq%yZ9YD6PwJnt!7)sg(VlSxlPCDH-rIfhq=gDm z3>ohg-h``#e;=-wCH>C%`C*m1q*cz_?v<3xO|<3gd?32XP(WS?ZJ%kPbY;wz-=}=& ogB*~X9o7VA?*a4v&nLBcNIG%0zC%pkk^Fmry9>gZ0*}c02jk4{mjD0& literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/formatting-6-500-100-2.0-agg-reference.png b/tests/visual_tests/images/formatting-6-500-100-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..da0140c7276261330d64bbbf3a980ff4accfc54c GIT binary patch literal 8590 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 Onu>zv$7+CO@c#f=aA~*z literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/formatting-6-500-100-2.0-cairo-reference.png b/tests/visual_tests/images/formatting-6-500-100-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..6ef13146f5c66742a875a850430422a0f2e3d3b7 GIT binary patch literal 4649 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 xJ&Ad$mY4WfEb + + + + + + My Style + + osm + ../data/points.osm + + + + + + diff --git a/tests/visual_tests/styles/formatting-6.xml b/tests/visual_tests/styles/formatting-6.xml new file mode 100644 index 000000000..e2a1c1747 --- /dev/null +++ b/tests/visual_tests/styles/formatting-6.xml @@ -0,0 +1,24 @@ + + + + + + + + My Style + + osm + ../data/points.osm + + + + + + diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 9f613ad7d..36d6fb06e 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -138,6 +138,8 @@ files = { 'formatting-2': {'bbox':default_text_box}, 'formatting-3': {'bbox':default_text_box}, 'formatting-4': {'bbox':default_text_box}, + 'formatting-5': {'bbox':default_text_box}, + 'formatting-6': {'bbox':default_text_box}, 'expressionformat': {'bbox':default_text_box}, 'shieldsymbolizer-1': {'sizes': sizes_many_in_small_range,'bbox':default_text_box}, 'shieldsymbolizer-2': {'sizes': sizes_many_in_small_range,'bbox':default_text_box}, From 2940de0b0be00aee1fba06702c1e3e3bdfd0ff11 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Mon, 28 Jul 2014 15:05:51 +0000 Subject: [PATCH 2/5] refactor markers placements --- include/build.py | 15 +- include/mapnik/grid/grid_marker_helpers.hpp | 158 +++------ include/mapnik/marker_helpers.hpp | 136 +++----- include/mapnik/markers_placement.hpp | 308 ++++-------------- .../mapnik/markers_placements/interior.hpp | 99 ++++++ include/mapnik/markers_placements/line.hpp | 241 ++++++++++++++ include/mapnik/markers_placements/point.hpp | 157 +++++++++ src/cairo/cairo_renderer.cpp | 175 ++++------ 8 files changed, 713 insertions(+), 576 deletions(-) create mode 100644 include/mapnik/markers_placements/interior.hpp create mode 100644 include/mapnik/markers_placements/line.hpp create mode 100644 include/mapnik/markers_placements/point.hpp diff --git a/include/build.py b/include/build.py index 138853a81..5629841db 100644 --- a/include/build.py +++ b/include/build.py @@ -25,7 +25,20 @@ from glob import glob Import('env') base = './mapnik/' -subdirs = ['','svg','wkt','cairo','grid','json','util','group','text','text/placements','text/formatting'] +subdirs = [ + '', + 'svg', + 'wkt', + 'cairo', + 'grid', + 'json', + 'util', + 'group', + 'text', + 'text/placements', + 'text/formatting', + 'markers_placements' + ] if env['SVG_RENDERER']: subdirs.append('svg/output') diff --git a/include/mapnik/grid/grid_marker_helpers.hpp b/include/mapnik/grid/grid_marker_helpers.hpp index 75408eed2..1279d0565 100644 --- a/include/mapnik/grid/grid_marker_helpers.hpp +++ b/include/mapnik/grid/grid_marker_helpers.hpp @@ -80,72 +80,29 @@ struct raster_markers_rasterizer_dispatch_grid : mapnik::noncopyable marker_placement_enum placement_method = get(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT); bool ignore_placement = get(sym_, keys::ignore_placement, feature_, vars_, false); bool allow_overlap = get(sym_, keys::allow_overlap, feature_, vars_, false); - - box2d bbox(0,0, src_.width(),src_.height()); - if (placement_method != MARKER_LINE_PLACEMENT || - path.type() == geometry_type::types::Point) + box2d bbox(0,0, src_.width(), src_.height()); + double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); + double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); + markers_placement_finder placement_finder( + placement_method, + path, + bbox, + marker_trans_, + detector_, + spacing * scale_factor_, + max_error, + allow_overlap); + double x, y, angle = .0; + while (placement_finder.get_point(x, y, angle, ignore_placement)) { - double x = 0; - double y = 0; - if (path.type() == geometry_type::types::LineString) - { - if (!label::middle_point(path, x, y)) - { - return; - } - } - else if (placement_method == MARKER_INTERIOR_PLACEMENT) - { - if (!label::interior_position(path, x, y)) - { - return; - } - } - else - { - if (!label::centroid(path, x, y)) - { - return; - } - } agg::trans_affine matrix = marker_trans_; - matrix.translate(x,y); - box2d transformed_bbox = bbox * matrix; - if (allow_overlap || - detector_.has_placement(transformed_bbox)) + matrix.rotate(angle); + matrix.translate(x, y); + render_raster_marker(matrix); + if (!placed_) { - render_raster_marker(matrix); - if (!ignore_placement) - { - detector_.insert(transformed_bbox); - } - if (!placed_) - { - pixmap_.add_feature(feature_); - placed_ = true; - } - } - } - else - { - double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); - double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); - markers_placement placement(path, bbox, marker_trans_, detector_, - spacing * scale_factor_, - max_error, - allow_overlap); - double x, y, angle; - while (placement.get_point(x, y, angle, ignore_placement)) - { - agg::trans_affine matrix = marker_trans_; - matrix.rotate(angle); - matrix.translate(x, y); - render_raster_marker(matrix); - if (!placed_) - { - pixmap_.add_feature(feature_); - placed_ = true; - } + pixmap_.add_feature(feature_); + placed_ = true; } } } @@ -239,69 +196,34 @@ struct vector_markers_rasterizer_dispatch_grid : mapnik::noncopyable bool ignore_placement = get(sym_, keys::ignore_placement, feature_, vars_, false); double opacity = get(sym_,keys::opacity, feature_, vars_, 1.0); bool allow_overlap = get(sym_, keys::allow_overlap, feature_, vars_, false); - coord2d center = bbox_.center(); agg::trans_affine_translation recenter(-center.x, -center.y); - - if (placement_method != MARKER_LINE_PLACEMENT || - path.type() == geometry_type::types::Point) + double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); + double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); + markers_placement_finder placement_finder( + placement_method, + path, + bbox_, + marker_trans_, + detector_, + spacing * scale_factor_, + max_error, + allow_overlap); + double x, y, angle = .0; + while (placement_finder.get_point(x, y, angle, ignore_placement)) { - double x = 0; - double y = 0; - if (path.type() == geometry_type::types::LineString) - { - if (!label::middle_point(path, x, y)) return; - } - else if (placement_method == MARKER_INTERIOR_PLACEMENT) - { - if (!label::interior_position(path, x, y)) return; - } - else - { - if (!label::centroid(path, x, y)) return; - } - agg::trans_affine matrix = recenter * marker_trans_; - matrix.translate(x,y); - box2d transformed_bbox = bbox_ * matrix; - if (allow_overlap || - detector_.has_placement(transformed_bbox)) + matrix.rotate(angle); + matrix.translate(x, y); + svg_renderer_.render_id(ras_, sl_, renb_, feature_.id(), matrix, opacity, bbox_); + if (!placed_) { - svg_renderer_.render_id(ras_, sl_, renb_, feature_.id(), matrix, opacity, bbox_); - if (!ignore_placement) - { - detector_.insert(transformed_bbox); - } - if (!placed_) - { - pixmap_.add_feature(feature_); - placed_ = true; - } - } - } - else - { - double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); - double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); - markers_placement placement(path, bbox_, marker_trans_, detector_, - spacing * scale_factor_, - max_error, - allow_overlap); - double x, y, angle; - while (placement.get_point(x, y, angle, ignore_placement)) - { - agg::trans_affine matrix = recenter * marker_trans_; - matrix.rotate(angle); - matrix.translate(x, y); - svg_renderer_.render_id(ras_, sl_, renb_, feature_.id(), matrix, opacity, bbox_); - if (!placed_) - { - pixmap_.add_feature(feature_); - placed_ = true; - } + pixmap_.add_feature(feature_); + placed_ = true; } } } + private: BufferType & buf_; pixfmt_type pixf_; diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index aeecec6aa..b1467f9b5 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -36,6 +36,8 @@ #include #include #include +#include +#include // agg #include "agg_ellipse.h" @@ -111,69 +113,35 @@ struct vector_markers_rasterizer_dispatch : mapnik::noncopyable bool ignore_placement = get(sym_, keys::ignore_placement, feature_, vars_, false); bool allow_overlap = get(sym_, keys::allow_overlap, feature_, vars_, false); double opacity = get(sym_,keys::opacity, feature_, vars_, 1.0); - coord2d center = bbox_.center(); agg::trans_affine_translation recenter(-center.x, -center.y); - - if (placement_method != MARKER_LINE_PLACEMENT || - path.type() == mapnik::geometry_type::types::Point) + double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); + double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); + markers_placement_finder placement_finder( + placement_method, + path, + bbox_, + marker_trans_, + detector_, + spacing * scale_factor_, + max_error, + allow_overlap); + double x, y, angle = .0; + while (placement_finder.get_point(x, y, angle, ignore_placement)) { - double x = 0; - double y = 0; - if (path.type() == mapnik::geometry_type::types::LineString) - { - if (!label::middle_point(path, x, y)) return; - } - else if (placement_method == MARKER_INTERIOR_PLACEMENT) - { - if (!label::interior_position(path, x, y)) return; - } - else - { - if (!label::centroid(path, x, y)) return; - } agg::trans_affine matrix = recenter * marker_trans_; - matrix.translate(x,y); + matrix.rotate(angle); + matrix.translate(x, y); if (snap_to_pixels_) { // https://github.com/mapnik/mapnik/issues/1316 - matrix.tx = std::floor(matrix.tx+.5); - matrix.ty = std::floor(matrix.ty+.5); - } - // TODO https://github.com/mapnik/mapnik/issues/1754 - box2d transformed_bbox = bbox_ * matrix; - - if (allow_overlap || - detector_.has_placement(transformed_bbox)) - { - svg_renderer_.render(ras_, sl_, renb_, matrix, opacity, bbox_); - if (!ignore_placement) - { - detector_.insert(transformed_bbox); - } - } - } - else - { - double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); - double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); - markers_placement placement(path, bbox_, marker_trans_, detector_, - spacing * scale_factor_, - max_error, - allow_overlap); - double x = 0; - double y = 0; - double angle = 0; - while (placement.get_point(x, y, angle, ignore_placement)) - { - - agg::trans_affine matrix = recenter * marker_trans_; - matrix.rotate(angle); - matrix.translate(x, y); - svg_renderer_.render(ras_, sl_, renb_, matrix, opacity, bbox_); + matrix.tx = std::floor(matrix.tx + .5); + matrix.ty = std::floor(matrix.ty + .5); } + svg_renderer_.render(ras_, sl_, renb_, matrix, opacity, bbox_); } } + private: BufferType & buf_; pixfmt_type pixf_; @@ -236,54 +204,24 @@ struct raster_markers_rasterizer_dispatch : mapnik::noncopyable box2d bbox_(0,0, src_.width(),src_.height()); double opacity = get(sym_, keys::opacity, feature_, vars_, 1.0); bool ignore_placement = get(sym_, keys::ignore_placement, feature_, vars_, false); - - if (placement_method != MARKER_LINE_PLACEMENT || - path.type() == mapnik::geometry_type::types::Point) + double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); + double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); + markers_placement_finder placement_finder( + placement_method, + path, + bbox_, + marker_trans_, + detector_, + spacing * scale_factor_, + max_error, + allow_overlap); + double x, y, angle = .0; + while (placement_finder.get_point(x, y, angle, ignore_placement)) { - double x = 0; - double y = 0; - if (path.type() == mapnik::geometry_type::types::LineString) - { - if (!label::middle_point(path, x, y)) return; - } - else if (placement_method == MARKER_INTERIOR_PLACEMENT) - { - if (!label::interior_position(path, x, y)) return; - } - else - { - if (!label::centroid(path, x, y)) return; - } agg::trans_affine matrix = marker_trans_; - matrix.translate(x,y); - box2d transformed_bbox = bbox_ * matrix; - - if (allow_overlap || - detector_.has_placement(transformed_bbox)) - { - render_raster_marker(matrix, opacity); - if (!ignore_placement) - { - detector_.insert(transformed_bbox); - } - } - } - else - { - double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); - double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); - markers_placement placement(path, bbox_, marker_trans_, detector_, - spacing * scale_factor_, - max_error, - allow_overlap); - double x, y, angle; - while (placement.get_point(x, y, angle, ignore_placement)) - { - agg::trans_affine matrix = marker_trans_; - matrix.rotate(angle); - matrix.translate(x, y); - render_raster_marker(matrix, opacity); - } + matrix.rotate(angle); + matrix.translate(x, y); + render_raster_marker(matrix, opacity); } } diff --git a/include/mapnik/markers_placement.hpp b/include/mapnik/markers_placement.hpp index 4cee42b6a..6a784a790 100644 --- a/include/mapnik/markers_placement.hpp +++ b/include/mapnik/markers_placement.hpp @@ -23,83 +23,55 @@ #ifndef MAPNIK_MARKERS_PLACEMENT_HPP #define MAPNIK_MARKERS_PLACEMENT_HPP -// mapnik -#include -#include -#include -#include //round -#include -#include +#include +#include +#include +#include -// agg -#include "agg_basics.h" -#include "agg_trans_affine.h" +#include +#include +#include -// stl -#include - -namespace mapnik { +namespace mapnik +{ template -class markers_placement : mapnik::noncopyable +class markers_placement_finder : mapnik::noncopyable { public: - /** Constructor for markers_placement object. - * \param locator Path along which markers are placed (type: vertex source) - * \param size Size of the marker - * \param tr Affine transform - * \param detector Collision detection - * \param spacing Distance between markers. If the value is negative it is - * converted to a positive value with similar magnitude, but - * choosen to optimize marker placement. 0 = no markers - */ - markers_placement(Locator &locator, - box2d const& size, - agg::trans_affine const& tr, - Detector &detector, - double spacing, - double max_error, - bool allow_overlap) - : locator_(locator), - size_(size), - tr_(tr), - detector_(detector), - max_error_(max_error), - allow_overlap_(allow_overlap), - marker_width_((size_ * tr_).width()), - done_(false), - last_x(0.0), - last_y(0.0), - next_x(0.0), - next_y(0.0), - error_(0.0), - spacing_left_(0.0), - marker_nr_(0) - { - if (spacing >= 1) - { - spacing_ = spacing; - } - else - { - spacing_ = 100; - } - rewind(); - } + using markers_placement = boost::variant, + markers_line_placement, + markers_interior_placement>; - /** Start again at first marker. - * \note Returns the same list of markers only works when they were NOT added - * to the detector. - */ - void rewind() + class get_point_visitor : public boost::static_visitor + { + public: + get_point_visitor(double &x, double &y, double &angle, bool ignore_placement) + : x_(x), y_(y), angle_(angle), ignore_placement_(ignore_placement) + { + } + + template + bool operator()(T &placement) const + { + return placement.get_point(x_, y_, angle_, ignore_placement_); + } + + private: + double &x_, &y_, &angle_; + bool ignore_placement_; + }; + + markers_placement_finder(marker_placement_e placement_type, + Locator &locator, + box2d const& size, + agg::trans_affine const& tr, + Detector &detector, + double spacing, + double max_error, + bool allow_overlap) + : placement_(create(placement_type, locator, size, tr, detector, spacing, max_error, allow_overlap)) { - locator_.rewind(0); - //Get first point - done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)); - last_x = next_x; - last_y = next_y; // Force request of new segment - error_ = 0.0; - marker_nr_ = 0; } /** Get a point where the marker should be placed. @@ -110,184 +82,40 @@ public: * \param ignore_placement Whether to add selected position to detector * \return True if a place is found, false if none is found. */ - bool get_point(double & x, double & y, double & angle, bool ignore_placement) + bool get_point(double &x, double &y, double &angle, bool ignore_placement) { - if (done_) - { - return false; - } - unsigned cmd; - /* This functions starts at the position of the previous marker, - walks along the path, counting how far it has to go in spacing_left. - If one marker can't be placed at the position it should go to it is - moved a bit. The error is compensated for in the next call to this - function. - - error > 0: Marker too near to the end of the path. - error = 0: Perfect position. - error < 0: Marker too near to the beginning of the path. - */ - if (marker_nr_ == 0) - { - //First marker - marker_nr_++; - spacing_left_ = spacing_ / 2; - } - else - { - spacing_left_ = spacing_; - } - spacing_left_ -= error_; - error_ = 0.0; - double max_err_allowed = max_error_ * spacing_; - // Loop exits when a position is found or when no more segments are available - while (true) - { - // Do not place markers too close to the beginning of a segment - if (spacing_left_ < marker_width_/2) - { - set_spacing_left(marker_width_/2); //Only moves forward - } - // Error for this marker is too large. Skip to the next position. - if (std::fabs(error_) > max_err_allowed) - { - if (error_ > spacing_) - { - error_ = spacing_; // Avoid moving backwards - } - spacing_left_ += spacing_ - error_; - error_ = 0.0; - } - double dx = next_x - last_x; - double dy = next_y - last_y; - double segment_length = std::sqrt(dx * dx + dy * dy); - if (segment_length <= spacing_left_) - { - //Segment is too short to place marker. Find next segment - spacing_left_ -= segment_length; - last_x = next_x; - last_y = next_y; - while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y))) - { - //Skip over "move" commands - last_x = next_x; - last_y = next_y; - } - if (agg::is_stop(cmd) || cmd == SEG_CLOSE) - { - done_ = true; - return false; - } - continue; //Try again - } - /* At this point we know the following things: - - segment_length > spacing_left - - error is small enough - - at least half a marker fits into this segment - */ - //Check if marker really fits in this segment - if (segment_length < marker_width_) - { - //Segment to short => Skip this segment - set_spacing_left(segment_length + marker_width_/2); //Only moves forward - continue; - } - else if (segment_length - spacing_left_ < marker_width_/2) - { - //Segment is long enough, but we are to close to the end - //Note: This function moves backwards. This could lead to an infinite - // loop when another function adds a positive offset. Therefore we - // only move backwards when there is no offset - if (error_ == 0) - { - set_spacing_left(segment_length - marker_width_/2, true); - } - else - { - //Skip this segment - set_spacing_left(segment_length + marker_width_/2); //Only moves forward - } - continue; //Force checking of max_error constraint - } - angle = std::atan2(dy, dx); - x = last_x + dx * (spacing_left_ / segment_length); - y = last_y + dy * (spacing_left_ / segment_length); - box2d box = perform_transform(angle, x, y); - if (!allow_overlap_ && !detector_.has_placement(box)) - { - //10.0 is the approximate number of positions tried and choosen arbitrarily - set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward - continue; - } - if (!ignore_placement) - { - detector_.insert(box); - } - last_x = x; - last_y = y; - return true; - } + return boost::apply_visitor(get_point_visitor(x, y, angle, ignore_placement), placement_); } private: - Locator &locator_; - box2d size_; - agg::trans_affine tr_; - Detector &detector_; - double spacing_; - double max_error_; - bool allow_overlap_; - double marker_width_; - - bool done_; - double last_x; - double last_y; - double next_x; - double next_y; - /** If a marker could not be placed at the exact point where it should - * go the next marker's distance will be a bit lower. */ - double error_; - double spacing_left_; - unsigned marker_nr_; - - /** Rotates the size_ box and translates the position. */ - box2d perform_transform(double angle, double dx, double dy) + /** Factory function for particular placement implementations. + */ + static markers_placement create(marker_placement_e placement_type, + Locator &locator, + box2d const& size, + agg::trans_affine const& tr, + Detector &detector, + double spacing, + double max_error, + bool allow_overlap) { - double x1 = size_.minx(); - double x2 = size_.maxx(); - double y1 = size_.miny(); - double y2 = size_.maxy(); - agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle).translate(dx, dy); - double xA = x1, yA = y1, xB = x2, yB = y1, xC = x2, yC = y2, xD = x1, yD = y2; - tr.transform(&xA, &yA); - tr.transform(&xB, &yB); - tr.transform(&xC, &yC); - tr.transform(&xD, &yD); - box2d result(xA, yA, xC, yC); - result.expand_to_include(xB, yB); - result.expand_to_include(xD, yD); - return result; - } - - /** Set spacing_left_, adjusts error_ and performs sanity checks. */ - void set_spacing_left(double sl, bool allow_negative=false) - { - double delta_error = sl - spacing_left_; - if (!allow_negative && delta_error < 0) - { - MAPNIK_LOG_WARN(markers_placement) << "Unexpected negative error in markers_placement. Please file a bug report."; - return; - } - #ifdef MAPNIK_DEBUG - if (delta_error == 0.0) - { - MAPNIK_LOG_WARN(markers_placement) << "Not moving at all in set_spacing_left()! Please file a bug report."; - } - #endif - error_ += delta_error; - spacing_left_ = sl; + static const std::map const& size, + agg::trans_affine const& tr, + Detector &detector, + double spacing, + double max_error, + bool allow_overlap)>> factories = + { + { MARKER_POINT_PLACEMENT, boost::value_factory>() }, + { MARKER_INTERIOR_PLACEMENT, boost::value_factory>() }, + { MARKER_LINE_PLACEMENT, boost::value_factory>() } + }; + return factories.at(placement_type)(locator, size, tr, detector, spacing, max_error, allow_overlap); } + markers_placement placement_; }; } diff --git a/include/mapnik/markers_placements/interior.hpp b/include/mapnik/markers_placements/interior.hpp new file mode 100644 index 000000000..02d998655 --- /dev/null +++ b/include/mapnik/markers_placements/interior.hpp @@ -0,0 +1,99 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2014 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_MARKERS_PLACEMENTS_INTERIOR_HPP +#define MAPNIK_MARKERS_PLACEMENTS_INTERIOR_HPP + +#include +#include + +namespace mapnik { + +template +class markers_interior_placement : public markers_point_placement +{ +public: + markers_interior_placement( + Locator &locator, + box2d const& size, + agg::trans_affine const& tr, + Detector &detector, + double spacing, + double max_error, + bool allow_overlap) + : markers_point_placement( + locator, size, tr, detector, + spacing, max_error, allow_overlap) + { + } + + bool get_point(double &x, double &y, double &angle, bool ignore_placement) + { + if (this->done_) + { + return false; + } + + if (this->locator_.type() == mapnik::geometry_type::types::Point) + { + return markers_point_placement::get_point(x, y, angle, ignore_placement); + } + + if (this->locator_.type() == mapnik::geometry_type::types::LineString) + { + if (!label::middle_point(this->locator_, x, y)) + { + this->done_ = true; + return false; + } + } + else + { + if (!label::interior_position(this->locator_, x, y)) + { + this->done_ = true; + return false; + } + } + + angle = 0; + + box2d box = this->perform_transform(angle, x, y); + + if (!this->allow_overlap_ && !this->detector_.has_placement(box)) + { + return false; + } + + if (!ignore_placement) + { + this->detector_.insert(box); + } + + this->done_ = true; + return true; + } +}; + +} + +#endif // MAPNIK_MARKERS_PLACEMENTS_INTERIOR_HPP diff --git a/include/mapnik/markers_placements/line.hpp b/include/mapnik/markers_placements/line.hpp new file mode 100644 index 000000000..3244ede79 --- /dev/null +++ b/include/mapnik/markers_placements/line.hpp @@ -0,0 +1,241 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2014 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_MARKERS_PLACEMENTS_LINE_HPP +#define MAPNIK_MARKERS_PLACEMENTS_LINE_HPP + +#include +#include +#include +#include //round + +#include + +namespace mapnik { + +template +class markers_line_placement : public markers_point_placement +{ +public: + markers_line_placement(Locator &locator, + box2d const& size, + agg::trans_affine const& tr, + Detector &detector, + double spacing, + double max_error, + bool allow_overlap) + : markers_point_placement( + locator, size, tr, detector, + spacing, max_error, allow_overlap), + last_x(0.0), + last_y(0.0), + next_x(0.0), + next_y(0.0), + error_(0.0), + spacing_left_(0.0), + marker_nr_(0) + { + if (spacing >= 1) + { + this->spacing_ = spacing; + } + else + { + this->spacing_ = 100; + } + rewind(); + } + + void rewind() + { + this->locator_.rewind(0); + //Get first point + this->done_ = agg::is_stop(this->locator_.vertex(&next_x, &next_y)); + last_x = next_x; + last_y = next_y; // Force request of new segment + error_ = 0.0; + marker_nr_ = 0; + } + + bool get_point(double &x, double &y, double &angle, bool ignore_placement) + { + if (this->done_) + { + return false; + } + + if (this->locator_.type() == mapnik::geometry_type::types::Point) + { + return markers_point_placement::get_point(x, y, angle, ignore_placement); + } + + unsigned cmd; + /* This functions starts at the position of the previous marker, + walks along the path, counting how far it has to go in spacing_left. + If one marker can't be placed at the position it should go to it is + moved a bit. The error is compensated for in the next call to this + function. + + error > 0: Marker too near to the end of the path. + error = 0: Perfect position. + error < 0: Marker too near to the beginning of the path. + */ + if (marker_nr_ == 0) + { + //First marker + marker_nr_++; + this->spacing_left_ = this->spacing_ / 2; + } + else + { + this->spacing_left_ = this->spacing_; + } + spacing_left_ -= error_; + error_ = 0.0; + double max_err_allowed = this->max_error_ * this->spacing_; + //Loop exits when a position is found or when no more segments are available + while (true) + { + //Do not place markers too close to the beginning of a segment + if (spacing_left_ < this->marker_width_/2) + { + set_spacing_left(this->marker_width_/2); //Only moves forward + } + //Error for this marker is too large. Skip to the next position. + if (std::fabs(error_) > max_err_allowed) + { + while (this->error_ > this->spacing_) + { + error_ -= this->spacing_; //Avoid moving backwards + } + spacing_left_ += this->spacing_ - this->error_; + error_ = 0.0; + } + double dx = next_x - last_x; + double dy = next_y - last_y; + double segment_length = std::sqrt(dx * dx + dy * dy); + if (segment_length <= spacing_left_) + { + //Segment is too short to place marker. Find next segment + spacing_left_ -= segment_length; + last_x = next_x; + last_y = next_y; + while (agg::is_move_to(cmd = this->locator_.vertex(&next_x, &next_y))) + { + //Skip over "move" commands + last_x = next_x; + last_y = next_y; + } + if (agg::is_stop(cmd) || cmd == SEG_CLOSE) + { + this->done_ = true; + return false; + } + continue; //Try again + } + /* At this point we know the following things: + - segment_length > spacing_left + - error is small enough + - at least half a marker fits into this segment + */ + //Check if marker really fits in this segment + if (segment_length < this->marker_width_) + { + //Segment to short => Skip this segment + set_spacing_left(segment_length + this->marker_width_/2); //Only moves forward + continue; + } + else if (segment_length - spacing_left_ < this->marker_width_/2) + { + //Segment is long enough, but we are to close to the end + //Note: This function moves backwards. This could lead to an infinite + // loop when another function adds a positive offset. Therefore we + // only move backwards when there is no offset + if (error_ == 0) + { + set_spacing_left(segment_length - this->marker_width_/2, true); + } + else + { + //Skip this segment + set_spacing_left(segment_length + this->marker_width_/2); //Only moves forward + } + continue; //Force checking of max_error constraint + } + angle = std::atan2(dy, dx); + x = last_x + dx * (spacing_left_ / segment_length); + y = last_y + dy * (spacing_left_ / segment_length); + box2d box = this->perform_transform(angle, x, y); + if (!this->allow_overlap_ && !this->detector_.has_placement(box)) + { + //10.0 is the approxmiate number of positions tried and choosen arbitrarily + set_spacing_left(spacing_left_ + this->spacing_ * this->max_error_ / 10.0); //Only moves forward + continue; + } + if (!ignore_placement) + { + this->detector_.insert(box); + } + last_x = x; + last_y = y; + return true; + } + } + +private: + double last_x; + double last_y; + double next_x; + double next_y; + /** If a marker could not be placed at the exact point where it should + * go the next marker's distance will be a bit lower. */ + double error_; + double spacing_left_; + unsigned marker_nr_; + + /** Set spacing_left_, adjusts error_ and performs sanity checks. */ + void set_spacing_left(double sl, bool allow_negative=false) + { + double delta_error = sl - spacing_left_; + if (!allow_negative && delta_error < 0) + { + MAPNIK_LOG_WARN(markers_line_placement) + << "Unexpected negative error in markers_line_placement. " + "Please file a bug report."; + return; + } +#ifdef MAPNIK_DEBUG + if (delta_error == 0.0) + { + MAPNIK_LOG_WARN(markers_line_placement) + << "Not moving at all in set_spacing_left()! " + "Please file a bug report."; + } +#endif + error_ += delta_error; + spacing_left_ = sl; + } +}; + +} + +#endif // MAPNIK_MARKERS_PLACEMENTS_LINE_HPP diff --git a/include/mapnik/markers_placements/point.hpp b/include/mapnik/markers_placements/point.hpp new file mode 100644 index 000000000..b153a54e7 --- /dev/null +++ b/include/mapnik/markers_placements/point.hpp @@ -0,0 +1,157 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2014 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_MARKERS_PLACEMENTS_POINT_HPP +#define MAPNIK_MARKERS_PLACEMENTS_POINT_HPP + +#include +#include + +#include "agg_basics.h" +#include "agg_trans_affine.h" + +namespace mapnik { + +template +class markers_point_placement +{ +public: + markers_point_placement( + Locator &locator, + box2d const& size, + agg::trans_affine const& tr, + Detector &detector, + double spacing, + double max_error, + bool allow_overlap) + : locator_(locator), + size_(size), + tr_(tr), + detector_(detector), + spacing_(spacing), + max_error_(max_error), + allow_overlap_(allow_overlap), + marker_width_((size_ * tr_).width()), + done_(false) + { + rewind(); + } + + /** Start again at first marker. + * \note Returns the same list of markers only works when they were NOT added + * to the detector. + */ + virtual void rewind() + { + locator_.rewind(0); + done_ = false; + } + + /** Get a point where the marker should be placed. + * Each time this function is called a new point is returned. + * \param x Return value for x position + * \param y Return value for x position + * \param angle Return value for rotation angle + * \param ignore_placement Whether to add selected position to detector + * \return True if a place is found, false if none is found. + */ + virtual bool get_point(double &x, double &y, double &angle, bool ignore_placement) + { + if (done_) + { + return false; + } + + if (locator_.type() == mapnik::geometry_type::types::LineString) + { + if (!label::middle_point(locator_, x, y)) + { + done_ = true; + return false; + } + } + else + { + if (!label::centroid(locator_, x, y)) + { + done_ = true; + return false; + } + } + + angle = 0; + + box2d box = perform_transform(angle, x, y); + + if (!allow_overlap_ && !detector_.has_placement(box)) + { + return false; + } + + if (!ignore_placement) + { + detector_.insert(box); + } + + done_ = true; + return true; + } + +protected: + Locator &locator_; + box2d size_; + agg::trans_affine tr_; + Detector &detector_; + double spacing_; + double max_error_; + bool allow_overlap_; + double marker_width_; + bool done_; + + /** Rotates the size_ box and translates the position. */ + box2d perform_transform(double angle, double dx, double dy) + { + double x1 = size_.minx(); + double x2 = size_.maxx(); + double y1 = size_.miny(); + double y2 = size_.maxy(); + agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle) + .translate(dx, dy); + double xA = x1, yA = y1, + xB = x2, yB = y1, + xC = x2, yC = y2, + xD = x1, yD = y2; + tr.transform(&xA, &yA); + tr.transform(&xB, &yB); + tr.transform(&xC, &yC); + tr.transform(&xD, &yD); + box2d result(xA, yA, xC, yC); + result.expand_to_include(xB, yB); + result.expand_to_include(xD, yD); + return result; + } +}; + +} + +#endif // MAPNIK_MARKERS_PLACEMENTS_POINT_HPP + diff --git a/src/cairo/cairo_renderer.cpp b/src/cairo/cairo_renderer.cpp index ff554c7f8..7920e0315 100644 --- a/src/cairo/cairo_renderer.cpp +++ b/src/cairo/cairo_renderer.cpp @@ -798,63 +798,37 @@ struct markers_dispatch : mapnik::noncopyable template void add_path(T & path) { - marker_placement_enum placement_method = get(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT); - bool ignore_placement = get(sym_, keys::ignore_placement, feature_, vars_, false); - bool allow_overlap = get(sym_, keys::allow_overlap, feature_, vars_, false); - double opacity = get(sym_, keys::opacity, feature_, vars_, 1.0); - double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); - double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); - - if (placement_method != MARKER_LINE_PLACEMENT || - path.type() == geometry_type::types::Point) - { - double x = 0; - double y = 0; - if (path.type() == geometry_type::types::LineString) - { - if (!label::middle_point(path, x, y)) return; - } - else if (placement_method == MARKER_INTERIOR_PLACEMENT) - { - if (!label::interior_position(path, x, y)) return; - } - else - { - if (!label::centroid(path, x, y)) return; - } - coord2d center = bbox_.center(); - agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y); - matrix *= marker_trans_; - matrix *=agg::trans_affine_translation(x, y); - - box2d transformed_bbox = bbox_ * matrix; - - if (allow_overlap || - detector_.has_placement(transformed_bbox)) - { - render_vector_marker(ctx_, pixel_position(x,y), marker_, bbox_, attributes_, marker_trans_, opacity, true); - - if (!ignore_placement) - { - detector_.insert(transformed_bbox); - } - } - } - else - { - markers_placement placement(path, bbox_, marker_trans_, detector_, - spacing * scale_factor_, - max_error, - allow_overlap); - double x, y, angle; - while (placement.get_point(x, y, angle, ignore_placement)) - { - agg::trans_affine matrix = marker_trans_; - matrix.rotate(angle); - render_vector_marker(ctx_, pixel_position(x,y), marker_, bbox_, attributes_, matrix, opacity, true); - - } - } + marker_placement_enum placement_method = get( + sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT); + bool ignore_placement = get(sym_, keys::ignore_placement, feature_, vars_, false); + bool allow_overlap = get(sym_, keys::allow_overlap, feature_, vars_, false); + double opacity = get(sym_, keys::opacity, feature_, vars_, 1.0); + double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); + double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); + markers_placement_finder placement_finder( + placement_method, + path, + bbox_, + marker_trans_, + detector_, + spacing * scale_factor_, + max_error, + allow_overlap); + double x, y, angle = .0; + while (placement_finder.get_point(x, y, angle, ignore_placement)) + { + agg::trans_affine matrix = marker_trans_; + matrix.rotate(angle); + render_vector_marker( + ctx_, + pixel_position(x, y), + marker_, + bbox_, + attributes_, + matrix, + opacity, + true); + } } SvgPath & marker_; @@ -893,67 +867,32 @@ struct raster_markers_dispatch : mapnik::noncopyable template void add_path(T & path) { - marker_placement_enum placement_method = get(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT); - double opacity = get(sym_, keys::opacity, feature_, vars_, 1.0); - double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); - double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); - bool allow_overlap = get(sym_, keys::allow_overlap, feature_, vars_, false); - bool ignore_placement = get(sym_, keys::ignore_placement, feature_, vars_, false); - - if (placement_method != MARKER_LINE_PLACEMENT || - path.type() == geometry_type::types::Point) - { - double x = 0; - double y = 0; - if (path.type() == geometry_type::types::LineString) - { - if (!label::middle_point(path, x, y)) - return; - } - else if (placement_method == MARKER_INTERIOR_PLACEMENT) - { - if (!label::interior_position(path, x, y)) - return; - } - else - { - if (!label::centroid(path, x, y)) - return; - } - coord2d center = bbox_.center(); - agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y); - matrix *= marker_trans_; - matrix *=agg::trans_affine_translation(x, y); - - box2d transformed_bbox = bbox_ * matrix; - - if (allow_overlap || - detector_.has_placement(transformed_bbox)) - { - ctx_.add_image(matrix, marker_, opacity); - if (!ignore_placement) - { - detector_.insert(transformed_bbox); - } - } - } - else - { - markers_placement placement(path, bbox_, marker_trans_, detector_, - spacing * scale_factor_, - max_error, - allow_overlap); - double x, y, angle; - while (placement.get_point(x, y, angle, ignore_placement)) - { - coord2d center = bbox_.center(); - agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y); - matrix *= marker_trans_; - matrix *= agg::trans_affine_rotation(angle); - matrix *= agg::trans_affine_translation(x, y); - ctx_.add_image(matrix, marker_, opacity); - } - } + marker_placement_enum placement_method = get(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT); + double opacity = get(sym_, keys::opacity, feature_, vars_, 1.0); + double spacing = get(sym_, keys::spacing, feature_, vars_, 100.0); + double max_error = get(sym_, keys::max_error, feature_, vars_, 0.2); + bool allow_overlap = get(sym_, keys::allow_overlap, feature_, vars_, false); + bool ignore_placement = get(sym_, keys::ignore_placement, feature_, vars_, false); + markers_placement_finder placement_finder( + placement_method, + path, + bbox_, + marker_trans_, + detector_, + spacing * scale_factor_, + max_error, + allow_overlap); + double x, y, angle = .0; + while (placement_finder.get_point(x, y, angle, ignore_placement)) + { + coord2d center = bbox_.center(); + agg::trans_affine matrix = agg::trans_affine_translation( + -center.x, -center.y); + matrix *= marker_trans_; + matrix *= agg::trans_affine_rotation(angle); + matrix *= agg::trans_affine_translation(x, y); + ctx_.add_image(matrix, marker_, opacity); + } } ImageMarker & marker_; From 6a17e17c04c27a6a6ae55e48899684c6f45efd03 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Thu, 10 Jul 2014 11:31:10 +0000 Subject: [PATCH 3/5] fix markers transforms calc --- include/mapnik/markers_placements/point.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/mapnik/markers_placements/point.hpp b/include/mapnik/markers_placements/point.hpp index b153a54e7..0e3a74afd 100644 --- a/include/mapnik/markers_placements/point.hpp +++ b/include/mapnik/markers_placements/point.hpp @@ -54,6 +54,9 @@ public: done_(false) { rewind(); + + coord2d center = size.center(); + tr_ = agg::trans_affine_translation(-center.x, -center.y) * tr_; } /** Start again at first marker. From 6730408039a85e808186396417a161ee720b25db Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Tue, 29 Jul 2014 10:54:29 +0000 Subject: [PATCH 4/5] fix include --- include/mapnik/group/group_layout.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/mapnik/group/group_layout.hpp b/include/mapnik/group/group_layout.hpp index abf59d35c..18074acd7 100644 --- a/include/mapnik/group/group_layout.hpp +++ b/include/mapnik/group/group_layout.hpp @@ -26,6 +26,9 @@ // boost #include +// std +#include + namespace mapnik { From a48ed22a0624a2ac8ff536fcf5b87de6276c2dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20B=C3=B6sch-Plepelits?= Date: Tue, 29 Jul 2014 14:46:14 +0200 Subject: [PATCH 5/5] Use points.csv instead of points.osm - for mapnik/mapnik#1900 --- tests/visual_tests/styles/formatting-5.xml | 4 ++-- tests/visual_tests/styles/formatting-6.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/visual_tests/styles/formatting-5.xml b/tests/visual_tests/styles/formatting-5.xml index 9f44f6799..1e727e33f 100644 --- a/tests/visual_tests/styles/formatting-5.xml +++ b/tests/visual_tests/styles/formatting-5.xml @@ -6,8 +6,8 @@ My Style - osm - ../data/points.osm + csv + ../data/points.csv diff --git a/tests/visual_tests/styles/formatting-6.xml b/tests/visual_tests/styles/formatting-6.xml index e2a1c1747..830f80880 100644 --- a/tests/visual_tests/styles/formatting-6.xml +++ b/tests/visual_tests/styles/formatting-6.xml @@ -7,8 +7,8 @@ My Style - osm - ../data/points.osm + csv + ../data/points.csv