From b5c4bb77de1f48312d64387d5118711ac0b2eea2 Mon Sep 17 00:00:00 2001 From: Matt Amos Date: Wed, 12 Oct 2011 01:05:35 +0100 Subject: [PATCH] Exposed the label collision detector outside the agg_render object and via Python, allowing detectors to be re-used across renderings. --- bindings/python/mapnik_python.cpp | 38 +++++++++++++++++++++ include/mapnik/agg_renderer.hpp | 9 ++++- include/mapnik/label_collision_detector.hpp | 10 ++++-- src/agg/agg_renderer.cpp | 27 +++++++++++++-- src/agg/process_glyph_symbolizer.cpp | 6 ++-- src/agg/process_markers_symbolizer.cpp | 8 ++--- src/agg/process_point_symbolizer.cpp | 4 +-- src/agg/process_shield_symbolizer.cpp | 6 ++-- src/agg/process_text_symbolizer.cpp | 2 +- 9 files changed, 92 insertions(+), 18 deletions(-) diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 9d36aebc6..03c6fc75f 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -66,6 +66,7 @@ void export_view_transform(); void export_raster_colorizer(); void export_glyph_symbolizer(); void export_inmem_metawriter(); +void export_label_collision_detector(); #include #include @@ -120,6 +121,28 @@ void render(const mapnik::Map& map, Py_END_ALLOW_THREADS } +void render_with_detector( + const mapnik::Map &map, + mapnik::image_32 &image, + boost::shared_ptr detector, + double scale_factor = 1.0, + unsigned offset_x = 0u, + unsigned offset_y = 0u) +{ + Py_BEGIN_ALLOW_THREADS + try + { + mapnik::agg_renderer ren(map,image,detector); + ren.apply(); + } + catch (...) + { + Py_BLOCK_THREADS + throw; + } + Py_END_ALLOW_THREADS +} + void render_layer2(const mapnik::Map& map, mapnik::image_32& image, unsigned layer_idx) @@ -374,6 +397,7 @@ BOOST_PYTHON_FUNCTION_OVERLOADS(load_map_string_overloads, load_map_string, 2, 4 BOOST_PYTHON_FUNCTION_OVERLOADS(save_map_overloads, save_map, 2, 3) BOOST_PYTHON_FUNCTION_OVERLOADS(save_map_to_string_overloads, save_map_to_string, 1, 2) BOOST_PYTHON_FUNCTION_OVERLOADS(render_overloads, render, 2, 5) +BOOST_PYTHON_FUNCTION_OVERLOADS(render_with_detector_overloads, render_with_detector, 3, 6) BOOST_PYTHON_MODULE(_mapnik2) { @@ -427,6 +451,7 @@ BOOST_PYTHON_MODULE(_mapnik2) export_raster_colorizer(); export_glyph_symbolizer(); export_inmem_metawriter(); + export_label_collision_detector(); def("render_grid",&render_grid, ( arg("map"), @@ -503,6 +528,19 @@ BOOST_PYTHON_MODULE(_mapnik2) "\n" )); + def("render_with_detector", &render_with_detector, render_with_detector_overloads( + "\n" + "Render Map to an AGG image_32 using a pre-constructed detector.\n" + "\n" + "Usage:\n" + ">>> from mapnik import Map, Image, LabelCollisionDetector, render_with_detector, load_map\n" + ">>> m = Map(256,256)\n" + ">>> load_map(m,'mapfile.xml')\n" + ">>> im = Image(m.width,m.height)\n" + ">>> detector = LabelCollisionDetector(m)\n" + ">>> render_with_detector(m, im, detector)\n" + )); + def("render_layer", &render_layer2, (arg("map"),arg("image"),args("layer")) ); diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 08a48d44a..3e5e218a5 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -40,6 +40,7 @@ // boost #include #include +#include // FIXME // forward declare so that @@ -61,7 +62,11 @@ class MAPNIK_DECL agg_renderer : public feature_style_processor { public: + // create with default, empty placement detector agg_renderer(Map const& m, T & pixmap, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0); + // create with external placement detector, possibly non-empty + agg_renderer(Map const &m, T & pixmap, boost::shared_ptr detector, + double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0); ~agg_renderer(); void start_map_processing(Map const& map); void end_map_processing(Map const& map); @@ -122,8 +127,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; + + void setup(Map const &m); }; } diff --git a/include/mapnik/label_collision_detector.hpp b/include/mapnik/label_collision_detector.hpp index f52d394e1..b9da67595 100644 --- a/include/mapnik/label_collision_detector.hpp +++ b/include/mapnik/label_collision_detector.hpp @@ -69,7 +69,7 @@ class label_collision_detector2 : boost::noncopyable typedef quad_tree > tree_t; tree_t tree_; public: - + explicit label_collision_detector2(box2d const& extent) : tree_(extent) {} @@ -138,6 +138,7 @@ public: //quad tree based label collission detector so labels dont appear within a given distance class label_collision_detector4 : boost::noncopyable { +public: struct label { label(box2d const& b) : box(b) {} @@ -146,11 +147,13 @@ class label_collision_detector4 : boost::noncopyable box2d box; UnicodeString text; }; - + +private: typedef quad_tree< label > tree_t; tree_t tree_; public: + typedef tree_t::query_iterator query_iterator; explicit label_collision_detector4(box2d const& extent) : tree_(extent) {} @@ -224,6 +227,9 @@ public: { return tree_.extent(); } + + query_iterator begin() { return tree_.query_in_box(extent()); } + query_iterator end() { return tree_.query_end(); } }; } diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index 42793cf49..bcdc8ef12 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -121,8 +121,31 @@ agg_renderer::agg_renderer(Map const& m, T & pixmap, double scale_factor, uns t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y), font_engine_(), font_manager_(font_engine_), - detector_(box2d(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size())), + detector_(new label_collision_detector4(box2d(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))), ras_ptr(new rasterizer) +{ + setup(m); +} + +template +agg_renderer::agg_renderer(Map const& m, T & pixmap, boost::shared_ptr detector, + double scale_factor, unsigned offset_x, unsigned offset_y) + : feature_style_processor(m, scale_factor), + pixmap_(pixmap), + width_(pixmap_.width()), + height_(pixmap_.height()), + scale_factor_(scale_factor), + t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y), + font_engine_(), + font_manager_(font_engine_), + detector_(detector), + ras_ptr(new rasterizer) +{ + setup(m); +} + +template +void agg_renderer::setup(Map const &m) { boost::optional const& bg = m.background(); if (bg) pixmap_.set_background(*bg); @@ -189,7 +212,7 @@ void agg_renderer::start_layer_processing(layer const& lay) #endif if (lay.clear_label_cache()) { - detector_.clear(); + detector_->clear(); } } diff --git a/src/agg/process_glyph_symbolizer.cpp b/src/agg/process_glyph_symbolizer.cpp index e3b31b3cb..674c2eed7 100644 --- a/src/agg/process_glyph_symbolizer.cpp +++ b/src/agg/process_glyph_symbolizer.cpp @@ -77,12 +77,12 @@ void agg_renderer::process(glyph_symbolizer const& sym, // final box so we can check for a valid placement box2d dim = ren.prepare_glyphs(path.get()); box2d ext(x-dim.width()/2, y-dim.height()/2, x+dim.width()/2, y+dim.height()/2); - if ((sym.get_allow_overlap() || detector_.has_placement(ext)) && - (!sym.get_avoid_edges() || detector_.extent().contains(ext))) + if ((sym.get_allow_overlap() || detector_->has_placement(ext)) && + (!sym.get_avoid_edges() || detector_->extent().contains(ext))) { // Placement is valid, render glyph and update detector. ren.render(x, y); - detector_.insert(ext); + detector_->insert(ext); metawriter_with_properties writer = sym.get_metawriter(); if (writer.first) writer.first->add_box(ext, feature, t_, writer.second); } diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index d40b02ee7..cd9757a01 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -109,7 +109,7 @@ void agg_renderer::process(markers_symbolizer const& sym, } path_type path(t_,geom,prj_trans); - markers_placement placement(path, extent, detector_, + markers_placement placement(path, extent, *detector_, sym.get_spacing() * scale_factor_, sym.get_max_error(), sym.get_allow_overlap()); @@ -194,7 +194,7 @@ void agg_renderer::process(markers_symbolizer const& sym, box2d label_ext (px, py, px + dx +1, py + dy +1); if (sym.get_allow_overlap() || - detector_.has_placement(label_ext)) + detector_->has_placement(label_ext)) { agg::ellipse c(x, y, w, h); marker.concat_path(c); @@ -215,7 +215,7 @@ void agg_renderer::process(markers_symbolizer const& sym, ren.color(agg::rgba8(s_r, s_g, s_b, int(s_a*stroke_.get_opacity()))); agg::render_scanlines(*ras_ptr, sl_line, ren); } - detector_.insert(label_ext); + detector_->insert(label_ext); if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second); } } @@ -226,7 +226,7 @@ void agg_renderer::process(markers_symbolizer const& sym, marker.concat_path(arrow_); path_type path(t_,geom,prj_trans); - markers_placement placement(path, extent, detector_, + markers_placement placement(path, extent, *detector_, sym.get_spacing() * scale_factor_, sym.get_max_error(), sym.get_allow_overlap()); diff --git a/src/agg/process_point_symbolizer.cpp b/src/agg/process_point_symbolizer.cpp index fd57ec1d5..d9974d82e 100644 --- a/src/agg/process_point_symbolizer.cpp +++ b/src/agg/process_point_symbolizer.cpp @@ -99,13 +99,13 @@ void agg_renderer::process(point_symbolizer const& sym, label_ext.re_center(x,y); if (sym.get_allow_overlap() || - detector_.has_placement(label_ext)) + detector_->has_placement(label_ext)) { render_marker(floor(x - 0.5 * w),floor(y - 0.5 * h) ,**marker,tr, sym.get_opacity()); if (!sym.get_ignore_placement()) - detector_.insert(label_ext); + detector_->insert(label_ext); metawriter_with_properties writer = sym.get_metawriter(); if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second); } diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index cb3591abc..17f87e0e0 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -135,7 +135,7 @@ void agg_renderer::process(shield_symbolizer const& sym, ren.set_halo_radius(sym.get_halo_radius() * scale_factor_); ren.set_opacity(sym.get_text_opacity()); - placement_finder finder(detector_); + placement_finder finder(*detector_); string_info info(text); @@ -210,13 +210,13 @@ void agg_renderer::process(shield_symbolizer const& sym, label_ext.re_center(label_x,label_y); } - if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) ) + if ( sym.get_allow_overlap() || detector_->has_placement(label_ext) ) { render_marker(px,py,**marker,tr,sym.get_opacity()); box2d dim = ren.prepare_glyphs(&text_placement.placements[0]); ren.render(x,y); - detector_.insert(label_ext); + detector_->insert(label_ext); finder.update_detector(text_placement); if (writer.first) { writer.first->add_box(label_ext, feature, t_, writer.second); diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index 58b00a4d6..3d7440c19 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -110,7 +110,7 @@ void agg_renderer::process(text_symbolizer const& sym, ren.set_opacity(sym.get_text_opacity()); box2d dims(0,0,width_,height_); - placement_finder finder(detector_,dims); + placement_finder finder(*detector_,dims); string_info info(text);