Skip to content

Commit

Permalink
Exposed the label collision detector outside the agg_render object
Browse files Browse the repository at this point in the history
and via Python, allowing detectors to be re-used across renderings.
  • Loading branch information
zerebubuth committed Oct 12, 2011
1 parent 8fba4b1 commit b5c4bb7
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 18 deletions.
38 changes: 38 additions & 0 deletions bindings/python/mapnik_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <mapnik/version.hpp>
#include <mapnik/value_error.hpp>
Expand Down Expand Up @@ -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<mapnik::label_collision_detector4> detector,
double scale_factor = 1.0,
unsigned offset_x = 0u,
unsigned offset_y = 0u)
{
Py_BEGIN_ALLOW_THREADS
try
{
mapnik::agg_renderer<mapnik::image_32> 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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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"))
);
Expand Down
9 changes: 8 additions & 1 deletion include/mapnik/agg_renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
// boost
#include <boost/utility.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>

// FIXME
// forward declare so that
Expand All @@ -61,7 +62,11 @@ class MAPNIK_DECL agg_renderer : public feature_style_processor<agg_renderer<T>
{

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<label_collision_detector4> 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);
Expand Down Expand Up @@ -122,8 +127,10 @@ class MAPNIK_DECL agg_renderer : public feature_style_processor<agg_renderer<T>
CoordTransform t_;
freetype_engine font_engine_;
face_manager<freetype_engine> font_manager_;
label_collision_detector4 detector_;
boost::shared_ptr<label_collision_detector4> detector_;
boost::scoped_ptr<rasterizer> ras_ptr;

void setup(Map const &m);
};
}

Expand Down
10 changes: 8 additions & 2 deletions include/mapnik/label_collision_detector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class label_collision_detector2 : boost::noncopyable
typedef quad_tree<box2d<double> > tree_t;
tree_t tree_;
public:

explicit label_collision_detector2(box2d<double> const& extent)
: tree_(extent) {}

Expand Down Expand Up @@ -138,6 +138,7 @@ class label_collision_detector3 : boost::noncopyable
//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<double> const& b) : box(b) {}
Expand All @@ -146,11 +147,13 @@ class label_collision_detector4 : boost::noncopyable
box2d<double> 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<double> const& extent)
: tree_(extent) {}
Expand Down Expand Up @@ -224,6 +227,9 @@ class label_collision_detector4 : boost::noncopyable
{
return tree_.extent();
}

query_iterator begin() { return tree_.query_in_box(extent()); }
query_iterator end() { return tree_.query_end(); }
};
}

Expand Down
27 changes: 25 additions & 2 deletions src/agg/agg_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,31 @@ agg_renderer<T>::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<double>(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size())),
detector_(new label_collision_detector4(box2d<double>(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))),
ras_ptr(new rasterizer)
{
setup(m);
}

template <typename T>
agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, boost::shared_ptr<label_collision_detector4> detector,
double scale_factor, unsigned offset_x, unsigned offset_y)
: feature_style_processor<agg_renderer>(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 <typename T>
void agg_renderer<T>::setup(Map const &m)
{
boost::optional<color> const& bg = m.background();
if (bg) pixmap_.set_background(*bg);
Expand Down Expand Up @@ -189,7 +212,7 @@ void agg_renderer<T>::start_layer_processing(layer const& lay)
#endif
if (lay.clear_label_cache())
{
detector_.clear();
detector_->clear();
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/agg/process_glyph_symbolizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ void agg_renderer<T>::process(glyph_symbolizer const& sym,
// final box so we can check for a valid placement
box2d<double> dim = ren.prepare_glyphs(path.get());
box2d<double> 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);
}
Expand Down
8 changes: 4 additions & 4 deletions src/agg/process_markers_symbolizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
}

path_type path(t_,geom,prj_trans);
markers_placement<path_type, label_collision_detector4> placement(path, extent, detector_,
markers_placement<path_type, label_collision_detector4> placement(path, extent, *detector_,
sym.get_spacing() * scale_factor_,
sym.get_max_error(),
sym.get_allow_overlap());
Expand Down Expand Up @@ -194,7 +194,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
box2d<double> 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);
Expand All @@ -215,7 +215,7 @@ void agg_renderer<T>::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);
}
}
Expand All @@ -226,7 +226,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
marker.concat_path(arrow_);

path_type path(t_,geom,prj_trans);
markers_placement<path_type, label_collision_detector4> placement(path, extent, detector_,
markers_placement<path_type, label_collision_detector4> placement(path, extent, *detector_,
sym.get_spacing() * scale_factor_,
sym.get_max_error(),
sym.get_allow_overlap());
Expand Down
4 changes: 2 additions & 2 deletions src/agg/process_point_symbolizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ void agg_renderer<T>::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);
}
Expand Down
6 changes: 3 additions & 3 deletions src/agg/process_shield_symbolizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
ren.set_opacity(sym.get_text_opacity());

placement_finder<label_collision_detector4> finder(detector_);
placement_finder<label_collision_detector4> finder(*detector_);

string_info info(text);

Expand Down Expand Up @@ -210,13 +210,13 @@ void agg_renderer<T>::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<double> 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);
Expand Down
2 changes: 1 addition & 1 deletion src/agg/process_text_symbolizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
ren.set_opacity(sym.get_text_opacity());

box2d<double> dims(0,0,width_,height_);
placement_finder<label_collision_detector4> finder(detector_,dims);
placement_finder<label_collision_detector4> finder(*detector_,dims);

string_info info(text);

Expand Down

1 comment on commit b5c4bb7

@artemp
Copy link

@artemp artemp commented on b5c4bb7 Oct 21, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, now I'd like to be able to pass different 'detector' implementations.

Please sign in to comment.