Update symbolizer helpers for ShieldSymbolizer.
Correctly handle point placement for TextSymbolizer. (Tries each possible placement for each point).
This commit is contained in:
parent
2d50c840fe
commit
09459683e9
7 changed files with 524 additions and 406 deletions
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ source = Split(
|
|||
memory_datasource.cpp
|
||||
stroke.cpp
|
||||
symbolizer.cpp
|
||||
symbolizer_helpers.cpp
|
||||
arrow.cpp
|
||||
unicode.cpp
|
||||
markers_symbolizer.cpp
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
351
src/symbolizer_helpers.cpp
Normal 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
|
Loading…
Reference in a new issue