From 2e76ff1958de5ead81208559c8666377f0927dda Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 2 Apr 2012 11:57:24 -0700 Subject: [PATCH 01/50] make stroke property on line_symbolizer a reference when accessed from python (not a copy) --- bindings/python/mapnik_line_symbolizer.cpp | 2 +- tests/python_tests/object_test.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bindings/python/mapnik_line_symbolizer.cpp b/bindings/python/mapnik_line_symbolizer.cpp index 9c7053b90..7ce8d5dc7 100644 --- a/bindings/python/mapnik_line_symbolizer.cpp +++ b/bindings/python/mapnik_line_symbolizer.cpp @@ -60,7 +60,7 @@ void export_line_symbolizer() "Set/get the rasterization method of the line of the point") .add_property("stroke",make_function (&line_symbolizer::get_stroke, - return_value_policy()), + return_value_policy()), &line_symbolizer::set_stroke) .add_property("smooth", &line_symbolizer::smooth, diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index cf0920d55..9bcfc0cad 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -21,6 +21,15 @@ def test_line_symbolizer_init(): s = mapnik.LineSymbolizer() eq_(s.rasterizer, mapnik.line_rasterizer.FULL) +def test_line_symbolizer_stroke_reference(): + l = mapnik.LineSymbolizer(mapnik.Color('green'),0.1) + l.stroke.add_dash(.1,.1) + l.stroke.add_dash(.1,.1) + eq_(l.stroke.get_dashes(), [(.1,.1),(.1,.1)]) + eq_(l.stroke.color,mapnik.Color('green')) + eq_(l.stroke.opacity,1.0) + assert_almost_equal(l.stroke.width,0.1) + # ShieldSymbolizer initialization def test_shieldsymbolizer_init(): s = mapnik.ShieldSymbolizer(mapnik.Expression('[Field Name]'), 'DejaVu Sans Bold', 6, mapnik.Color('#000000'), mapnik.PathExpression('../data/images/dummy.png')) From 8931509adce1a5e2753f80e503a484f335d81b27 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 30 Jul 2012 18:31:15 +0100 Subject: [PATCH 02/50] + move middle_point into label namespace --- include/mapnik/geom_util.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mapnik/geom_util.hpp b/include/mapnik/geom_util.hpp index 31432aa33..c3dda724b 100644 --- a/include/mapnik/geom_util.hpp +++ b/include/mapnik/geom_util.hpp @@ -241,6 +241,8 @@ double path_length(PathType & path) return length; } +namespace label { + template bool middle_point(PathType & path, double & x, double & y) { @@ -271,8 +273,6 @@ bool middle_point(PathType & path, double & x, double & y) return true; } -namespace label { - template bool centroid(PathType & path, double & x, double & y) { From 822531a114a82090284139716706270c7b4a2949 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 30 Jul 2012 18:32:42 +0100 Subject: [PATCH 03/50] + filter on geometry type to use centroid (Polygon) or middle_point (LineString) label placement --- src/symbolizer_helpers.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/symbolizer_helpers.cpp b/src/symbolizer_helpers.cpp index 1cf014bb9..85a917787 100644 --- a/src/symbolizer_helpers.cpp +++ b/src/symbolizer_helpers.cpp @@ -244,7 +244,14 @@ void text_symbolizer_helper::initialize_points() { if (how_placed == POINT_PLACEMENT) { - label::centroid(geom, label_x, label_y); + if (geom.type() == Polygon) + { + label::centroid(geom, label_x, label_y); + } + else if (geom.type() == LineString) + { + label::middle_point(geom, label_x,label_y); + } } else if (how_placed == INTERIOR_PLACEMENT) { From ce7b6a93fbd5594c061d50a31d8222fabc567a1e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 30 Jul 2012 14:48:32 -0700 Subject: [PATCH 04/50] Add test case for #1354 --- .../good_maps/text_halo_and_collision.xml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/data/good_maps/text_halo_and_collision.xml diff --git a/tests/data/good_maps/text_halo_and_collision.xml b/tests/data/good_maps/text_halo_and_collision.xml new file mode 100644 index 000000000..d7155566d --- /dev/null +++ b/tests/data/good_maps/text_halo_and_collision.xml @@ -0,0 +1,53 @@ + + + + + + + + + labels + + csv + +x,y +2,2 +2,2.3 + + + + + + + + + + frame + + csv + +x,y +0,0 +5,0 +0,5 +5,5 + + + + + \ No newline at end of file From 1a6cbfb568325c62c11b2f20e4c39ae9644acee3 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 30 Jul 2012 15:54:08 -0700 Subject: [PATCH 05/50] fix xml comment --- tests/data/good_maps/text_halo_and_collision.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/data/good_maps/text_halo_and_collision.xml b/tests/data/good_maps/text_halo_and_collision.xml index d7155566d..b44b3e2dd 100644 --- a/tests/data/good_maps/text_halo_and_collision.xml +++ b/tests/data/good_maps/text_halo_and_collision.xml @@ -1,6 +1,5 @@ - - + + + + ellipse + + csv + +x,y +2.5,2.5 + + + + + + + + + + frame + + csv + +x,y +0,0 +5,0 +0,5 +5,5 + + + + + \ No newline at end of file From 4d2eb73e3b3a4b787d0e617079d22ea4710e9072 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 16:04:42 -0700 Subject: [PATCH 20/50] add tests for complex marker rendering functionality --- .../good_maps/marker_ellipse_transform.xml | 66 ++++++++++++++++++ .../support/mapnik-marker-ellipse-render1.png | Bin 0 -> 15130 bytes 2 files changed, 66 insertions(+) create mode 100644 tests/data/good_maps/marker_ellipse_transform.xml create mode 100644 tests/python_tests/images/support/mapnik-marker-ellipse-render1.png diff --git a/tests/data/good_maps/marker_ellipse_transform.xml b/tests/data/good_maps/marker_ellipse_transform.xml new file mode 100644 index 000000000..e96595619 --- /dev/null +++ b/tests/data/good_maps/marker_ellipse_transform.xml @@ -0,0 +1,66 @@ + + + + + + ellipse + + csv + +x,y +2.5,2.5 + + + + + + + + + + frame + + csv + +x,y +0,0 +5,0 +0,5 +5,5 + + + + + \ No newline at end of file diff --git a/tests/python_tests/images/support/mapnik-marker-ellipse-render1.png b/tests/python_tests/images/support/mapnik-marker-ellipse-render1.png new file mode 100644 index 0000000000000000000000000000000000000000..5a74baad0e4ec3a6f51b74a06db7b060ce8dcd9f GIT binary patch literal 15130 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 aioa$P)jk(7LT{c|Kwd^!x Date: Tue, 31 Jul 2012 16:40:30 -0700 Subject: [PATCH 21/50] move ellipse contruction code to marker_helpers --- include/mapnik/marker_helpers.hpp | 48 +++++++++++++++++++++++--- src/agg/process_markers_symbolizer.cpp | 44 +++++------------------ 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index 4d7a2be78..f22dd6ec5 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -27,12 +27,51 @@ #include #include #include +#include +#include + +// agg +#include "agg_ellipse.h" // boost #include namespace mapnik { +template +void build_ellipse(T const& sym, mapnik::feature_impl const& feature, svg_storage_type & marker_ellipse, svg::svg_path_adapter & svg_path) +{ + expression_ptr const& width_expr = sym.get_width(); + expression_ptr const& height_expr = sym.get_height(); + double width = 0; + double height = 0; + if (width_expr && height_expr) + { + width = boost::apply_visitor(evaluate(feature), *width_expr).to_double(); + height = boost::apply_visitor(evaluate(feature), *height_expr).to_double(); + } + else if (width_expr) + { + width = boost::apply_visitor(evaluate(feature), *width_expr).to_double(); + height = width; + } + else if (height_expr) + { + height = boost::apply_visitor(evaluate(feature), *height_expr).to_double(); + width = height; + } + svg::svg_converter_type styled_svg(svg_path, marker_ellipse.attributes()); + styled_svg.push_attr(); + styled_svg.begin_path(); + agg::ellipse c(0, 0, width/2.0, height/2.0); + styled_svg.storage().concat_path(c); + styled_svg.end_path(); + styled_svg.pop_attr(); + double lox,loy,hix,hiy; + styled_svg.bounding_rect(&lox, &loy, &hix, &hiy); + marker_ellipse.set_bounding_box(lox,loy,hix,hiy); +} + template bool push_explicit_style(Attr const& src, Attr & dst, markers_symbolizer const& sym) { @@ -42,10 +81,12 @@ bool push_explicit_style(Attr const& src, Attr & dst, markers_symbolizer const& boost::optional const& fill_opacity = sym.get_fill_opacity(); if (strk || fill || opacity || fill_opacity) { + bool success = false; for(unsigned i = 0; i < src.size(); ++i) { - mapnik::svg::path_attributes attr = src[i]; - + success = true; + dst.push_back(src[i]); + mapnik::svg::path_attributes & attr = dst.last(); if (attr.stroke_flag) { // TODO - stroke attributes need to be boost::optional @@ -87,9 +128,8 @@ bool push_explicit_style(Attr const& src, Attr & dst, markers_symbolizer const& attr.fill_opacity = *fill_opacity; } } - dst.push_back(attr); } - return true; + return success; } return false; } diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index bc5f9c851..40c7336a7 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -41,7 +40,6 @@ // agg #include "agg_basics.h" -#include "agg_ellipse.h" #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" #include "agg_rasterizer_scanline_aa.h" @@ -317,12 +315,15 @@ void agg_renderer::process(markers_symbolizer const& sym, { using namespace mapnik::svg; typedef agg::renderer_scanline_aa_solid renderer_type; + typedef agg::pod_bvector svg_attribute_type; typedef svg_renderer, + svg_attribute_type, renderer_type, agg::pixfmt_rgba32 > svg_renderer_type; - typedef vector_markers_rasterizer_dispatch dispatch_type; - + typedef vector_markers_rasterizer_dispatch dispatch_type; boost::optional const& stock_vector_marker = (*mark)->get_vector_data(); expression_ptr const& width_expr = sym.get_width(); expression_ptr const& height_expr = sym.get_height(); @@ -332,38 +333,11 @@ void agg_renderer::process(markers_symbolizer const& sym, if (filename == "shape://ellipse" && (width_expr || height_expr)) { - double width = 0; - double height = 0; - if (width_expr && height_expr) - { - width = boost::apply_visitor(evaluate(feature), *width_expr).to_double(); - height = boost::apply_visitor(evaluate(feature), *height_expr).to_double(); - } - else if (width_expr) - { - width = boost::apply_visitor(evaluate(feature), *width_expr).to_double(); - height = width; - } - else if (height_expr) - { - height = boost::apply_visitor(evaluate(feature), *height_expr).to_double(); - width = height; - } - // create a new marker svg_storage_type marker_ellipse; vertex_stl_adapter stl_storage(marker_ellipse.source()); svg_path_adapter svg_path(stl_storage); - svg_converter_type styled_svg(svg_path, marker_ellipse.attributes()); - styled_svg.push_attr(); - styled_svg.begin_path(); - agg::ellipse c(0, 0, width/2.0, height/2.0); - styled_svg.storage().concat_path(c); - styled_svg.end_path(); - styled_svg.pop_attr(); - double lox,loy,hix,hiy; - styled_svg.bounding_rect(&lox, &loy, &hix, &hiy); - marker_ellipse.set_bounding_box(lox,loy,hix,hiy); - agg::pod_bvector attributes; + build_ellipse(sym, feature, marker_ellipse, svg_path); + svg_attribute_type attributes; bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym); svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes()); evaluate_transform(tr, feature, sym.get_image_transform()); @@ -393,7 +367,7 @@ void agg_renderer::process(markers_symbolizer const& sym, agg::trans_affine marker_trans = recenter * tr; vertex_stl_adapter stl_storage((*stock_vector_marker)->source()); svg_path_adapter svg_path(stl_storage); - agg::pod_bvector attributes; + svg_attribute_type attributes; bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym); svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes()); dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr, From 3867a75adf90d807d156ee6fb52b1b432c66d718 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 16:42:07 -0700 Subject: [PATCH 22/50] comment crashing test --- .../markers_complex_rendering_test.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/python_tests/markers_complex_rendering_test.py diff --git a/tests/python_tests/markers_complex_rendering_test.py b/tests/python_tests/markers_complex_rendering_test.py new file mode 100644 index 000000000..a3173abbd --- /dev/null +++ b/tests/python_tests/markers_complex_rendering_test.py @@ -0,0 +1,39 @@ +#coding=utf8 +import os +import mapnik +from utilities import execution_path +from nose.tools import * + +def setup(): + # All of the paths used are relative, if we run the tests + # from another directory we need to chdir() + os.chdir(execution_path('.')) + +def test_marker_ellipse_render1(): + m = mapnik.Map(256,256) + mapnik.load_map(m,'../data/good_maps/marker_ellipse_transform.xml') + m.zoom_all() + im = mapnik.Image(m.width,m.height) + mapnik.render(m,im) + actual = '/tmp/mapnik-marker-ellipse-render1.png' + expected = 'images/support/mapnik-marker-ellipse-render1.png' + im.save(actual) + expected_im = mapnik.Image.open(expected) + eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected)) + +#def test_marker_ellipse_render2(): +# # currently crashes https://github.com/mapnik/mapnik/issues/1365 +# m = mapnik.Map(256,256) +# mapnik.load_map(m,'../data/good_maps/marker_ellipse_transform2.xml') +# m.zoom_all() +# im = mapnik.Image(m.width,m.height) +# mapnik.render(m,im) +# actual = '/tmp/mapnik-marker-ellipse-render2.png' +# expected = 'images/support/mapnik-marker-ellipse-render2.png' +# im.save(actual) +# expected_im = mapnik.Image.open(expected) +# eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected)) + +if __name__ == "__main__": + setup() + [eval(run)() for run in dir() if 'test_' in run] From 48c391da0d544d44d1fc9ac131cb951011c66b43 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 16:56:22 -0700 Subject: [PATCH 23/50] reduce copying of svg attributes via @lightmare - refs #1360 --- include/mapnik/svg/svg_converter.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mapnik/svg/svg_converter.hpp b/include/mapnik/svg/svg_converter.hpp index 7e61a09ff..4ba4c4bff 100644 --- a/include/mapnik/svg/svg_converter.hpp +++ b/include/mapnik/svg/svg_converter.hpp @@ -66,10 +66,10 @@ public: { throw std::runtime_error("end_path : The path was not begun"); } - path_attributes attr = cur_attr(); - unsigned idx = attributes_[attributes_.size() - 1].index; + path_attributes& attr = attributes_[attributes_.size() - 1]; + unsigned idx = attr.index; + attr = cur_attr(); attr.index = idx; - attributes_[attributes_.size() - 1] = attr; } void move_to(double x, double y, bool rel=false) // M, m From 64b990ef6c1387f4dde75c2d25645b1ab6227f09 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 17:00:24 -0700 Subject: [PATCH 24/50] viewer ignores --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 5d2536c3e..fe40dfa29 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,11 @@ demo/c++/demo.tif demo/c++/demo.jpg demo/c++/demo.png demo/c++/demo256.png +demo/viewer/Makefile +demo/viewer/Makefile.Debug +demo/viewer/Makefile.Release +demo/viewer/release/ +demo/viewer/ui_about.h +demo/viewer/ui_info.h +demo/viewer/ui_layer_info.h tests/cpp_tests/*-bin From 9d9c525de0eaebfa21a6dad4f71e9ba97f762cba Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 17:27:52 -0700 Subject: [PATCH 25/50] revert 1dfde78 and properly avoid clipping for point type geometries in markers_symbolizer while moving markers_placement to cpp file to avoid overhead of declaring all possible permutations of vertex convertors - closes #1341 --- include/mapnik/markers_placement.hpp | 219 ++++++++++++++++++++- src/agg/process_markers_symbolizer.cpp | 32 +++- src/build.py | 1 - src/markers_placement.cpp | 253 ------------------------- src/markers_symbolizer.cpp | 10 +- 5 files changed, 243 insertions(+), 272 deletions(-) delete mode 100644 src/markers_placement.cpp diff --git a/include/mapnik/markers_placement.hpp b/include/mapnik/markers_placement.hpp index a0828caba..5ac83a22d 100644 --- a/include/mapnik/markers_placement.hpp +++ b/include/mapnik/markers_placement.hpp @@ -24,13 +24,26 @@ #define MAPNIK_MARKERS_PLACEMENT_HPP // mapnik +#include +#include +#include +#include +#include //round #include // boost #include // agg +#include "agg_basics.h" +#include "agg_conv_clip_polygon.h" +#include "agg_conv_clip_polyline.h" +#include "agg_trans_affine.h" #include "agg_conv_transform.h" +#include "agg_conv_smooth_poly1.h" + +// stl +#include namespace mapnik { @@ -47,12 +60,40 @@ public: * 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); + 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(); + if (spacing >= 0) + { + spacing_ = spacing; + } else if (spacing < 0) + { + spacing_ = find_optimal_spacing(-spacing); + } + rewind(); + } + /** Start again at first marker. * \note Returns the same list of markers only works when they were NOT added * to the detector. */ - void rewind(); + void rewind() + { + locator_.rewind(0); + //Get first point + done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < marker_width_; + last_x = next_x; + last_y = next_y; // Force request of new segment + error_ = 0; + marker_nr_ = 0; + } + /** 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 @@ -61,7 +102,115 @@ public: * \param add_to_detector 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 add_to_detector = true); + bool get_point(double & x, double & y, double & angle, bool add_to_detector = true) + { + 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 + spacing_left_ = spacing_ / 2; + } else + { + spacing_left_ = spacing_; + } + spacing_left_ -= error_; + error_ = 0; + //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 (abs(error_) > max_error_ * spacing_) + { + if (error_ > spacing_) + { + error_ = 0; //Avoid moving backwards + MAPNIK_LOG_WARN(markers_placement) << "Extremely large error in markers_placement. Please file a bug report."; + } + spacing_left_ += spacing_ - error_; + error_ = 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 to 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)) + { + 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 = 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 approxmiate number of positions tried and choosen arbitrarily + set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward + continue; + } + if (add_to_detector) detector_.insert(box); + last_x = x; + last_y = y; + return true; + } + } + private: Locator &locator_; box2d size_; @@ -82,11 +231,69 @@ private: unsigned marker_nr_; /** Rotates the size_ box and translates the position. */ - box2d perform_transform(double angle, double dx, double dy); + 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; + } + /** Automatically chooses spacing. */ - double find_optimal_spacing(double s); + double find_optimal_spacing(double s) + { + rewind(); + //Calculate total path length + unsigned cmd = agg::path_cmd_move_to; + double length = 0; + while (!agg::is_stop(cmd)) + { + double dx = next_x - last_x; + double dy = next_y - last_y; + length += std::sqrt(dx * dx + dy * dy); + 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; + } + } + unsigned points = round(length / s); + if (points == 0) return 0.0; //Path to short + return length / points; + } + /** Set spacing_left_, adjusts error_ and performs sanity checks. */ - void set_spacing_left(double sl, bool allow_negative=false); + 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; + } + }; } diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 40c7336a7..79723f485 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -295,7 +295,7 @@ void agg_renderer::process(markers_symbolizer const& sym, typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; typedef agg::renderer_base renderer_base; typedef label_collision_detector4 detector_type; - typedef boost::mpl::vector conv_types; + typedef boost::mpl::vector conv_types; std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature); @@ -350,7 +350,15 @@ void agg_renderer::process(markers_symbolizer const& sym, vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_* 1.1,rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); - if (sym.clip()) converter.template set(); //optional clip (default: true) + if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) + { + eGeomType type = feature.paths()[0].type(); + if (type == Polygon) + converter.template set(); + else if (type == LineString) + converter.template set(); + // don't clip if type==Point + } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter BOOST_FOREACH(geometry_type & geom, feature.paths()) @@ -375,7 +383,15 @@ void agg_renderer::process(markers_symbolizer const& sym, vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_* 1.1,rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); - if (sym.clip()) converter.template set(); //optional clip (default: true) + if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) + { + eGeomType type = feature.paths()[0].type(); + if (type == Polygon) + converter.template set(); + else if (type == LineString) + converter.template set(); + // don't clip if type==Point + } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter BOOST_FOREACH(geometry_type & geom, feature.paths()) @@ -399,7 +415,15 @@ void agg_renderer::process(markers_symbolizer const& sym, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_* 1.1, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); - if (sym.clip()) converter.template set(); //optional clip (default: true) + if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) + { + eGeomType type = feature.paths()[0].type(); + if (type == Polygon) + converter.template set(); + else if (type == LineString) + converter.template set(); + // don't clip if type==Point + } converter.template set(); //always transform if (sym.smooth() > 0.0) converter.template set(); // optional smooth converter diff --git a/src/build.py b/src/build.py index a56aac0ea..5a74be3c3 100644 --- a/src/build.py +++ b/src/build.py @@ -172,7 +172,6 @@ source = Split( json/feature_grammar.cpp json/feature_collection_parser.cpp json/geojson_generator.cpp - markers_placement.cpp processed_text.cpp formatting/base.cpp formatting/expression.cpp diff --git a/src/markers_placement.cpp b/src/markers_placement.cpp deleted file mode 100644 index 6ea4b0e14..000000000 --- a/src/markers_placement.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// mapnik -#include -#include -#include -#include -#include //round -// agg -#include "agg_basics.h" -#include "agg_conv_clip_polyline.h" -#include "agg_trans_affine.h" -#include "agg_conv_transform.h" -#include "agg_conv_smooth_poly1.h" -// stl -#include - -namespace mapnik -{ -template -markers_placement::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(); - if (spacing >= 0) - { - spacing_ = spacing; - } else if (spacing < 0) - { - spacing_ = find_optimal_spacing(-spacing); - } - rewind(); -} - -template -double markers_placement::find_optimal_spacing(double s) -{ - rewind(); - //Calculate total path length - unsigned cmd = agg::path_cmd_move_to; - double length = 0; - while (!agg::is_stop(cmd)) - { - double dx = next_x - last_x; - double dy = next_y - last_y; - length += std::sqrt(dx * dx + dy * dy); - 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; - } - } - unsigned points = round(length / s); - if (points == 0) return 0.0; //Path to short - return length / points; -} - - -template -void markers_placement::rewind() -{ - locator_.rewind(0); - //Get first point - done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < marker_width_; - last_x = next_x; - last_y = next_y; // Force request of new segment - error_ = 0; - marker_nr_ = 0; -} - -template -bool markers_placement::get_point( - double & x, double & y, double & angle, bool add_to_detector) -{ - 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 - spacing_left_ = spacing_ / 2; - } else - { - spacing_left_ = spacing_; - } - - spacing_left_ -= error_; - error_ = 0; - - //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 (abs(error_) > max_error_ * spacing_) - { - if (error_ > spacing_) - { - error_ = 0; //Avoid moving backwards - - MAPNIK_LOG_WARN(markers_placement) << "Extremely large error in markers_placement. Please file a bug report."; - } - spacing_left_ += spacing_ - error_; - error_ = 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 to 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)) - { - 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 = 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 approxmiate number of positions tried and choosen arbitrarily - set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward - continue; - } - if (add_to_detector) detector_.insert(box); - last_x = x; - last_y = y; - return true; - } -} - - -template -box2d markers_placement::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; -} - -template -void markers_placement::set_spacing_left(double sl, bool allow_negative) -{ - 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; -} - -typedef agg::conv_clip_polyline clipped_geometry_type; -typedef coord_transform path_type; -typedef coord_transform clipped_path_type; -typedef agg::conv_transform transformed_path_type; - -template class markers_placement; -template class markers_placement; -template class markers_placement; -template class markers_placement; -template class markers_placement; -template class markers_placement, label_collision_detector4>; -template class markers_placement, label_collision_detector4>; -template class markers_placement, label_collision_detector4>; -template class markers_placement, label_collision_detector4>; -template class markers_placement, label_collision_detector4>; -} //ns mapnik diff --git a/src/markers_symbolizer.cpp b/src/markers_symbolizer.cpp index 5ea1c4624..643c7d04b 100644 --- a/src/markers_symbolizer.cpp +++ b/src/markers_symbolizer.cpp @@ -45,10 +45,7 @@ markers_symbolizer::markers_symbolizer() allow_overlap_(false), spacing_(100.0), max_error_(0.2), - marker_p_(MARKER_POINT_PLACEMENT) { - // override the default for clipping in symbolizer base - this->set_clip(false); - } + marker_p_(MARKER_POINT_PLACEMENT) { } markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename) : symbolizer_with_image(filename), @@ -59,10 +56,7 @@ markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename) allow_overlap_(false), spacing_(100.0), max_error_(0.2), - marker_p_(MARKER_POINT_PLACEMENT) { - // override the default for clipping in symbolizer base - this->set_clip(false); - } + marker_p_(MARKER_POINT_PLACEMENT) { } markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs) : symbolizer_with_image(rhs), From b7aafe51bc2b7b2f3f1590ab0110c6354198a5fe Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 17:53:20 -0700 Subject: [PATCH 26/50] update markers line placement tests --- tests/data/good_maps/markers_symbolizer_lines.xml | 9 ++++++++- .../data/good_maps/markers_symbolizer_lines_file.xml | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/data/good_maps/markers_symbolizer_lines.xml b/tests/data/good_maps/markers_symbolizer_lines.xml index 74adff287..e1f879ad9 100644 --- a/tests/data/good_maps/markers_symbolizer_lines.xml +++ b/tests/data/good_maps/markers_symbolizer_lines.xml @@ -3,7 +3,14 @@ diff --git a/tests/data/good_maps/markers_symbolizer_lines_file.xml b/tests/data/good_maps/markers_symbolizer_lines_file.xml index 39d2f1dbd..e08621485 100644 --- a/tests/data/good_maps/markers_symbolizer_lines_file.xml +++ b/tests/data/good_maps/markers_symbolizer_lines_file.xml @@ -2,8 +2,15 @@ From 3821afd74d72430a2ca8da8f78173e30b0dd2d97 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 18:06:52 -0700 Subject: [PATCH 27/50] inherit default placement types from class instance --- src/load_map.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/load_map.cpp b/src/load_map.cpp index 315e85e9a..2f4d9c8e2 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -889,7 +889,7 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym) symbol.set_ignore_placement(* ignore_placement); } point_placement_e placement = - sym.get_attr("placement", CENTROID_POINT_PLACEMENT); + sym.get_attr("placement", sym.get_point_placement()); symbol.set_point_placement(placement); if (file && !file->empty()) @@ -1064,7 +1064,7 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node) sym.set_stroke(strk); } - marker_placement_e placement = node.get_attr("placement", MARKER_POINT_PLACEMENT); + marker_placement_e placement = node.get_attr("placement",sym.get_marker_placement()); sym.set_marker_placement(placement); parse_symbolizer_base(sym, node); rule.append(sym); From 16084ff335b004114996535f2e0a04b12cd91ffb Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 18:13:50 -0700 Subject: [PATCH 28/50] support both interior placement and point for markers - helps prepare for #952 --- bindings/python/mapnik_markers_symbolizer.cpp | 1 + include/mapnik/markers_symbolizer.hpp | 1 + src/agg/process_markers_symbolizer.cpp | 22 +++++++++++++++---- src/load_map.cpp | 2 +- src/markers_symbolizer.cpp | 1 + 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/bindings/python/mapnik_markers_symbolizer.cpp b/bindings/python/mapnik_markers_symbolizer.cpp index 9613e7476..2ab881c05 100644 --- a/bindings/python/mapnik_markers_symbolizer.cpp +++ b/bindings/python/mapnik_markers_symbolizer.cpp @@ -92,6 +92,7 @@ void export_markers_symbolizer() mapnik::enumeration_("marker_placement") .value("POINT_PLACEMENT",mapnik::MARKER_POINT_PLACEMENT) + .value("INTERIOR_PLACEMENT",mapnik::MARKER_INTERIOR_PLACEMENT) .value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT) ; diff --git a/include/mapnik/markers_symbolizer.hpp b/include/mapnik/markers_symbolizer.hpp index d11cf6f8f..0a441a98d 100644 --- a/include/mapnik/markers_symbolizer.hpp +++ b/include/mapnik/markers_symbolizer.hpp @@ -39,6 +39,7 @@ namespace mapnik { // TODO - consider merging with text_symbolizer label_placement_e enum marker_placement_enum { MARKER_POINT_PLACEMENT, + MARKER_INTERIOR_PLACEMENT, MARKER_LINE_PLACEMENT, marker_placement_enum_MAX }; diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 79723f485..451323f5b 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -94,11 +94,18 @@ struct vector_markers_rasterizer_dispatch { marker_placement_e placement_method = sym_.get_marker_placement(); - if (placement_method == MARKER_POINT_PLACEMENT) + if (placement_method != MARKER_LINE_PLACEMENT) { double x,y; path.rewind(0); - label::interior_position(path, x, y); + if (placement_method == MARKER_INTERIOR_PLACEMENT) + { + label::interior_position(path, x, y); + } + else + { + label::centroid(path, x, y); + } agg::trans_affine matrix = marker_trans_; matrix.translate(x,y); box2d transformed_bbox = bbox_ * matrix; @@ -231,11 +238,18 @@ struct raster_markers_rasterizer_dispatch marker_placement_e placement_method = sym_.get_marker_placement(); box2d bbox_(0,0, src_.width(),src_.height()); - if (placement_method == MARKER_POINT_PLACEMENT) + if (placement_method != MARKER_LINE_PLACEMENT) { double x,y; path.rewind(0); - label::interior_position(path, x, y); + if (placement_method == MARKER_INTERIOR_PLACEMENT) + { + label::interior_position(path, x, y); + } + else + { + label::centroid(path, x, y); + } agg::trans_affine matrix = marker_trans_; matrix.translate(x,y); box2d transformed_bbox = bbox_ * matrix; diff --git a/src/load_map.cpp b/src/load_map.cpp index 2f4d9c8e2..f834e8202 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -889,7 +889,7 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym) symbol.set_ignore_placement(* ignore_placement); } point_placement_e placement = - sym.get_attr("placement", sym.get_point_placement()); + sym.get_attr("placement", symbol.get_point_placement()); symbol.set_point_placement(placement); if (file && !file->empty()) diff --git a/src/markers_symbolizer.cpp b/src/markers_symbolizer.cpp index 643c7d04b..9de55e492 100644 --- a/src/markers_symbolizer.cpp +++ b/src/markers_symbolizer.cpp @@ -30,6 +30,7 @@ namespace mapnik { static const char * marker_placement_strings[] = { "point", + "interior", "line", "" }; From a9f99848c238a4c0a4ef461b91abdca77d25fe87 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 18:40:53 -0700 Subject: [PATCH 29/50] expose boost::optional for markers_symbolizer (which works as oopposed to boost::optional and boost::optional which are broken --- bindings/python/mapnik_python.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 883946427..71231aa25 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -608,6 +608,7 @@ BOOST_PYTHON_MODULE(_mapnik) def("has_cairo", &has_cairo, "Get cairo library status"); def("has_pycairo", &has_pycairo, "Get pycairo module status"); + python_optional(); python_optional(); python_optional >(); python_optional(); From 295c661af2d95e31b256d8de49ef149ec16c23c5 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 31 Jul 2012 19:19:45 -0700 Subject: [PATCH 30/50] start setting up to sync markers_symbolizer between agg and grid renderer --- src/agg/process_markers_symbolizer.cpp | 6 ++-- src/grid/process_markers_symbolizer.cpp | 46 ++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 451323f5b..ff0d46b18 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -21,11 +21,12 @@ *****************************************************************************/ // mapnik -#include #include -#include #include #include + +#include +#include #include #include #include @@ -52,6 +53,7 @@ #include "agg_span_allocator.h" #include "agg_image_accessors.h" #include "agg_span_image_filter_rgba.h" + // boost #include diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index e80d185b6..9242d2027 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -20,22 +20,55 @@ * *****************************************************************************/ +/* + +porting notes --> + + - grid includes + - detector + - no gamma + - mapnik::pixfmt_gray32 + - agg::scanline_bin sl + - grid_rendering_buffer + - agg::renderer_scanline_bin_solid + - clamping: + // - clamp sizes to > 4 pixels of interactivity + if (tr.scale() < 0.5) + { + agg::trans_affine tr2; + tr2 *= agg::trans_affine_scaling(0.5); + tr = tr2; + } + tr *= agg::trans_affine_scaling(scale_factor_*(1.0/pixmap_.get_resolution())); + - svg_renderer.render_id + - only encode feature if placements are found: + if (placed) + { + pixmap_.add_feature(feature); + } + +*/ + // mapnik -#include #include #include #include #include #include + +#include +#include +#include +#include #include #include #include -#include -#include -#include #include +#include #include +#include #include +#include // agg #include "agg_basics.h" @@ -46,6 +79,11 @@ #include "agg_path_storage.h" #include "agg_conv_clip_polyline.h" #include "agg_conv_transform.h" +#include "agg_image_filters.h" +#include "agg_trans_bilinear.h" +#include "agg_span_allocator.h" +#include "agg_image_accessors.h" +#include "agg_span_image_filter_rgba.h" // boost #include From fc6a22c014bd04b417eff13747d33be2a92502a2 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 1 Aug 2012 11:07:45 +0100 Subject: [PATCH 31/50] + c++ style --- include/mapnik/attribute_collector.hpp | 6 ++--- src/feature_style_processor.cpp | 35 +++++++++++++------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/include/mapnik/attribute_collector.hpp b/include/mapnik/attribute_collector.hpp index f814d1ac5..81cdad888 100644 --- a/include/mapnik/attribute_collector.hpp +++ b/include/mapnik/attribute_collector.hpp @@ -249,7 +249,7 @@ public: struct directive_collector : public boost::static_visitor<> { - directive_collector(double * filter_factor) + directive_collector(double & filter_factor) : filter_factor_(filter_factor) {} template @@ -257,10 +257,10 @@ struct directive_collector : public boost::static_visitor<> void operator () (raster_symbolizer const& sym) { - *filter_factor_ = sym.calculate_filter_factor(); + filter_factor_ = sym.calculate_filter_factor(); } private: - double * filter_factor_; + double & filter_factor_; }; } // namespace mapnik diff --git a/src/feature_style_processor.cpp b/src/feature_style_processor.cpp index a8e0fc8cd..7bc161f6a 100644 --- a/src/feature_style_processor.cpp +++ b/src/feature_style_processor.cpp @@ -101,35 +101,35 @@ struct feature_style_processor::symbol_dispatch : public boost::stati : output_(output), f_(f), prj_trans_(prj_trans) {} - + template void operator () (T const& sym) const { process_impl::value>::process(output_,sym,f_,prj_trans_); } - + Processor & output_; mapnik::feature_impl & f_; proj_transform const& prj_trans_; }; -typedef char (&no_tag)[1]; -typedef char (&yes_tag)[2]; +typedef char (&no_tag)[1]; +typedef char (&yes_tag)[2]; template -struct process_memfun_helper {}; - -template no_tag has_process_helper(...); +struct process_memfun_helper {}; + +template no_tag has_process_helper(...); template yes_tag has_process_helper(process_memfun_helper* p); - -template + +template struct has_process -{ +{ typedef typename T0::processor_impl_type processor_impl_type; - BOOST_STATIC_CONSTANT(bool - , value = sizeof(has_process_helper(0)) == sizeof(yes_tag) - ); -}; + BOOST_STATIC_CONSTANT(bool + , value = sizeof(has_process_helper(0)) == sizeof(yes_tag) + ); +}; template @@ -350,8 +350,8 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces query q(layer_ext,res,scale_denom,m_.get_current_extent()); std::vector active_styles; attribute_collector collector(names); - double filt_factor = 1; - directive_collector d_collector(&filt_factor); + double filt_factor = 1.0; + directive_collector d_collector(filt_factor); // iterate through all named styles collecting active styles and attribute names BOOST_FOREACH(std::string const& style_name, style_names) @@ -519,7 +519,7 @@ void feature_style_processor::render_style( { p.start_style_processing(*style); - + #if defined(RENDERING_STATS) std::ostringstream s1; s1 << "rendering style for layer: '" << lay.name() @@ -661,4 +661,3 @@ template class feature_style_processor >; template class feature_style_processor >; } - From 5541ea027138c4d8f6d469152df7c58d0146f100 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 1 Aug 2012 15:44:36 +0100 Subject: [PATCH 32/50] + layer : minimum-extent and buffer-size + agg::process_xxx : remove ad-hoc query_extent modifiers + ctrans : simplify vertex skipping --- bindings/python/mapnik_layer.cpp | 33 +++++++++++ bindings/python/mapnik_map.cpp | 7 +-- include/mapnik/ctrans.hpp | 25 +++------ include/mapnik/layer.hpp | 13 ++++- include/mapnik/map.hpp | 6 +- src/agg/agg_renderer.cpp | 17 ++++++ src/agg/process_line_pattern_symbolizer.cpp | 20 +++---- src/agg/process_line_symbolizer.cpp | 2 +- src/agg/process_markers_symbolizer.cpp | 6 +- .../process_polygon_pattern_symbolizer.cpp | 3 +- src/agg/process_polygon_symbolizer.cpp | 6 +- src/feature_style_processor.cpp | 4 +- src/layer.cpp | 34 +++++++++++- src/load_map.cpp | 55 ++++++++++++++----- src/map.cpp | 6 +- src/save_map.cpp | 18 +++++- 16 files changed, 187 insertions(+), 68 deletions(-) diff --git a/bindings/python/mapnik_layer.cpp b/bindings/python/mapnik_layer.cpp index 0b2e8d63b..ae99e5971 100644 --- a/bindings/python/mapnik_layer.cpp +++ b/bindings/python/mapnik_layer.cpp @@ -91,6 +91,18 @@ struct layer_pickle_suite : boost::python::pickle_suite std::vector & (mapnik::layer::*_styles_)() = &mapnik::layer::styles; +void set_maximum_extent(mapnik::layer & l, boost::optional > const& box) +{ + if (box) + { + l.set_maximum_extent(*box); + } + else + { + l.reset_maximum_extent(); + } +} + void export_layer() { using namespace boost::python; @@ -196,6 +208,27 @@ void export_layer() "\n" ) + .add_property("buffer_size", + &layer::buffer_size, + &layer::set_buffer_size, + "Get/Set the size of buffer around layer in pixels.\n" + "\n" + "Usage:\n" + ">>> l.buffer_size\n" + "0 # zero by default\n" + ">>> l.buffer_size = 2\n" + ">>> l.buffer_size\n" + "2\n" + ) + .add_property("maximum_extent",make_function + (&layer::maximum_extent,return_value_policy()), + &set_maximum_extent, + "The maximum extent of the map.\n" + "\n" + "Usage:\n" + ">>> m.maximum_extent = Box2d(-180,-90,180,90)\n" + ) + .add_property("maxzoom", &layer::max_zoom, &layer::set_max_zoom, diff --git a/bindings/python/mapnik_map.cpp b/bindings/python/mapnik_map.cpp index 3bc1ca983..e2a21e674 100644 --- a/bindings/python/mapnik_map.cpp +++ b/bindings/python/mapnik_map.cpp @@ -117,7 +117,7 @@ struct map_pickle_suite : boost::python::pickle_suite std::vector& (Map::*layers_nonconst)() = &Map::layers; std::vector const& (Map::*layers_const)() const = &Map::layers; mapnik::parameters& (Map::*params_nonconst)() = &Map::get_extra_parameters; -boost::optional > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent; +//boost::optional > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent; mapnik::feature_type_style find_style(mapnik::Map const& m, std::string const& name) { @@ -192,7 +192,6 @@ mapnik::Map map_deepcopy(mapnik::Map & m, boost::python::dict memo) return result; } -// TODO - find a simplier way to set optional to uninitialized void set_maximum_extent(mapnik::Map & m, boost::optional > const& box) { if (box) @@ -201,7 +200,7 @@ void set_maximum_extent(mapnik::Map & m, boost::optional > } else { - m.maximum_extent().reset(); + m.reset_maximum_extent(); } } @@ -562,7 +561,7 @@ void export_map() ) .add_property("maximum_extent",make_function - (maximum_extent_const,return_value_policy()), + (&Map::maximum_extent,return_value_policy()), &set_maximum_extent, "The maximum extent of the map.\n" "\n" diff --git a/include/mapnik/ctrans.hpp b/include/mapnik/ctrans.hpp index a06872422..a56bc4bc4 100644 --- a/include/mapnik/ctrans.hpp +++ b/include/mapnik/ctrans.hpp @@ -52,34 +52,25 @@ struct MAPNIK_DECL coord_transform : t_(0), geom_(geom), prj_trans_(0) {} - + void set_proj_trans(proj_transform const& prj_trans) { prj_trans_ = &prj_trans; } - + void set_trans(Transform const& t) { t_ = &t; } - + unsigned vertex(double *x, double *y) const { - unsigned command = SEG_MOVETO; - bool ok = false; - bool skipped_points = false; - double z = 0; - while (!ok && command != SEG_END) + unsigned command = geom_.vertex(x, y); + if ( command != SEG_END) { - command = geom_.vertex(x, y); - ok = prj_trans_->backward(*x, *y, z); - if (!ok) { - skipped_points = true; - } - } - if (skipped_points && (command == SEG_LINETO)) - { - command = SEG_MOVETO; + double z = 0; + if (!prj_trans_->backward(*x, *y, z)) + return SEG_END; } t_->forward(x, y); return command; diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index ab0eb4a8e..1be1b23c0 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -43,7 +43,9 @@ namespace mapnik class MAPNIK_DECL layer { public: - explicit layer(std::string const& name, std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); + layer(std::string const& name, + std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); + layer(layer const& l); layer& operator=(layer const& l); bool operator==(layer const& other) const; @@ -57,7 +59,7 @@ public: * @return the name of the layer. */ - const std::string& name() const; + std::string const& name() const; /*! * @brief Set the SRS of the layer. @@ -188,6 +190,11 @@ public: */ box2d envelope() const; + void set_maximum_extent(box2d const& box); + boost::optional > const& maximum_extent() const; + void reset_maximum_extent(); + void set_buffer_size(int size); + int buffer_size() const; ~layer(); private: void swap(const layer& other); @@ -204,6 +211,8 @@ private: std::string group_by_; std::vector styles_; datasource_ptr ds_; + int buffer_size_; + boost::optional > maximum_extent_; }; } diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index bfbf87edd..400f081ac 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -331,15 +331,13 @@ public: /*! \brief Set the map maximum extent. * @param box The bounding box for the maximum extent. */ - void set_maximum_extent(box2dconst& box); + void set_maximum_extent(box2d const& box); /*! \brief Get the map maximum extent as box2d */ boost::optional > const& maximum_extent() const; - /*! \brief Get the non-const map maximum extent as box2d - */ - boost::optional > & maximum_extent(); + void reset_maximum_extent(); /*! \brief Get the map base path where paths should be relative to. */ diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index 92cda8973..77ae1e752 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -191,7 +191,24 @@ void agg_renderer::start_layer_processing(layer const& lay, box2d con { detector_->clear(); } + query_extent_ = query_extent; + int buffer_size = lay.buffer_size(); + if (buffer_size != 0 ) + { + double padding = buffer_size * (double)(query_extent.width()/pixmap_.width()); + double x0 = query_extent_.minx(); + double y0 = query_extent_.miny(); + double x1 = query_extent_.maxx(); + double y1 = query_extent_.maxy(); + query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); + } + + boost::optional > const& maximum_extent = lay.maximum_extent(); + if (maximum_extent) + { + query_extent_.clip(*maximum_extent); + } } template diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index 5f79a7755..e8c50209b 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -56,7 +56,7 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, { typedef agg::rgba8 color; typedef agg::order_rgba order; - typedef agg::pixel32_type pixel_type; + typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba_pre blender_type; typedef agg::pattern_filter_bilinear_rgba8 pattern_filter_type; typedef agg::line_image_pattern pattern_type; @@ -64,7 +64,7 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, typedef agg::renderer_base renderer_base; typedef agg::renderer_outline_image renderer_type; typedef agg::rasterizer_outline_aa rasterizer_type; - + std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature); boost::optional mark = marker_cache::instance()->find(filename,true); @@ -81,8 +81,6 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, if (!pat) return; - box2d ext = query_extent_ * 1.0; - agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4); pixfmt_type pixf(buf); pixf.comp_op(static_cast(sym.comp_op())); @@ -91,26 +89,26 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, pattern_source source(*(*pat)); pattern_type pattern (filter,source); - renderer_type ren(ren_base, pattern); + renderer_type ren(ren_base, pattern); rasterizer_type ras(ren); - + agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); typedef boost::mpl::vector conv_types; vertex_converter, rasterizer_type, line_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(ext,ras,sym,t_,prj_trans,tr,scale_factor_); - + converter(query_extent_,ras,sym,t_,prj_trans,tr,scale_factor_); + if (sym.clip()) converter.set(); //optional clip (default: true) - converter.set(); //always transform + converter.set(); //always transform if (sym.smooth() > 0.0) converter.set(); // optional smooth converter - + BOOST_FOREACH(geometry_type & geom, feature.paths()) { if (geom.size() > 1) { - converter.apply(geom); + converter.apply(geom); } } } diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index ec75035b4..bc02c8739 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -80,7 +80,7 @@ void agg_renderer::process(line_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - box2d ext = query_extent_ * 1.1; + box2d ext = query_extent_ * 1.0; if (sym.get_rasterizer() == RASTERIZER_FAST) { diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 451323f5b..9a32f0424 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -363,7 +363,7 @@ void agg_renderer::process(markers_symbolizer const& sym, bbox, marker_trans, sym, *detector_, scale_factor_); vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(query_extent_* 1.1,rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { eGeomType type = feature.paths()[0].type(); @@ -396,7 +396,7 @@ void agg_renderer::process(markers_symbolizer const& sym, bbox, marker_trans, sym, *detector_, scale_factor_); vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(query_extent_* 1.1,rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { eGeomType type = feature.paths()[0].type(); @@ -427,7 +427,7 @@ void agg_renderer::process(markers_symbolizer const& sym, marker_trans, sym, *detector_, scale_factor_); vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(query_extent_* 1.1, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { diff --git a/src/agg/process_polygon_pattern_symbolizer.cpp b/src/agg/process_polygon_pattern_symbolizer.cpp index 2a5ec53d3..a8b5a7c9f 100644 --- a/src/agg/process_polygon_pattern_symbolizer.cpp +++ b/src/agg/process_polygon_pattern_symbolizer.cpp @@ -139,11 +139,10 @@ void agg_renderer::process(polygon_pattern_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - box2d inflated_extent = query_extent_ * 1.0; typedef boost::mpl::vector conv_types; vertex_converter, rasterizer, polygon_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform diff --git a/src/agg/process_polygon_symbolizer.cpp b/src/agg/process_polygon_symbolizer.cpp index 2b1266659..323f987ef 100644 --- a/src/agg/process_polygon_symbolizer.cpp +++ b/src/agg/process_polygon_symbolizer.cpp @@ -48,15 +48,13 @@ void agg_renderer::process(polygon_symbolizer const& sym, ras_ptr->reset(); set_gamma_method(sym,ras_ptr); - box2d inflated_extent = query_extent_ * 1.0; - agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); typedef boost::mpl::vector conv_types; vertex_converter, rasterizer, polygon_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform @@ -72,7 +70,7 @@ void agg_renderer::process(polygon_symbolizer const& sym, } agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4); - + color const& fill = sym.get_fill(); unsigned r=fill.red(); unsigned g=fill.green(); diff --git a/src/feature_style_processor.cpp b/src/feature_style_processor.cpp index 7bc161f6a..d22d8caf1 100644 --- a/src/feature_style_processor.cpp +++ b/src/feature_style_processor.cpp @@ -320,9 +320,11 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces // if we've got this far, now prepare the unbuffered extent // which is used as a bbox for clipping geometries box2d query_ext = m_.get_current_extent(); // unbuffered - if (maximum_extent) { + if (maximum_extent) + { query_ext.clip(*maximum_extent); } + box2d layer_ext2 = lay.envelope(); if (fw_success) { diff --git a/src/layer.cpp b/src/layer.cpp index 985eb5bad..09fbb0eb6 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -42,7 +42,8 @@ layer::layer(std::string const& name, std::string const& srs) clear_label_cache_(false), cache_features_(false), group_by_(""), - ds_() {} + ds_(), + buffer_size_(0) {} layer::layer(const layer& rhs) : name_(rhs.name_), @@ -55,7 +56,9 @@ layer::layer(const layer& rhs) cache_features_(rhs.cache_features_), group_by_(rhs.group_by_), styles_(rhs.styles_), - ds_(rhs.ds_) {} + ds_(rhs.ds_), + buffer_size_(rhs.buffer_size_), + maximum_extent_(rhs.maximum_extent_) {} layer& layer::operator=(const layer& rhs) { @@ -82,6 +85,8 @@ void layer::swap(const layer& rhs) group_by_ = rhs.group_by_; styles_=rhs.styles_; ds_=rhs.ds_; + buffer_size_ = rhs.buffer_size_; + maximum_extent_ = rhs.maximum_extent_; } layer::~layer() {} @@ -176,6 +181,31 @@ void layer::set_datasource(datasource_ptr const& ds) ds_ = ds; } +void layer::set_maximum_extent(box2d const& box) +{ + maximum_extent_.reset(box); +} + +boost::optional > const& layer::maximum_extent() const +{ + return maximum_extent_; +} + +void layer::reset_maximum_extent() +{ + maximum_extent_.reset(); +} + +void layer::set_buffer_size(int size) +{ + buffer_size_ = size; +} + +int layer::buffer_size() const +{ + return buffer_size_; +} + box2d layer::envelope() const { if (ds_) return ds_->envelope(); diff --git a/src/load_map.cpp b/src/load_map.cpp index f834e8202..9c0cebd93 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -574,66 +574,95 @@ bool map_parser::parse_font(font_set &fset, xml_node const& f) return false; } -void map_parser::parse_layer(Map & map, xml_node const& lay) +void map_parser::parse_layer(Map & map, xml_node const& node) { std::string name; try { - name = lay.get_attr("name", std::string("Unnamed")); + name = node.get_attr("name", std::string("Unnamed")); // XXX if no projection is given inherit from map? [DS] - std::string srs = lay.get_attr("srs", map.srs()); + std::string srs = node.get_attr("srs", map.srs()); layer lyr(name, srs); - optional status = lay.get_opt_attr("status"); + optional status = node.get_opt_attr("status"); if (status) { lyr.set_active(* status); } - optional min_zoom = lay.get_opt_attr("minzoom"); + optional min_zoom = node.get_opt_attr("minzoom"); if (min_zoom) { lyr.set_min_zoom(* min_zoom); } - optional max_zoom = lay.get_opt_attr("maxzoom"); + optional max_zoom = node.get_opt_attr("maxzoom"); if (max_zoom) { lyr.set_max_zoom(* max_zoom); } - optional queryable = lay.get_opt_attr("queryable"); + optional queryable = node.get_opt_attr("queryable"); if (queryable) { lyr.set_queryable(* queryable); } optional clear_cache = - lay.get_opt_attr("clear-label-cache"); + node.get_opt_attr("clear-label-cache"); if (clear_cache) { lyr.set_clear_label_cache(* clear_cache); } optional cache_features = - lay.get_opt_attr("cache-features"); + node.get_opt_attr("cache-features"); if (cache_features) { lyr.set_cache_features(* cache_features); } optional group_by = - lay.get_opt_attr("group-by"); + node.get_opt_attr("group-by"); if (group_by) { lyr.set_group_by(* group_by); } - xml_node::const_iterator child = lay.begin(); - xml_node::const_iterator end = lay.end(); + optional buffer_size = node.get_opt_attr("buffer-size"); + if (buffer_size) + { + lyr.set_buffer_size(*buffer_size); + } + + optional maximum_extent = node.get_opt_attr("maximum-extent"); + if (maximum_extent) + { + box2d box; + if (box.from_string(*maximum_extent)) + { + lyr.set_maximum_extent(box); + } + else + { + std::ostringstream s_err; + s_err << "failed to parse 'maximum-extent' in layer " << name; + if (strict_) + { + throw config_error(s_err.str()); + } + else + { + MAPNIK_LOG_WARN(load_map) << "map_parser: " << s_err.str(); + } + } + } + + xml_node::const_iterator child = node.begin(); + xml_node::const_iterator end = node.end(); for(; child != end; ++child) { @@ -718,7 +747,7 @@ void map_parser::parse_layer(Map & map, xml_node const& lay) { if (!name.empty()) { - ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", lay); + ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", node); } throw; } diff --git a/src/map.cpp b/src/map.cpp index 7a9d1f14b..c94509df7 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -352,7 +352,7 @@ void Map::set_background_image(std::string const& image_filename) void Map::set_maximum_extent(box2d const& box) { - maximum_extent_ = box; + maximum_extent_.reset(box); } boost::optional > const& Map::maximum_extent() const @@ -360,9 +360,9 @@ boost::optional > const& Map::maximum_extent() const return maximum_extent_; } -boost::optional > & Map::maximum_extent() +void Map::reset_maximum_extent() { - return maximum_extent_; + maximum_extent_.reset(); } std::string const& Map::base_path() const diff --git a/src/save_map.cpp b/src/save_map.cpp index 52d0482ec..fef378e79 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -741,6 +741,22 @@ void serialize_layer( ptree & map_node, const layer & layer, bool explicit_defau set_attr( layer_node, "group-by", layer.group_by() ); } + int buffer_size = layer.buffer_size(); + if ( buffer_size || explicit_defaults) + { + set_attr( layer_node, "buffer-size", buffer_size ); + } + + optional > const& maximum_extent = layer.maximum_extent(); + if ( maximum_extent) + { + std::ostringstream s; + s << std::setprecision(16) + << maximum_extent->minx() << "," << maximum_extent->miny() << "," + << maximum_extent->maxx() << "," << maximum_extent->maxy(); + set_attr( layer_node, "maximum-extent", s.str() ); + } + std::vector const& style_names = layer.styles(); for (unsigned i = 0; i < style_names.size(); ++i) { @@ -788,7 +804,7 @@ void serialize_map(ptree & pt, Map const & map, bool explicit_defaults) set_attr( map_node, "background-image", *image_filename ); } - unsigned buffer_size = map.buffer_size(); + int buffer_size = map.buffer_size(); if ( buffer_size || explicit_defaults) { set_attr( map_node, "buffer-size", buffer_size ); From 6c173cd9d457dfa1a9c03df1eda715d5b0bd1238 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 1 Aug 2012 16:30:11 +0100 Subject: [PATCH 33/50] + cleanup --- src/agg/process_line_symbolizer.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index bc02c8739..28ec848f7 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -80,8 +80,6 @@ void agg_renderer::process(line_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); - box2d ext = query_extent_ * 1.0; - if (sym.get_rasterizer() == RASTERIZER_FAST) { typedef agg::renderer_outline_aa renderer_type; @@ -99,7 +97,7 @@ void agg_renderer::process(line_symbolizer const& sym, smooth_tag, dash_tag, stroke_tag> conv_types; vertex_converter, rasterizer_type, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(ext,ras,sym,t_,prj_trans,tr,scaled); + converter(query_extent_,ras,sym,t_,prj_trans,tr,scaled); if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { @@ -133,7 +131,7 @@ void agg_renderer::process(line_symbolizer const& sym, vertex_converter, rasterizer, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(ext,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); + converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { From 3db39f2b7849fbacee988f9f0efbf568b3723714 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 1 Aug 2012 17:06:35 +0100 Subject: [PATCH 34/50] + revert using clip_poly_line to avoid rendering artifacts (TODO: consider modifying agg::conv_clip_polyline) --- src/agg/process_line_symbolizer.cpp | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index 28ec848f7..36d2ae83e 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -92,23 +92,13 @@ void agg_renderer::process(line_symbolizer const& sym, rasterizer_type ras(ren); set_join_caps_aa(stroke_,ras); - typedef boost::mpl::vector conv_types; vertex_converter, rasterizer_type, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,ras,sym,t_,prj_trans,tr,scaled); - - if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) - { - eGeomType type = feature.paths()[0].type(); - if (type == Polygon) - converter.set(); - else if (type == LineString) - converter.set(); - // don't clip if type==Point - } - + if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform @@ -126,23 +116,14 @@ void agg_renderer::process(line_symbolizer const& sym, } else { - typedef boost::mpl::vector conv_types; vertex_converter, rasterizer, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); - if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) - { - eGeomType type = feature.paths()[0].type(); - if (type == Polygon) - converter.set(); - else if (type == LineString) - converter.set(); - // don't clip if type==Point - } - + if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform From 8ff71a24bd6c473f5d63824212d552c3be8c789a Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 1 Aug 2012 17:29:22 +0100 Subject: [PATCH 35/50] + fixed naming (as per SVG) --- src/save_map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/save_map.cpp b/src/save_map.cpp index fef378e79..3138ca040 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -467,7 +467,7 @@ private: } if ( strk.dash_offset() != dfl.dash_offset() || explicit_defaults_ ) { - set_attr( node, "stroke-dash-offset", strk.dash_offset()); + set_attr( node, "stroke-dashoffset", strk.dash_offset()); } if ( ! strk.get_dash_array().empty() ) { From 6f15731c37db5579cfacd1cc11e3c616e47ad359 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 1 Aug 2012 16:40:06 -0700 Subject: [PATCH 36/50] use std::fixed when outputting bbox's to string --- include/mapnik/agg_renderer.hpp | 1 + include/mapnik/box2d.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index c82f57239..15ca8c2d5 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -123,6 +123,7 @@ protected: double x, double y, double angle = 0.0); void debug_draw_box(box2d const& extent, double x, double y, double angle = 0.0); + void draw_geo_extent(box2d const& extent,mapnik::color const& color); private: buffer_type & pixmap_; diff --git a/include/mapnik/box2d.hpp b/include/mapnik/box2d.hpp index fa52573fb..9f6bdb599 100644 --- a/include/mapnik/box2d.hpp +++ b/include/mapnik/box2d.hpp @@ -109,7 +109,7 @@ operator << (std::basic_ostream& out, std::basic_ostringstream s; s.copyfmt(out); s.width(0); - s << "box2d(" << std::setprecision(16) + s << "box2d(" << std::fixed << std::setprecision(16) << e.minx() << ',' << e.miny() << ',' << e.maxx() << ',' << e.maxy() << ')'; out << s.str(); From 092a1bf625f5da3fe902a24c30f553c4a1c5a56b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 1 Aug 2012 16:40:48 -0700 Subject: [PATCH 37/50] impl debug box drawing in geo extents --- src/agg/agg_renderer.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index 77ae1e752..a3af6c492 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -444,6 +444,27 @@ void agg_renderer::debug_draw_box(R& buf, box2d const& box, agg::render_scanlines(*ras_ptr, sl_line, ren); } +template +void agg_renderer::draw_geo_extent(box2d const& extent, mapnik::color const& color) +{ + box2d box = t_.forward(extent); + double x0 = box.minx(); + double x1 = box.maxx(); + double y0 = box.miny(); + double y1 = box.maxy(); + unsigned rgba = color.rgba(); + for (double x=x0; x; template void agg_renderer::debug_draw_box( agg::rendering_buffer& buf, From 72114363135ec83b37089f881c8e79e67d104df0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 1 Aug 2012 16:43:00 -0700 Subject: [PATCH 38/50] avoid clipping artifacts with line_symbolizer by using stroke sensitive clipping extent - refs #1282, #1185, #1215 --- src/agg/process_line_symbolizer.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index 36d2ae83e..776f0870e 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -47,7 +47,6 @@ namespace mapnik { - template void agg_renderer::process(line_symbolizer const& sym, mapnik::feature_impl & feature, @@ -80,6 +79,23 @@ void agg_renderer::process(line_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); + box2d clipping_extent = query_extent_; + if (sym.clip()) + { + double padding = (double)(query_extent_.width()/pixmap_.width()); + float half_stroke = stroke_.get_width()/2.0; + if (half_stroke > 1) + padding *= half_stroke; + double x0 = query_extent_.minx(); + double y0 = query_extent_.miny(); + double x1 = query_extent_.maxx(); + double y1 = query_extent_.maxy(); + clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); + // debugging + //box2d inverse(x0 + padding, y0 + padding, x1 - padding , y1 - padding); + //draw_geo_extent(inverse,mapnik::color("red")); + } + if (sym.get_rasterizer() == RASTERIZER_FAST) { typedef agg::renderer_outline_aa renderer_type; @@ -97,7 +113,7 @@ void agg_renderer::process(line_symbolizer const& sym, smooth_tag, dash_tag, stroke_tag> conv_types; vertex_converter, rasterizer_type, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(query_extent_,ras,sym,t_,prj_trans,tr,scaled); + converter(clipping_extent,ras,sym,t_,prj_trans,tr,scaled); if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform if (fabs(sym.offset()) > 0.0) converter.set(); // parallel offset @@ -121,7 +137,7 @@ void agg_renderer::process(line_symbolizer const& sym, vertex_converter, rasterizer, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); + converter(clipping_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform From a22b31b0cc431196516fecd108b29bad36e797df Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 1 Aug 2012 16:43:47 -0700 Subject: [PATCH 39/50] switch out new test image since there is no visual difference to the old one --- .../mapnik-wgs842merc-reprojection-render.png | Bin 48060 -> 48065 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/python_tests/images/support/mapnik-wgs842merc-reprojection-render.png b/tests/python_tests/images/support/mapnik-wgs842merc-reprojection-render.png index b5f8a7c1b2b9914e8dae1733ee88f899117705e5..029212655128b6a28959f4f5e8de9abc5c01aff8 100644 GIT binary patch delta 18929 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 From 56e11423efc230575b145fe884c35d42fe3bef00 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 2 Aug 2012 07:26:55 -0700 Subject: [PATCH 40/50] inflate clipping extent for line patterns - refs #1282 --- src/agg/process_line_pattern_symbolizer.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index e8c50209b..66dc01ca2 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -95,10 +95,24 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); + box2d clipping_extent = query_extent_; + if (sym.clip()) + { + double padding = (double)(query_extent_.width()/pixmap_.width()); + float half_stroke = (*mark)->width()/2.0; + if (half_stroke > 1) + padding *= half_stroke; + double x0 = query_extent_.minx(); + double y0 = query_extent_.miny(); + double x1 = query_extent_.maxx(); + double y1 = query_extent_.maxy(); + clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); + } + typedef boost::mpl::vector conv_types; vertex_converter, rasterizer_type, line_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> - converter(query_extent_,ras,sym,t_,prj_trans,tr,scale_factor_); + converter(clipping_extent,ras,sym,t_,prj_trans,tr,scale_factor_); if (sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform From c2c006f9c20cdb09c8a09692395d692e0ac4337d Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 2 Aug 2012 10:52:09 -0700 Subject: [PATCH 41/50] support setting opacity on bitmaps in render_marker - refs #1364 --- src/agg/agg_renderer.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index a3af6c492..8b8469656 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -379,15 +379,18 @@ void agg_renderer::render_marker(pixel_position const& pos, marker const& mar src.height(), src.width()*4); agg::pixfmt_rgba32_pre pixf(marker_buf); - typedef agg::image_accessor_clone img_accessor_type; typedef agg::span_interpolator_linear interpolator_type; typedef agg::span_image_filter_rgba_2x2 span_gen_type; + typedef agg::renderer_scanline_aa_alpha, + span_gen_type> renderer_type; img_accessor_type ia(pixf); interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) ); span_gen_type sg(ia, interpolator, filter); - agg::render_scanlines_aa(*ras_ptr, sl, renb, sa, sg); + renderer_type rp(renb,sa, sg, unsigned(opacity*255)); + agg::render_scanlines(*ras_ptr, sl, rp); } } } From 693cc881425af63b45a4234c85c23d10cafb85e7 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 2 Aug 2012 11:13:44 -0700 Subject: [PATCH 42/50] sync grid renderer with agg for point_symbolizer - refs #1309 --- src/agg/process_point_symbolizer.cpp | 18 +++++++----------- src/grid/process_point_symbolizer.cpp | 13 ++++++++++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/agg/process_point_symbolizer.cpp b/src/agg/process_point_symbolizer.cpp index aac4355a2..58e1fe9cd 100644 --- a/src/agg/process_point_symbolizer.cpp +++ b/src/agg/process_point_symbolizer.cpp @@ -23,19 +23,16 @@ // mapnik #include #include -#include #include -#include + +#include +#include +#include #include #include -#include // agg -#include "agg_basics.h" -#include "agg_rendering_buffer.h" -#include "agg_pixfmt_rgba.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_scanline_u.h" +#include "agg_trans_affine.h" // stl #include @@ -89,7 +86,6 @@ void agg_renderer::process(point_symbolizer const& sym, prj_trans.backward(x,y,z); t_.forward(&x,&y); label_ext.re_center(x,y); - if (sym.get_allow_overlap() || detector_->has_placement(label_ext)) { @@ -106,8 +102,8 @@ void agg_renderer::process(point_symbolizer const& sym, if (!sym.get_ignore_placement()) detector_->insert(label_ext); - metawriter_with_properties writer = sym.get_metawriter(); - if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second); + //metawriter_with_properties writer = sym.get_metawriter(); + //if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second); } } } diff --git a/src/grid/process_point_symbolizer.cpp b/src/grid/process_point_symbolizer.cpp index d1689f03b..79e895f67 100644 --- a/src/grid/process_point_symbolizer.cpp +++ b/src/grid/process_point_symbolizer.cpp @@ -26,15 +26,22 @@ #include #include #include + #include #include #include #include #include +// agg +#include "agg_trans_affine.h" + // stl #include +// boost +#include + namespace mapnik { template @@ -57,14 +64,14 @@ void grid_renderer::process(point_symbolizer const& sym, if (marker) { box2d const& bbox = (*marker)->bounding_box(); - coord2d const center = bbox.center(); + coord2d center = bbox.center(); agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_image_transform()); tr = agg::trans_affine_scaling(scale_factor_) * tr; - agg::trans_affine_translation const recenter(-center.x, -center.y); - agg::trans_affine const recenter_tr = recenter * tr; + agg::trans_affine_translation recenter(-center.x, -center.y); + agg::trans_affine recenter_tr = recenter * tr; box2d label_ext = bbox * recenter_tr; for (unsigned i=0; i Date: Thu, 2 Aug 2012 13:10:05 -0700 Subject: [PATCH 43/50] fix render_marker for grid renderer and other minor syncing - refs #1309 --- include/mapnik/grid/grid_renderer.hpp | 3 +- src/agg/agg_renderer.cpp | 9 +++-- src/grid/grid_renderer.cpp | 52 ++++++++++++++++++++----- src/grid/process_markers_symbolizer.cpp | 8 ++-- src/grid/process_point_symbolizer.cpp | 4 +- src/grid/process_shield_symbolizer.cpp | 2 +- src/grid/process_text_symbolizer.cpp | 2 +- tests/python_tests/render_grid_test.py | 12 +++--- 8 files changed, 64 insertions(+), 28 deletions(-) diff --git a/include/mapnik/grid/grid_renderer.hpp b/include/mapnik/grid/grid_renderer.hpp index a5a31245f..a26eedc12 100644 --- a/include/mapnik/grid/grid_renderer.hpp +++ b/include/mapnik/grid/grid_renderer.hpp @@ -117,9 +117,10 @@ private: CoordTransform t_; freetype_engine font_engine_; face_manager font_manager_; - label_collision_detector4 detector_; + boost::shared_ptr detector_; boost::scoped_ptr ras_ptr; box2d query_extent_; + void setup(Map const& m); }; } diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index 8b8469656..cd0c55e1e 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -21,12 +21,14 @@ *****************************************************************************/ // mapnik -#include -#include -#include #include #include #include +#include + +#include +#include +#include #include #include #include @@ -36,6 +38,7 @@ #include #include #include + #include #include #include diff --git a/src/grid/grid_renderer.cpp b/src/grid/grid_renderer.cpp index ab231e853..4e2b11663 100644 --- a/src/grid/grid_renderer.cpp +++ b/src/grid/grid_renderer.cpp @@ -21,14 +21,15 @@ *****************************************************************************/ // mapnik -#include -#include -#include #include #include #include #include #include + +#include +#include +#include #include #include #include @@ -56,13 +57,22 @@ grid_renderer::grid_renderer(Map const& m, T & pixmap, double scale_factor, u width_(pixmap_.width()), height_(pixmap_.height()), scale_factor_(scale_factor), + // NOTE: can change this to m dims instead of pixmap_ if render-time + // resolution support is dropped from grid_renderer python interface t_(pixmap_.width(),pixmap_.height(),m.get_current_extent(),offset_x,offset_y), font_engine_(), font_manager_(font_engine_), - detector_(box2d(-m.buffer_size(), -m.buffer_size(), pixmap_.width() + m.buffer_size(), pixmap_.height() + m.buffer_size())), + detector_(boost::make_shared(box2d(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))), ras_ptr(new grid_rasterizer) +{ + setup(m); +} + +template +void grid_renderer::setup(Map const& m) { MAPNIK_LOG_DEBUG(grid_renderer) << "grid_renderer: Scale=" << m.scale(); + // nothing to do for grids yet on setup } template @@ -91,9 +101,25 @@ void grid_renderer::start_layer_processing(layer const& lay, box2d co if (lay.clear_label_cache()) { - detector_.clear(); + detector_->clear(); } query_extent_ = query_extent; + int buffer_size = lay.buffer_size(); + if (buffer_size != 0 ) + { + double padding = buffer_size * (double)(query_extent.width()/pixmap_.width()); + double x0 = query_extent_.minx(); + double y0 = query_extent_.miny(); + double x1 = query_extent_.maxx(); + double y1 = query_extent_.maxy(); + query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); + } + + boost::optional > const& maximum_extent = lay.maximum_extent(); + if (maximum_extent) + { + query_extent_.clip(*maximum_extent); + } } template @@ -144,21 +170,27 @@ void grid_renderer::render_marker(mapnik::feature_impl & feature, unsigned in else { image_data_32 const& data = **marker.get_bitmap_data(); - if (step == 1 && scale_factor_ == 1.0) + double width = data.width(); + double height = data.height(); + double cx = 0.5 * width; + double cy = 0.5 * height; + if (step == 1 && (std::fabs(1.0 - scale_factor_) < 0.001 && tr.is_identity())) { + // TODO - support opacity pixmap_.set_rectangle(feature.id(), data, - boost::math::iround(pos.x), - boost::math::iround(pos.y)); + boost::math::iround(pos.x - cx), + boost::math::iround(pos.y - cy)); } else { + // TODO - remove support for step != or add support for agg scaling with opacity double ratio = (1.0/step); image_data_32 target(ratio * data.width(), ratio * data.height()); mapnik::scale_image_agg(target,data, SCALING_NEAR, scale_factor_, 0.0, 0.0, 1.0, ratio); pixmap_.set_rectangle(feature.id(), target, - boost::math::iround(pos.x), - boost::math::iround(pos.y)); + boost::math::iround(pos.x - cx), + boost::math::iround(pos.y - cy)); } } pixmap_.add_feature(feature); diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index 9242d2027..cef9cb478 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -175,12 +175,12 @@ void grid_renderer::process(markers_symbolizer const& sym, box2d transformed_bbox = bbox * matrix; if (sym.get_allow_overlap() || - detector_.has_placement(transformed_bbox)) + detector_->has_placement(transformed_bbox)) { placed = true; svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox); if (!sym.get_ignore_placement()) - detector_.insert(transformed_bbox); + detector_->insert(transformed_bbox); } } else if (sym.clip()) @@ -193,7 +193,7 @@ void grid_renderer::process(markers_symbolizer const& sym, clipped.clip_box(query_extent_.minx(),query_extent_.miny(),query_extent_.maxx(),query_extent_.maxy()); path_type path(t_,clipped,prj_trans); transformed_path_type path_transformed(path,geom_tr); - markers_placement placement(path_transformed, bbox, marker_trans, detector_, + markers_placement placement(path_transformed, bbox, marker_trans, *detector_, sym.get_spacing() * scale_factor_, sym.get_max_error(), sym.get_allow_overlap()); @@ -213,7 +213,7 @@ void grid_renderer::process(markers_symbolizer const& sym, typedef agg::conv_transform transformed_path_type; path_type path(t_,geom,prj_trans); transformed_path_type path_transformed(path,geom_tr); - markers_placement placement(path_transformed, bbox, marker_trans, detector_, + markers_placement placement(path_transformed, bbox, marker_trans, *detector_, sym.get_spacing() * scale_factor_, sym.get_max_error(), sym.get_allow_overlap()); diff --git a/src/grid/process_point_symbolizer.cpp b/src/grid/process_point_symbolizer.cpp index 79e895f67..eec6bf685 100644 --- a/src/grid/process_point_symbolizer.cpp +++ b/src/grid/process_point_symbolizer.cpp @@ -89,7 +89,7 @@ void grid_renderer::process(point_symbolizer const& sym, t_.forward(&x,&y); label_ext.re_center(x,y); if (sym.get_allow_overlap() || - detector_.has_placement(label_ext)) + detector_->has_placement(label_ext)) { render_marker(feature, @@ -101,7 +101,7 @@ void grid_renderer::process(point_symbolizer const& sym, sym.comp_op()); if (!sym.get_ignore_placement()) - detector_.insert(label_ext); + detector_->insert(label_ext); } } } diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp index e57266509..ac092061b 100644 --- a/src/grid/process_shield_symbolizer.cpp +++ b/src/grid/process_shield_symbolizer.cpp @@ -46,7 +46,7 @@ void grid_renderer::process(shield_symbolizer const& sym, sym, feature, prj_trans, width_, height_, scale_factor_, - t_, font_manager_, detector_, + t_, font_manager_, *detector_, query_extent_); bool placement_found = false; diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp index 22e6e7d18..28b709eed 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -36,7 +36,7 @@ void grid_renderer::process(text_symbolizer const& sym, sym, feature, prj_trans, width_, height_, scale_factor_ * (1.0/pixmap_.get_resolution()), - t_, font_manager_, detector_, + t_, font_manager_, *detector_, query_extent_); bool placement_found = false; diff --git a/tests/python_tests/render_grid_test.py b/tests/python_tests/render_grid_test.py index 596480ab4..2a9369946 100644 --- a/tests/python_tests/render_grid_test.py +++ b/tests/python_tests/render_grid_test.py @@ -108,7 +108,7 @@ def show_grids(name,g1,g2): val += '\n\t%s\n\t%s' % (g1_file,g2_file) return val -def test_render_grid(): +def test_render_grid_old(): """ test old method """ width,height = 256,256 m = create_grid_map(width,height) @@ -117,7 +117,7 @@ def test_render_grid(): lr_lonlat = mapnik.Coord(143.40,-38.80) m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) grid = mapnik.render_grid(m,0,key='Name',resolution=4,fields=['Name']) - eq_(grid,grid_correct_old2,show_grids('old',grid,grid_correct_old2)) + eq_(grid,grid_correct_old2,show_grids('old-markers',grid,grid_correct_old2)) eq_(resolve(grid,0,0),None) # check every pixel of the nw symbol @@ -128,7 +128,7 @@ def test_render_grid(): eq_(resolve(grid,23,10),expected) eq_(resolve(grid,23,11),expected) -def test_render_grid2(): +def test_render_grid_new(): """ test old against new""" width,height = 256,256 m = create_grid_map(width,height) @@ -140,7 +140,7 @@ def test_render_grid2(): grid = mapnik.Grid(m.width,m.height,key='Name') mapnik.render_layer(m,grid,layer=0,fields=['Name']) utf1 = grid.encode('utf',resolution=4) - eq_(utf1,grid_correct_new2,show_grids('new',utf1,grid_correct_new2)) + eq_(utf1,grid_correct_new2,show_grids('new-markers',utf1,grid_correct_new2)) # check a full view is the same as a full image grid_view = grid.view(0,0,width,height) @@ -177,7 +177,7 @@ def test_render_grid3(): grid = mapnik.Grid(m.width,m.height,key='__id__') mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name']) utf1 = grid.encode('utf',resolution=4) - eq_(utf1,grid_feat_id2,show_grids('id',utf1,grid_feat_id2)) + eq_(utf1,grid_feat_id2,show_grids('id-markers',utf1,grid_feat_id2)) # check a full view is the same as a full image grid_view = grid.view(0,0,width,height) # for kicks check at full res too @@ -282,7 +282,7 @@ def test_line_rendering(): eq_(utf1,line_expected,show_grids('line',utf1,line_expected)) #open('test.json','w').write(json.dumps(grid.encode())) -point_expected = {"keys": ["", "3", "4", "2", "1"], "data": {"1": {"Name": "South East"}, "3": {"Name": "North West"}, "2": {"Name": "South West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} +point_expected = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} def test_point_symbolizer_grid(): width,height = 256,256 From c1d124f6d993b392f4b79d7ba9deaadbb1927a92 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 2 Aug 2012 21:00:52 -0700 Subject: [PATCH 44/50] Account for offset value to avoid severe clipping with large offsets - refs #1282 --- src/agg/process_line_symbolizer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index 776f0870e..02263be95 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -86,6 +86,8 @@ void agg_renderer::process(line_symbolizer const& sym, float half_stroke = stroke_.get_width()/2.0; if (half_stroke > 1) padding *= half_stroke; + if (fabs(sym.offset()) > 0) + padding *= fabs(sym.offset()) * 1.2; double x0 = query_extent_.minx(); double y0 = query_extent_.miny(); double x1 = query_extent_.maxx(); From 8ea21d866d8c966c214ea9c602c84b6ba23d5806 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 2 Aug 2012 22:52:21 -0700 Subject: [PATCH 45/50] move more code to marker_helpers.hpp to set up for grid impl of new markers code - refs #1282 --- include/mapnik/marker_helpers.hpp | 254 +++++++++++++++++++++++++ src/agg/process_markers_symbolizer.cpp | 251 +----------------------- 2 files changed, 258 insertions(+), 247 deletions(-) diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index f22dd6ec5..2a11c07d6 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -28,16 +28,270 @@ #include #include #include +#include // for svg_storage_type #include +#include // agg #include "agg_ellipse.h" +#include "agg_basics.h" +#include "agg_renderer_base.h" +#include "agg_renderer_scanline.h" +#include "agg_rendering_buffer.h" +#include "agg_scanline_u.h" +#include "agg_image_filters.h" +#include "agg_trans_bilinear.h" +#include "agg_span_allocator.h" +#include "agg_image_accessors.h" +#include "agg_pixfmt_rgba.h" +#include "agg_span_image_filter_rgba.h" // boost #include namespace mapnik { + +template +struct vector_markers_rasterizer_dispatch +{ + typedef agg::rgba8 color_type; + typedef agg::order_rgba order_type; + typedef agg::pixel32_type pixel_type; + typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender + typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; + typedef agg::renderer_base renderer_base; + + vector_markers_rasterizer_dispatch(BufferType & image_buffer, + SvgRenderer & svg_renderer, + Rasterizer & ras, + box2d const& bbox, + agg::trans_affine const& marker_trans, + markers_symbolizer const& sym, + Detector & detector, + double scale_factor) + : buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4), + pixf_(buf_), + renb_(pixf_), + svg_renderer_(svg_renderer), + ras_(ras), + bbox_(bbox), + marker_trans_(marker_trans), + sym_(sym), + detector_(detector), + scale_factor_(scale_factor) + { + pixf_.comp_op(static_cast(sym_.comp_op())); + } + + template + void add_path(T & path) + { + marker_placement_e placement_method = sym_.get_marker_placement(); + + if (placement_method != MARKER_LINE_PLACEMENT) + { + double x,y; + path.rewind(0); + if (placement_method == MARKER_INTERIOR_PLACEMENT) + { + label::interior_position(path, x, y); + } + else + { + label::centroid(path, x, y); + } + agg::trans_affine matrix = marker_trans_; + matrix.translate(x,y); + box2d transformed_bbox = bbox_ * matrix; + + if (sym_.get_allow_overlap() || + detector_.has_placement(transformed_bbox)) + { + svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_); + + if (!sym_.get_ignore_placement()) + detector_.insert(transformed_bbox); + } + } + else + { + markers_placement placement(path, bbox_, marker_trans_, 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 = marker_trans_; + matrix.rotate(angle); + matrix.translate(x, y); + svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_); + } + } + } +private: + agg::scanline_u8 sl_; + agg::rendering_buffer buf_; + pixfmt_comp_type pixf_; + renderer_base renb_; + SvgRenderer & svg_renderer_; + Rasterizer & ras_; + box2d const& bbox_; + agg::trans_affine const& marker_trans_; + markers_symbolizer const& sym_; + Detector & detector_; + double scale_factor_; +}; + +template +void render_raster_marker(Rasterizer & ras, RendererBuffer & renb, + agg::scanline_u8 & sl, image_data_32 const& src, + agg::trans_affine const& marker_tr, double opacity) +{ + double width = src.width(); + double height = src.height(); + double p[8]; + p[0] = 0; p[1] = 0; + p[2] = width; p[3] = 0; + p[4] = width; p[5] = height; + p[6] = 0; p[7] = height; + + marker_tr.transform(&p[0], &p[1]); + marker_tr.transform(&p[2], &p[3]); + marker_tr.transform(&p[4], &p[5]); + marker_tr.transform(&p[6], &p[7]); + + ras.move_to_d(p[0],p[1]); + ras.line_to_d(p[2],p[3]); + ras.line_to_d(p[4],p[5]); + ras.line_to_d(p[6],p[7]); + + typedef agg::rgba8 color_type; + agg::span_allocator sa; + agg::image_filter_bilinear filter_kernel; + agg::image_filter_lut filter(filter_kernel, false); + + agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(), + src.width(), + src.height(), + src.width()*4); + agg::pixfmt_rgba32_pre pixf(marker_buf); + + typedef agg::image_accessor_clone img_accessor_type; + typedef agg::span_interpolator_linear interpolator_type; + typedef agg::span_image_filter_rgba_2x2 span_gen_type; + typedef agg::order_rgba order_type; + typedef agg::pixel32_type pixel_type; + typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender + typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; + typedef agg::renderer_base renderer_base; + typedef agg::renderer_scanline_aa_alpha, + span_gen_type> renderer_type; + img_accessor_type ia(pixf); + interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) ); + span_gen_type sg(ia, interpolator, filter); + renderer_type rp(renb,sa, sg, unsigned(opacity*255)); + agg::render_scanlines(ras, sl, rp); +} + +template +struct raster_markers_rasterizer_dispatch +{ + typedef agg::rgba8 color_type; + typedef agg::order_rgba order_type; + typedef agg::pixel32_type pixel_type; + typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender + typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; + typedef agg::renderer_base renderer_base; + + raster_markers_rasterizer_dispatch(BufferType & image_buffer, + Rasterizer & ras, + image_data_32 const& src, + agg::trans_affine const& marker_trans, + markers_symbolizer const& sym, + Detector & detector, + double scale_factor) + : buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4), + pixf_(buf_), + renb_(pixf_), + ras_(ras), + src_(src), + marker_trans_(marker_trans), + sym_(sym), + detector_(detector), + scale_factor_(scale_factor) + { + pixf_.comp_op(static_cast(sym_.comp_op())); + } + + template + void add_path(T & path) + { + marker_placement_e placement_method = sym_.get_marker_placement(); + box2d bbox_(0,0, src_.width(),src_.height()); + + if (placement_method != MARKER_LINE_PLACEMENT) + { + double x,y; + path.rewind(0); + if (placement_method == MARKER_INTERIOR_PLACEMENT) + { + label::interior_position(path, x, y); + } + else + { + label::centroid(path, x, y); + } + agg::trans_affine matrix = marker_trans_; + matrix.translate(x,y); + box2d transformed_bbox = bbox_ * matrix; + + if (sym_.get_allow_overlap() || + detector_.has_placement(transformed_bbox)) + { + + float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1; + render_raster_marker(ras_, renb_, sl_, src_, + matrix, opacity); + if (!sym_.get_ignore_placement()) + detector_.insert(transformed_bbox); + } + } + else + { + markers_placement placement(path, bbox_, marker_trans_, 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 = marker_trans_; + matrix.rotate(angle); + matrix.translate(x,y); + float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1; + render_raster_marker(ras_, renb_, sl_, src_, + matrix, opacity); + } + } + } +private: + agg::scanline_u8 sl_; + agg::rendering_buffer buf_; + pixfmt_comp_type pixf_; + renderer_base renb_; + Rasterizer & ras_; + image_data_32 const& src_; + agg::trans_affine const& marker_trans_; + markers_symbolizer const& sym_; + Detector & detector_; + double scale_factor_; +}; + + template void build_ellipse(T const& sym, mapnik::feature_impl const& feature, svg_storage_type & marker_ellipse, svg::svg_path_adapter & svg_path) { diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 754b2445f..5a5b10810 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -29,18 +29,19 @@ #include #include #include +#include #include #include -#include #include #include #include #include -#include #include // agg #include "agg_basics.h" +#include "agg_renderer_base.h" +#include "agg_renderer_scanline.h" #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" #include "agg_rasterizer_scanline_aa.h" @@ -48,257 +49,13 @@ #include "agg_path_storage.h" #include "agg_conv_clip_polyline.h" #include "agg_conv_transform.h" -#include "agg_image_filters.h" -#include "agg_trans_bilinear.h" -#include "agg_span_allocator.h" -#include "agg_image_accessors.h" -#include "agg_span_image_filter_rgba.h" + // boost #include namespace mapnik { -template -struct vector_markers_rasterizer_dispatch -{ - typedef agg::rgba8 color_type; - typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; - typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender - typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; - typedef agg::renderer_base renderer_base; - - vector_markers_rasterizer_dispatch(BufferType & image_buffer, - SvgRenderer & svg_renderer, - Rasterizer & ras, - box2d const& bbox, - agg::trans_affine const& marker_trans, - markers_symbolizer const& sym, - Detector & detector, - double scale_factor) - : buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4), - pixf_(buf_), - renb_(pixf_), - svg_renderer_(svg_renderer), - ras_(ras), - bbox_(bbox), - marker_trans_(marker_trans), - sym_(sym), - detector_(detector), - scale_factor_(scale_factor) - { - pixf_.comp_op(static_cast(sym_.comp_op())); - } - - template - void add_path(T & path) - { - marker_placement_e placement_method = sym_.get_marker_placement(); - - if (placement_method != MARKER_LINE_PLACEMENT) - { - double x,y; - path.rewind(0); - if (placement_method == MARKER_INTERIOR_PLACEMENT) - { - label::interior_position(path, x, y); - } - else - { - label::centroid(path, x, y); - } - agg::trans_affine matrix = marker_trans_; - matrix.translate(x,y); - box2d transformed_bbox = bbox_ * matrix; - - if (sym_.get_allow_overlap() || - detector_.has_placement(transformed_bbox)) - { - svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_); - - if (!sym_.get_ignore_placement()) - detector_.insert(transformed_bbox); - } - } - else - { - markers_placement placement(path, bbox_, marker_trans_, 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 = marker_trans_; - matrix.rotate(angle); - matrix.translate(x, y); - svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_); - } - } - } -private: - agg::scanline_u8 sl_; - agg::rendering_buffer buf_; - pixfmt_comp_type pixf_; - renderer_base renb_; - SvgRenderer & svg_renderer_; - Rasterizer & ras_; - box2d const& bbox_; - agg::trans_affine const& marker_trans_; - markers_symbolizer const& sym_; - Detector & detector_; - double scale_factor_; -}; - -template -void render_raster_marker(Rasterizer & ras, RendererBuffer & renb, - agg::scanline_u8 & sl, image_data_32 const& src, - agg::trans_affine const& marker_tr, double opacity) -{ - double width = src.width(); - double height = src.height(); - double p[8]; - p[0] = 0; p[1] = 0; - p[2] = width; p[3] = 0; - p[4] = width; p[5] = height; - p[6] = 0; p[7] = height; - - marker_tr.transform(&p[0], &p[1]); - marker_tr.transform(&p[2], &p[3]); - marker_tr.transform(&p[4], &p[5]); - marker_tr.transform(&p[6], &p[7]); - - ras.move_to_d(p[0],p[1]); - ras.line_to_d(p[2],p[3]); - ras.line_to_d(p[4],p[5]); - ras.line_to_d(p[6],p[7]); - - typedef agg::rgba8 color_type; - agg::span_allocator sa; - agg::image_filter_bilinear filter_kernel; - agg::image_filter_lut filter(filter_kernel, false); - - agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(), - src.width(), - src.height(), - src.width()*4); - agg::pixfmt_rgba32_pre pixf(marker_buf); - - typedef agg::image_accessor_clone img_accessor_type; - typedef agg::span_interpolator_linear interpolator_type; - typedef agg::span_image_filter_rgba_2x2 span_gen_type; - typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; - typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender - typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; - typedef agg::renderer_base renderer_base; - typedef agg::renderer_scanline_aa_alpha, - span_gen_type> renderer_type; - img_accessor_type ia(pixf); - interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) ); - span_gen_type sg(ia, interpolator, filter); - renderer_type rp(renb,sa, sg, unsigned(opacity*255)); - agg::render_scanlines(ras, sl, rp); -} - -template -struct raster_markers_rasterizer_dispatch -{ - typedef agg::rgba8 color_type; - typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; - typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender - typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; - typedef agg::renderer_base renderer_base; - - raster_markers_rasterizer_dispatch(BufferType & image_buffer, - Rasterizer & ras, - image_data_32 const& src, - agg::trans_affine const& marker_trans, - markers_symbolizer const& sym, - Detector & detector, - double scale_factor) - : buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4), - pixf_(buf_), - renb_(pixf_), - ras_(ras), - src_(src), - marker_trans_(marker_trans), - sym_(sym), - detector_(detector), - scale_factor_(scale_factor) - { - pixf_.comp_op(static_cast(sym_.comp_op())); - } - - template - void add_path(T & path) - { - marker_placement_e placement_method = sym_.get_marker_placement(); - box2d bbox_(0,0, src_.width(),src_.height()); - - if (placement_method != MARKER_LINE_PLACEMENT) - { - double x,y; - path.rewind(0); - if (placement_method == MARKER_INTERIOR_PLACEMENT) - { - label::interior_position(path, x, y); - } - else - { - label::centroid(path, x, y); - } - agg::trans_affine matrix = marker_trans_; - matrix.translate(x,y); - box2d transformed_bbox = bbox_ * matrix; - - if (sym_.get_allow_overlap() || - detector_.has_placement(transformed_bbox)) - { - - float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1; - render_raster_marker(ras_, renb_, sl_, src_, - matrix, opacity); - if (!sym_.get_ignore_placement()) - detector_.insert(transformed_bbox); - } - } - else - { - markers_placement placement(path, bbox_, marker_trans_, 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 = marker_trans_; - matrix.rotate(angle); - matrix.translate(x,y); - float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1; - render_raster_marker(ras_, renb_, sl_, src_, - matrix, opacity); - } - } - } -private: - agg::scanline_u8 sl_; - agg::rendering_buffer buf_; - pixfmt_comp_type pixf_; - renderer_base renb_; - Rasterizer & ras_; - image_data_32 const& src_; - agg::trans_affine const& marker_trans_; - markers_symbolizer const& sym_; - Detector & detector_; - double scale_factor_; -}; - - template void agg_renderer::process(markers_symbolizer const& sym, feature_impl & feature, From 5414912160267a219e8c418c3adfaecea3c9ebec Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 3 Aug 2012 15:34:21 +0100 Subject: [PATCH 46/50] + prevent division by zero in color-burn mode (FIXME: re-implement using latest(corrected) math from http://www.w3.org/TR/SVGCompositing/) + re-implemented grain-extract mode --- deps/agg/include/agg_pixfmt_rgba.h | 65 ++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/deps/agg/include/agg_pixfmt_rgba.h b/deps/agg/include/agg_pixfmt_rgba.h index a134eb6fc..127638dc9 100644 --- a/deps/agg/include/agg_pixfmt_rgba.h +++ b/deps/agg/include/agg_pixfmt_rgba.h @@ -1015,18 +1015,34 @@ namespace agg // Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa) // // Da' = Sa + Da - Sa.Da + + + // http://www.w3.org/TR/SVGCompositing/ + // if Sca == 0 and Dca == Da + // Dca' = Sa × Da + Sca × (1 - Da) + Dca × (1 - Sa) + // = Sa × Da + Dca × (1 - Sa) + // = Da = Dca + // otherwise if Sca == 0 + // Dca' = Sca × (1 - Da) + Dca × (1 - Sa) + // = Dca × (1 - Sa) + // otherwise if Sca > 0 + // Dca' = Sa × Da - Sa × Da × min(1, (1 - Dca/Da) × Sa/Sca) + Sca × (1 - Da) + Dca × (1 - Sa) + // = Sa × Da × (1 - min(1, (1 - Dca/Da) × Sa/Sca)) + Sca × (1 - Da) + Dca × (1 - Sa) + + // sa * da * (255 - std::min(255, (255 - p[0]/da)*(sa/(sc*sa)) + static AGG_INLINE void blend_pix(value_type* p, unsigned sr, unsigned sg, unsigned sb, unsigned sa, unsigned cover) { - if(cover < 255) + if (cover < 255) { sr = (sr * cover + 255) >> 8; sg = (sg * cover + 255) >> 8; sb = (sb * cover + 255) >> 8; sa = (sa * cover + 255) >> 8; } - if(sa) + + if (sa) { calc_type d1a = base_mask - p[Order::A]; calc_type s1a = base_mask - sa; @@ -1042,15 +1058,15 @@ namespace agg long_type sbda = sb * da; long_type sada = sa * da; - p[Order::R] = (value_type)(((srda + drsa <= sada) ? + if ( sr > 0) p[Order::R] = (value_type)(((srda + drsa <= sada) ? sr * d1a + dr * s1a : sa * (srda + drsa - sada) / sr + sr * d1a + dr * s1a + base_mask) >> base_shift); - p[Order::G] = (value_type)(((sgda + dgsa <= sada) ? + if ( sg > 0 ) p[Order::G] = (value_type)(((sgda + dgsa <= sada) ? sg * d1a + dg * s1a : sa * (sgda + dgsa - sada) / sg + sg * d1a + dg * s1a + base_mask) >> base_shift); - p[Order::B] = (value_type)(((sbda + dbsa <= sada) ? + if ( sb > 0) p[Order::B] = (value_type)(((sbda + dbsa <= sada) ? sb * d1a + db * s1a : sa * (sbda + dbsa - sada) / sb + sb * d1a + db * s1a + base_mask) >> base_shift); @@ -1502,24 +1518,29 @@ namespace agg unsigned sr, unsigned sg, unsigned sb, unsigned sa, unsigned cover) { - if (cover < 255) - { - sr = (sr * cover + 255) >> 8; - sg = (sg * cover + 255) >> 8; - sb = (sb * cover + 255) >> 8; - sa = (sa * cover + 255) >> 8; - } - if (sa > 0) - { - calc_type da = p[Order::A]; - int dr = p[Order::R] - sr + 128; - int dg = p[Order::G] - sg + 128; - int db = p[Order::B] - sb + 128; + calc_type da = (p[Order::A] * sa + 255) >> 8; - p[Order::R] = dr < 0 ? 0 : (dr > 255 ? 255 : dr); - p[Order::G] = dg < 0 ? 0 : (dg > 255 ? 255 : dg); - p[Order::B] = db < 0 ? 0 : (db > 255 ? 255 : db); - p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + int dr = p[Order::R] - sr + 128; + int dg = p[Order::G] - sg + 128; + int db = p[Order::B] - sb + 128; + + dr = dr < 0 ? 0 : (dr > 255 ? 255 : dr); + dg = dg < 0 ? 0 : (dg > 255 ? 255 : dg); + db = db < 0 ? 0 : (db > 255 ? 255 : db); + + p[Order::A] = da; + + if (da < 255) + { + p[Order::R] = (dr * da + 255) >> 8; + p[Order::G] = (dg * da + 255) >> 8; + p[Order::B] = (db * da + 255) >> 8; + } + else + { + p[Order::R] = dr; + p[Order::G] = dg; + p[Order::B] = db; } } }; From 8c98d8a199226cd59c16f707bd1e87aa78cc12a3 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 3 Aug 2012 15:37:05 +0100 Subject: [PATCH 47/50] + pre-multiply rendering buffer after applying background color and/or pattern --- src/agg/agg_renderer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index cd0c55e1e..a1ce58534 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -159,6 +159,10 @@ void agg_renderer::setup(Map const &m) } } + agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4); + agg::pixfmt_rgba32 pixf(buf); + pixf.premultiply(); + MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Scale=" << m.scale(); } @@ -169,7 +173,6 @@ template void agg_renderer::start_map_processing(Map const& map) { MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start map processing bbox=" << map.get_current_extent(); - ras_ptr->clip_box(0,0,width_,height_); } From 5f26d82ca2930c3250be7d6a24345e13a7db22ab Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 3 Aug 2012 15:38:02 +0100 Subject: [PATCH 48/50] + correct typedef logic (avoid redefining types) --- include/mapnik/marker_helpers.hpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index 2a11c07d6..54a1201fe 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -55,12 +55,8 @@ namespace mapnik { template struct vector_markers_rasterizer_dispatch { - typedef agg::rgba8 color_type; - typedef agg::order_rgba order_type; - typedef agg::pixel32_type pixel_type; - typedef agg::comp_op_adaptor_rgba_pre blender_type; // comp blender - typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; - typedef agg::renderer_base renderer_base; + typedef typename SvgRenderer::renderer_base renderer_base; + typedef typename renderer_base::pixfmt_type pixfmt_type; vector_markers_rasterizer_dispatch(BufferType & image_buffer, SvgRenderer & svg_renderer, @@ -133,7 +129,7 @@ struct vector_markers_rasterizer_dispatch private: agg::scanline_u8 sl_; agg::rendering_buffer buf_; - pixfmt_comp_type pixf_; + pixfmt_type pixf_; renderer_base renb_; SvgRenderer & svg_renderer_; Rasterizer & ras_; From 9e95d88acd912d7779b8e71968e17e5a5b5ff3b4 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 3 Aug 2012 15:39:04 +0100 Subject: [PATCH 49/50] + make typedefs public --- include/mapnik/svg/svg_renderer.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mapnik/svg/svg_renderer.hpp b/include/mapnik/svg/svg_renderer.hpp index fad181e02..f3b61c041 100644 --- a/include/mapnik/svg/svg_renderer.hpp +++ b/include/mapnik/svg/svg_renderer.hpp @@ -102,14 +102,14 @@ private: template class svg_renderer : boost::noncopyable { +public: typedef agg::conv_curve curved_type; typedef agg::conv_stroke curved_stroked_type; typedef agg::conv_transform curved_stroked_trans_type; typedef agg::conv_transform curved_trans_type; typedef agg::conv_contour curved_trans_contour_type; typedef agg::renderer_base renderer_base; - -public: + svg_renderer(VertexSource & source, AttributeSource const& attributes) : source_(source), curved_(source_), From 6fc8f3ab8978053f332207a23541f8f7e50a8aed Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 3 Aug 2012 15:39:44 +0100 Subject: [PATCH 50/50] + instantiate svg_renderer with pixfmt_comp_type --- src/agg/process_markers_symbolizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 5a5b10810..a84787702 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -92,7 +92,7 @@ void agg_renderer::process(markers_symbolizer const& sym, typedef svg_renderer svg_renderer_type; + pixfmt_comp_type > svg_renderer_type; typedef vector_markers_rasterizer_dispatch