Update symbolizer helpers for ShieldSymbolizer.

Correctly handle point placement for TextSymbolizer. (Tries each possible placement for each point).
This commit is contained in:
Hermann Kraus 2012-01-29 04:49:02 +01:00
parent 2d50c840fe
commit 09459683e9
7 changed files with 524 additions and 406 deletions

View file

@ -36,20 +36,17 @@ public:
placement_finder(text_placement_info &p, string_info &info, DetectorT & detector);
placement_finder(text_placement_info &p, string_info &info, DetectorT & detector, box2d<double> const& extent);
//Try place a single label at the given point
/** Try place a single label at the given point. */
void find_point_placement(double pos_x, double pos_y, double angle=0.0);
//Iterate over the given path, placing point labels with respect to label_spacing
/** Iterate over the given path, placing point labels with respect to label_spacing. */
template <typename T>
void find_point_placements(T & path);
//Iterate over the given path, placing line-following labels with respect to label_spacing
/** Iterate over the given path, placing line-following labels with respect to label_spacing. */
template <typename T>
void find_line_placements(T & path);
//Find placement, automatically select point or line placement
void find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans);
void update_detector();
void clear();

View file

@ -36,6 +36,10 @@
namespace mapnik {
typedef std::pair<double, double> point_type;
/** Helper object that does all the TextSymbolizer placment finding
* work except actually rendering the object. */
template <typename FaceManagerT, typename DetectorT>
class text_symbolizer_helper
{
@ -48,223 +52,119 @@ public:
double scale_factor,
CoordTransform const &t,
FaceManagerT &font_manager,
DetectorT &detector) :
sym_(sym),
feature_(feature),
prj_trans_(prj_trans),
width_(width),
height_(height),
scale_factor_(scale_factor),
t_(t),
font_manager_(font_manager),
detector_(detector),
text_(),
angle_(0.0)
{
initialize_geometries();
if (!geometries_to_process_.size()) return;
text_ = boost::shared_ptr<processed_text>(new processed_text(font_manager_, scale_factor_));
placement_ = sym_.get_placement_options()->get_placement_info();
placement_->init(scale_factor_, width_, height_);
metawriter_with_properties writer = sym_.get_metawriter();
if (writer.first)
placement_->collect_extents = true;
itr_ = geometries_to_process_.begin();
next_placement();
}
bool next_placement()
{
if (!placement_->next()) return false;
/* TODO: Simplify this. */
text_->clear();
placement_->properties.processor.process(*text_, feature_);
info_ = &(text_->get_string_info());
if (placement_->properties.orientation)
{
angle_ = boost::apply_visitor(
evaluate<Feature, value_type>(feature_),
*(placement_->properties.orientation)).to_double();
} else {
angle_ = 0.0;
}
/* END TODO */
return true;
}
DetectorT &detector)
: sym_(sym),
feature_(feature),
prj_trans_(prj_trans),
t_(t),
font_manager_(font_manager),
detector_(detector),
writer_(sym.get_metawriter()),
dims_(0, 0, width, height),
text_(font_manager, scale_factor),
angle_(0.0),
placement_valid_(true)
{
initialize_geometries();
if (!geometries_to_process_.size()) return; //TODO: Test this
placement_ = sym_.get_placement_options()->get_placement_info();
placement_->init(scale_factor, width, height);
if (writer_.first) placement_->collect_extents = true;
next_placement();
initialize_points();
}
/** Return next placement.
* If no more placements are found returns null pointer.
* TODO: Currently stops returning placements to early.
*/
text_placement_info_ptr get_placement();
private:
text_placement_info_ptr get_point_placement();
text_placement_info_ptr get_line_placement();
protected:
bool next_placement();
void initialize_geometries();
void initialize_points();
//Input
text_symbolizer const& sym_;
Feature const& feature_;
proj_transform const& prj_trans_;
unsigned width_;
unsigned height_;
double scale_factor_;
CoordTransform const &t_;
FaceManagerT &font_manager_;
DetectorT &detector_;
boost::shared_ptr<processed_text> text_; /*TODO: Use shared pointers for text placement so we don't need to keep a reference here! */
std::vector<geometry_type*> geometries_to_process_;
std::vector<geometry_type*>::iterator itr_;
text_placement_info_ptr placement_;
metawriter_with_properties writer_;
box2d<double> dims_;
//Processing
processed_text text_;
/* Using list instead of vector, because we delete random elements and need iterators to stay valid. */
std::list<geometry_type*> geometries_to_process_;
std::list<geometry_type*>::iterator geo_itr_;
std::list<point_type> points_;
std::list<point_type>::iterator point_itr_;
double angle_;
string_info *info_;
bool placement_valid_;
bool point_placement_;
//Output
text_placement_info_ptr placement_;
};
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_placement()
{
if (!geometries_to_process_.size()) return text_placement_info_ptr();
metawriter_with_properties writer = sym_.get_metawriter();
box2d<double> dims(0, 0, width_, height_);
while (geometries_to_process_.size())
{
if (itr_ == geometries_to_process_.end()) {
//Just processed the last geometry. Try next placement.
if (!next_placement()) return text_placement_info_ptr(); //No more placements
//Start again from begin of list
itr_ = geometries_to_process_.begin();
}
//TODO: Avoid calling constructor repeatedly
placement_finder<DetectorT> finder(*placement_, *info_, detector_, dims);
finder.find_placement(angle_, **itr_, t_, prj_trans_);
if (placement_->placements.size())
{
//Found a placement
geometries_to_process_.erase(itr_); //Remove current geometry
if (writer.first) writer.first->add_text(*placement_, font_manager_, feature_, t_, writer.second);
itr_++;
return placement_;
}
//No placement for this geometry. Keep it in geometries_to_process_ for next try.
itr_++;
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries()
{
unsigned num_geom = feature_.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature_.get_geometry(i);
// don't bother with empty geometries
if (geom.num_points() == 0) continue;
if ((geom.type() == Polygon) && sym_.get_minimum_path_length() > 0)
{
// TODO - find less costly method than fetching full envelope
box2d<double> gbox = t_.forward(geom.envelope(), prj_trans_);
if (gbox.width() < sym_.get_minimum_path_length())
{
continue;
}
}
// TODO - calculate length here as well
geometries_to_process_.push_back(const_cast<geometry_type*>(&geom));
}
}
/*****************************************************************************/
template <typename FaceManagerT, typename DetectorT>
class shield_symbolizer_helper
class shield_symbolizer_helper: public text_symbolizer_helper<FaceManagerT, DetectorT>
{
public:
shield_symbolizer_helper(unsigned width,
unsigned height,
double scale_factor,
CoordTransform const &t,
FaceManagerT &font_manager,
DetectorT &detector) :
width_(width),
height_(height),
scale_factor_(scale_factor),
t_(t),
font_manager_(font_manager),
detector_(detector),
text_()
shield_symbolizer_helper(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans,
unsigned width,
unsigned height,
double scale_factor,
CoordTransform const &t,
FaceManagerT &font_manager,
DetectorT &detector) :
text_symbolizer_helper<FaceManagerT, DetectorT>(sym, feature, prj_trans, width, height, scale_factor, t, font_manager, detector),
sym_(sym)
{
init_marker();
}
text_placement_info_ptr get_placement(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
private:
void init_marker(shield_symbolizer const& sym, Feature const& feature);
unsigned width_;
unsigned height_;
double scale_factor_;
CoordTransform const &t_;
FaceManagerT &font_manager_;
DetectorT &detector_;
boost::shared_ptr<processed_text> text_;
box2d<double> label_ext_;
text_placement_info_ptr get_placement();
std::pair<int, int> get_marker_position(text_path &p);
marker &get_marker() const;
agg::trans_affine const& get_transform() const;
protected:
text_placement_info_ptr get_point_placement();
text_placement_info_ptr get_line_placement();
void init_marker();
shield_symbolizer const& sym_;
box2d<double> marker_ext_;
boost::optional<marker_ptr> marker_;
agg::trans_affine transform_;
int marker_w;
int marker_h;
int marker_w_;
int marker_h_;
int marker_x_;
int marker_y_;
// F***ing templates...
// http://womble.decadent.org.uk/c++/template-faq.html#base-lookup
using text_symbolizer_helper<FaceManagerT, DetectorT>::geometries_to_process_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::placement_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::next_placement;
using text_symbolizer_helper<FaceManagerT, DetectorT>::info_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::geo_itr_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::point_itr_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::points_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::writer_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::font_manager_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::feature_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::t_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::detector_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::dims_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::prj_trans_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::placement_valid_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::point_placement_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::angle_;
};
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_placement(
shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
init_marker(sym);
if (!marker_) return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
void shield_symbolizer_helper<FaceManagerT, DetectorT>::init_marker(shield_symbolizer const& sym, Feature const& feature)
{
std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature);
boost::array<double,6> const& m = sym.get_transform();
transform_.load_from(&m[0]);
marker_.reset();
if (!filename.empty())
{
marker_ = marker_cache::instance()->find(filename, true);
}
if (!marker_) {
marker_w = 0;
marker_h = 0;
label_ext_.init(0, 0, 0, 0);
return;
}
marker_w = (*marker_)->width();
marker_h = (*marker_)->height();
double px0 = - 0.5 * marker_w;
double py0 = - 0.5 * marker_h;
double px1 = 0.5 * marker_w;
double py1 = 0.5 * marker_h;
double px2 = px1;
double py2 = py0;
double px3 = px0;
double py3 = py1;
transform_.transform(&px0,&py0);
transform_.transform(&px1,&py1);
transform_.transform(&px2,&py2);
transform_.transform(&px3,&py3);
label_ext_.init(px0, py0, px1, py1);
label_ext_.expand_to_include(px2, py2);
label_ext_.expand_to_include(px3, py3);
}
} //namespace
#endif // SYMBOLIZER_HELPERS_HPP

View file

@ -23,16 +23,13 @@
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/expression_evaluator.hpp>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_u.h"
#include <mapnik/symbolizer_helpers.hpp>
// boost
#include <boost/make_shared.hpp>
@ -44,140 +41,28 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
#if 0
text_renderer<T> ren(pixmap_, faces, *strk);
shield_symbolizer_helper<face_manager<freetype_engine>,
label_collision_detector4> helper(
sym, feature, prj_trans,
width_, height_,
scale_factor_,
t_, font_manager_, *detector_);
text_renderer<T> ren(pixmap_, font_manager_, *(font_manager_.get_stroker()));
placement_finder<label_collision_detector4> finder(*detector_);
text_placement_info_ptr placement;
while ((placement = helper.get_placement())) {
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
std::pair<int, int> marker_pos = helper.get_marker_position(placement->placements[ii]);
render_marker(marker_pos.first, marker_pos.second, helper.get_marker(), helper.get_transform(), sym.get_opacity());
string_info info(text);
faces->get_string_info(info, text, 0);
metawriter_with_properties writer = sym.get_metawriter();
for (unsigned i = 0; i < feature.num_geometries(); ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 0 )
{
path_type path(t_,geom,prj_trans);
label_placement_enum how_placed = sym.get_label_placement();
if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT || how_placed == INTERIOR_PLACEMENT)
{
// for every vertex, try and place a shield/text
geom.rewind(0);
placement text_placement(info, sym, scale_factor_, w, h, false);
text_placement.avoid_edges = sym.get_avoid_edges();
text_placement.allow_overlap = sym.get_allow_overlap();
if (writer.first)
text_placement.collect_extents =true; // needed for inmem metawriter
position const& pos = sym.get_displacement();
position const& shield_pos = sym.get_shield_displacement();
for( unsigned jj = 0; jj < geom.num_points(); jj++ )
{
double label_x;
double label_y;
double z=0.0;
if( how_placed == VERTEX_PLACEMENT )
geom.vertex(&label_x,&label_y); // by vertex
else if( how_placed == INTERIOR_PLACEMENT )
geom.label_interior_position(&label_x,&label_y);
else
geom.label_position(&label_x, &label_y); // by middle of line or by point
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
label_x += boost::get<0>(shield_pos);
label_y += boost::get<1>(shield_pos);
finder.find_point_placement( text_placement, placement_options,
label_x, label_y, 0.0,
sym.get_line_spacing(),
sym.get_character_spacing());
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
if( text_placement.placements.size() > 0)
{
double x = floor(text_placement.placements[0].starting_x);
double y = floor(text_placement.placements[0].starting_y);
int px;
int py;
if( !sym.get_unlock_image() )
{
// center image at text center position
// remove displacement from image label
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
px=int(floor(lx - (0.5 * w))) + 1;
py=int(floor(ly - (0.5 * h))) + 1;
label_ext.re_center(lx,ly);
}
else
{ // center image at reference location
px=int(floor(label_x - 0.5 * w));
py=int(floor(label_y - 0.5 * h));
label_ext.re_center(label_x,label_y);
}
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);
finder.update_detector(text_placement);
if (writer.first) {
writer.first->add_box(label_ext, feature, t_, writer.second);
writer.first->add_text(text_placement, faces, feature, t_, writer.second);
}
}
}
}
}
else if (geom.num_points() > 1 && how_placed == LINE_PLACEMENT)
{
placement text_placement(info, sym, scale_factor_, w, h, false);
position const& pos = sym.get_displacement();
text_placement.avoid_edges = sym.get_avoid_edges();
text_placement.additional_boxes.push_back(
box2d<double>(-0.5 * label_ext.width() - boost::get<0>(pos),
-0.5 * label_ext.height() - boost::get<1>(pos),
0.5 * label_ext.width() - boost::get<0>(pos),
0.5 * label_ext.height() - boost::get<1>(pos)));
finder.find_point_placements<path_type>(text_placement, placement_options, path);
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
double x = floor(text_placement.placements[ii].starting_x);
double y = floor(text_placement.placements[ii].starting_y);
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
int px=int(floor(lx - (0.5*w))) + 1;
int py=int(floor(ly - (0.5*h))) + 1;
label_ext.re_center(lx, ly);
render_marker(px,py,**marker,tr,sym.get_opacity());
box2d<double> dim = ren.prepare_glyphs(&text_placement.placements[ii]);
ren.render(x,y);
if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second);
}
finder.update_detector(text_placement);
if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second);
}
}
}
double x = placement->placements[ii].starting_x;
double y = placement->placements[ii].starting_y;
ren.prepare_glyphs(&(placement->placements[ii]));
ren.render(x, y);
}
}
#endif
}

View file

@ -143,6 +143,7 @@ source = Split(
memory_datasource.cpp
stroke.cpp
symbolizer.cpp
symbolizer_helpers.cpp
arrow.cpp
unicode.cpp
markers_symbolizer.cpp

View file

@ -1281,7 +1281,6 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
placement_finder = text_placements_ptr(new text_placements_dummy());
}
text_symbolizer text_symbol = text_symbolizer(placement_finder);
placement_finder->properties.from_xml(sym, fontsets_);
if (strict_) ensure_font_face(placement_finder->properties.processor.defaults.face_name);
if (list) {
@ -1300,6 +1299,8 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
if (strict_) ensure_font_face(p.processor.defaults.face_name);
}
}
text_symbolizer text_symbol = text_symbolizer(placement_finder);
parse_metawriter_in_symbolizer(text_symbol, sym);
rule.append(text_symbol);
}
@ -1312,26 +1313,64 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
{
std::string s_common(
"name,face-name,fontset-name,size,fill,orientation,"
"dx,dy,placement,vertical-alignment,halo-fill,"
"halo-radius,text-ratio,wrap-width,wrap-before,"
"wrap-character,text-transform,line-spacing,"
"label-position-tolerance,character-spacing,"
"spacing,minimum-distance,minimum-padding,minimum-path-length,"
"avoid-edges,allow-overlap,opacity,max-char-angle-delta,"
"horizontal-alignment,justify-alignment");
std::stringstream s;
s << "name,face-name,fontset-name,size,fill,"
<< "dx,dy,placement,vertical-alignment,halo-fill,"
<< "halo-radius,text-ratio,wrap-width,wrap-before,"
<< "wrap-character,text-transform,line-spacing,"
<< "label-position-tolerance,character-spacing,"
<< "spacing,minimum-distance,minimum-padding,"
<< "avoid-edges,allow-overlap,opacity,max-char-angle-delta,"
<< "horizontal-alignment,justify-alignment,"
// additional for shield
/* transform instead of orientation */
<< "file,base,transform,shield-dx,shield-dy,"
<< "text-opacity,unlock-image,no-text,"
<< "meta-writer,meta-output";
std::string s_symbolizer(s_common + ",file,base,"
"transform,shield-dx,shield-dy,text-opacity,"
"unlock-image"
"placements,placement-type,meta-writer,meta-output");
ensure_attrs(sym, "ShieldSymbolizer", s.str());
ensure_attrs(sym, "ShieldSymbolizer", s_symbolizer);
try
{
shield_symbolizer shield_symbol = shield_symbolizer();
text_placements_ptr placement_finder;
text_placements_list *list = 0;
optional<std::string> placement_type = get_opt_attr<std::string>(sym, "placement-type");
if (placement_type) {
if (*placement_type == "simple") {
placement_finder = text_placements_ptr(
new text_placements_simple(
get_attr<std::string>(sym, "placements", "X")));
} else if (*placement_type == "list") {
list = new text_placements_list();
placement_finder = text_placements_ptr(list);
} else if (*placement_type != "dummy" && *placement_type != "") {
throw config_error(std::string("Unknown placement type '"+*placement_type+"'"));
}
}
if (!placement_finder) {
placement_finder = text_placements_ptr(new text_placements_dummy());
}
placement_finder->properties.from_xml(sym, fontsets_);
if (strict_) ensure_font_face(placement_finder->properties.processor.defaults.face_name);
if (list) {
ptree::const_iterator symIter = sym.begin();
ptree::const_iterator endSym = sym.end();
for( ;symIter != endSym; ++symIter) {
if (symIter->first.find('<') != std::string::npos) continue;
if (symIter->first != "Placement")
{
// throw config_error("Unknown element '" + symIter->first + "'"); TODO
continue;
}
ensure_attrs(symIter->second, "TextSymbolizer/Placement", s_common);
text_symbolizer_properties & p = list->add();
p.from_xml(symIter->second, fontsets_);
if (strict_) ensure_font_face(p.processor.defaults.face_name);
}
}
shield_symbolizer shield_symbol = shield_symbolizer(placement_finder);
/* Symbolizer specific attributes. */
optional<std::string> transform_wkt = get_opt_attr<std::string>(sym, "transform");
if (transform_wkt)
{
@ -1378,14 +1417,6 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
shield_symbol.set_unlock_image( * unlock_image );
}
// no text
optional<boolean> no_text =
get_opt_attr<boolean>(sym, "no-text");
if (no_text)
{
shield_symbol.set_no_text( * no_text );
}
parse_metawriter_in_symbolizer(shield_symbol, sym);
std::string image_file = get_attr<std::string>(sym, "file");
@ -1418,8 +1449,6 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
std::clog << "### WARNING: " << msg << endl;
}
}
text_placements_ptr placement_finder = shield_symbol.get_placement_options();
placement_finder->properties.from_xml(sym, fontsets_);
rule.append(shield_symbol);
}
catch (const config_error & ex)

View file

@ -103,6 +103,7 @@ placement_finder<DetectorT>::placement_finder(text_placement_info &placement_inf
dimensions_(detector_.extent()),
info_(info), p(placement_info.properties), pi(placement_info), string_width_(0), string_height_(0), first_line_space_(0), valign_(V_AUTO), halign_(H_AUTO), line_breaks_(), line_sizes_()
{
placement_info.placements.clear(); //Remove left overs
}
template <typename DetectorT>
@ -111,6 +112,7 @@ placement_finder<DetectorT>::placement_finder(text_placement_info &placement_inf
dimensions_(extent),
info_(info), p(placement_info.properties), pi(placement_info), string_width_(0), string_height_(0), first_line_space_(0), valign_(V_AUTO), halign_(H_AUTO), line_breaks_(), line_sizes_()
{
placement_info.placements.clear(); //Remove left overs
}
template <typename DetectorT>
@ -996,53 +998,6 @@ void placement_finder<DetectorT>::clear()
detector_.clear();
}
template <typename DetectorT>
void placement_finder<DetectorT>::find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans)
{
double label_x=0.0;
double label_y=0.0;
double z=0.0;
if (p.label_placement == POINT_PLACEMENT ||
p.label_placement == VERTEX_PLACEMENT ||
p.label_placement == INTERIOR_PLACEMENT)
{
unsigned iterations = 1;
if (p.label_placement == VERTEX_PLACEMENT)
{
iterations = geom.num_points();
geom.rewind(0);
}
for(unsigned jj = 0; jj < iterations; jj++) {
switch (p.label_placement)
{
case POINT_PLACEMENT:
geom.label_position(&label_x, &label_y);
break;
case INTERIOR_PLACEMENT:
geom.label_interior_position(&label_x, &label_y);
break;
case VERTEX_PLACEMENT:
geom.vertex(&label_x, &label_y);
break;
case LINE_PLACEMENT:
case label_placement_enum_MAX:
/*not handled here*/
break;
}
prj_trans.backward(label_x, label_y, z);
t.forward(&label_x, &label_y);
find_point_placement(label_x, label_y, angle);
}
update_detector();
} else if (p.label_placement == LINE_PLACEMENT && geom.num_points() > 1)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
path_type path(t, geom, prj_trans);
find_line_placements<path_type>(path);
}
}
typedef coord_transform2<CoordTransform,geometry_type> PathType;
typedef label_collision_detector4 DetectorType;

351
src/symbolizer_helpers.cpp Normal file
View file

@ -0,0 +1,351 @@
#include <mapnik/symbolizer_helpers.hpp>
namespace mapnik {
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_placement()
{
if (!placement_valid_) return text_placement_info_ptr();
if (point_placement_)
return get_point_placement();
else
return get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_line_placement()
{
while (geometries_to_process_.size())
{
if (geo_itr_ == geometries_to_process_.end())
{
//Just processed the last geometry. Try next placement.
if (!next_placement()) return text_placement_info_ptr(); //No more placements
//Start again from begin of list
geo_itr_ = geometries_to_process_.begin();
continue; //Reexecute size check
}
//TODO: Avoid calling constructor repeatedly
placement_finder<DetectorT> finder(*placement_, *info_, detector_, dims_);
typedef coord_transform2<CoordTransform,geometry_type> path_type;
path_type path(t_, **geo_itr_, prj_trans_);
finder.find_line_placements(path);
//Keep reference to current object so we can delete it.
std::list<geometry_type*>::iterator current_object = geo_itr_;
geo_itr_++;
if (placement_->placements.size())
{
//Found a placement
geometries_to_process_.erase(current_object);
if (writer_.first) writer_.first->add_text(
*placement_, font_manager_,
feature_, t_, writer_.second);
return placement_;
}
//No placement for this geometry. Keep it in geometries_to_process_ for next try.
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_point_placement()
{
while (points_.size())
{
if (point_itr_ == points_.end())
{
//Just processed the last point. Try next placement.
if (!next_placement()) return text_placement_info_ptr(); //No more placements
//Start again from begin of list
point_itr_ = points_.begin();
continue; //Reexecute size check
}
placement_finder<DetectorT> finder(*placement_, *info_, detector_, dims_);
finder.find_point_placement(point_itr_->first, point_itr_->second, angle_);
//Keep reference to current object so we can delete it.
std::list<point_type>::iterator current_object = point_itr_;
point_itr_++;
if (placement_->placements.size())
{
//Found a placement
points_.erase(current_object);
if (writer_.first) writer_.first->add_text(
*placement_, font_manager_,
feature_, t_, writer_.second);
finder.update_detector();
return placement_;
}
//No placement for this point. Keep it in points_ for next try.
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries()
{
unsigned num_geom = feature_.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature_.get_geometry(i);
// don't bother with empty geometries
if (geom.num_points() == 0) continue;
if ((geom.type() == Polygon) && sym_.get_minimum_path_length() > 0)
{
// TODO - find less costly method than fetching full envelope
box2d<double> gbox = t_.forward(geom.envelope(), prj_trans_);
if (gbox.width() < sym_.get_minimum_path_length())
{
continue;
}
}
// TODO - calculate length here as well
geometries_to_process_.push_back(const_cast<geometry_type*>(&geom));
}
geo_itr_ = geometries_to_process_.begin();
}
template <typename FaceManagerT, typename DetectorT>
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points()
{
label_placement_enum how_placed = placement_->properties.label_placement;
if (how_placed == LINE_PLACEMENT) {
point_placement_ = false;
return;
} else {
point_placement_ = true;
}
double label_x=0.0;
double label_y=0.0;
double z=0.0;
std::list<geometry_type*>::const_iterator itr = geometries_to_process_.begin();
std::list<geometry_type*>::const_iterator end = geometries_to_process_.end();
for (; itr != end; itr++)
{
geometry_type const& geom = **itr;
if (how_placed == VERTEX_PLACEMENT)
{
geom.rewind(0);
for(unsigned i = 0; i < geom.num_points(); i++)
{
geom.vertex(&label_x, &label_y);
prj_trans_.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
points_.push_back(std::make_pair(label_x, label_y));
}
} else {
if (how_placed == POINT_PLACEMENT)
{
geom.label_position(&label_x, &label_y);
} else if (how_placed == INTERIOR_PLACEMENT)
{
geom.label_interior_position(&label_x, &label_y);
} else {
#ifdef MAPNIK_DEBUG
std::cerr << "ERROR: Unknown placement type in initialize_points();\n";
#endif
}
prj_trans_.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
points_.push_back(std::make_pair(label_x, label_y));
}
}
point_itr_ = points_.begin();
}
template <typename FaceManagerT, typename DetectorT>
bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_placement()
{
if (!placement_->next()) {
placement_valid_ = false;
return false;
}
placement_->properties.processor.process(text_, feature_);
info_ = &(text_.get_string_info());
if (placement_->properties.orientation)
{
angle_ = boost::apply_visitor(
evaluate<Feature, value_type>(feature_),
*(placement_->properties.orientation)).to_double();
} else {
angle_ = 0.0;
}
return true;
}
/*****************************************************************************/
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_placement()
{
if (!placement_valid_ || !marker_) return text_placement_info_ptr();
if (point_placement_)
return get_point_placement();
else
return get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_point_placement()
{
position const& shield_pos = sym_.get_shield_displacement();
while (points_.size())
{
if (point_itr_ == points_.end())
{
//Just processed the last point. Try next placement.
if (!next_placement()) return text_placement_info_ptr(); //No more placements
//Start again from begin of list
point_itr_ = points_.begin();
continue; //Reexecute size check
}
position const& pos = placement_->properties.displacement;
double label_x = point_itr_->first + boost::get<0>(shield_pos);
double label_y = point_itr_->second + boost::get<1>(shield_pos);
placement_finder<DetectorT> finder(*placement_, *info_, detector_, dims_);
finder.find_point_placement(label_x, label_y, angle_);
//Keep reference to current object so we can delete it.
std::list<point_type>::iterator current_object = point_itr_;
point_itr_++;
if (!placement_->placements.size())
{
//No placement for this point. Keep it in points_ for next try.
continue;
}
//Found a label placement but not necessarily also a marker placement
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
double x = floor(placement_->placements[0].starting_x);
double y = floor(placement_->placements[0].starting_y);
if (!sym_.get_unlock_image())
{
// center image at text center position
// remove displacement from image label
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
marker_x_ = int(floor(lx - (0.5 * marker_w_))) + 1;
marker_y_ = int(floor(ly - (0.5 * marker_h_))) + 1;
marker_ext_.re_center(lx, ly);
}
else
{ // center image at reference location
marker_x_ = int(floor(label_x - 0.5 * marker_w_));
marker_y_ = int(floor(label_y - 0.5 * marker_h_));
marker_ext_.re_center(label_x, label_y);
}
if (placement_->properties.allow_overlap || detector_.has_placement(marker_ext_))
{
detector_.insert(marker_ext_);
finder.update_detector();
if (writer_.first) {
writer_.first->add_box(marker_ext_, feature_, t_, writer_.second);
writer_.first->add_text(*placement_, font_manager_, feature_, t_, writer_.second);
}
points_.erase(current_object);
return placement_;
}
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_line_placement()
{
#if 0
TODO: Not supported by placement_finder atm
position const& pos = placement_->properties.displacement;
text_placement.additional_boxes.push_back(
box2d<double>(-0.5 * label_ext.width() - boost::get<0>(pos),
-0.5 * label_ext.height() - boost::get<1>(pos),
0.5 * label_ext.width() - boost::get<0>(pos),
0.5 * label_ext.height() - boost::get<1>(pos)));
#endif
return text_symbolizer_helper<FaceManagerT, DetectorT>::get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
void shield_symbolizer_helper<FaceManagerT, DetectorT>::init_marker()
{
std::string filename = path_processor_type::evaluate(*sym_.get_filename(), this->feature_);
boost::array<double,6> const& m = sym_.get_transform();
transform_.load_from(&m[0]);
marker_.reset();
if (!filename.empty())
{
marker_ = marker_cache::instance()->find(filename, true);
}
if (!marker_) {
marker_w_ = 0;
marker_h_ = 0;
marker_ext_.init(0, 0, 0, 0);
return;
}
marker_w_ = (*marker_)->width();
marker_h_ = (*marker_)->height();
double px0 = - 0.5 * marker_w_;
double py0 = - 0.5 * marker_h_;
double px1 = 0.5 * marker_w_;
double py1 = 0.5 * marker_h_;
double px2 = px1;
double py2 = py0;
double px3 = px0;
double py3 = py1;
transform_.transform(&px0,&py0);
transform_.transform(&px1,&py1);
transform_.transform(&px2,&py2);
transform_.transform(&px3,&py3);
marker_ext_.init(px0, py0, px1, py1);
marker_ext_.expand_to_include(px2, py2);
marker_ext_.expand_to_include(px3, py3);
}
template <typename FaceManagerT, typename DetectorT>
std::pair<int, int> shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker_position(text_path &p)
{
position const& pos = placement_->properties.displacement;
if (placement_->properties.label_placement == LINE_PLACEMENT) {
double x = floor(p.starting_x);
double y = floor(p.starting_y);
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
int px = int(floor(lx - (0.5*marker_w_))) + 1;
int py = int(floor(ly - (0.5*marker_h_))) + 1;
marker_ext_.re_center(lx, ly);
// detector_->insert(label_ext); //TODO: Is this done by placement_finder?
if (writer_.first) writer_.first->add_box(marker_ext_, feature_, t_, writer_.second);
return std::make_pair(px, py);
} else {
return std::make_pair(marker_x_, marker_y_);
}
}
template <typename FaceManagerT, typename DetectorT>
marker& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker() const
{
return **marker_;
}
template <typename FaceManagerT, typename DetectorT>
agg::trans_affine const& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_transform() const
{
return transform_;
}
template class text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>;
template class shield_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>;
} //namespace