merge textplacement-merge into feature_impl

This commit is contained in:
Artem Pavlenko 2012-01-26 13:04:08 +00:00
parent ba674c89e9
commit d1f16bb227
118 changed files with 2556 additions and 3440 deletions

View File

@ -611,7 +611,6 @@ __all__ = [
'FontEngine',
'FontSet',
'Geometry2d',
'GlyphSymbolizer',
'Image',
'ImageView',
'Grid',

View File

@ -1,121 +0,0 @@
#include <boost/python.hpp>
#include <mapnik/glyph_symbolizer.hpp>
#include "mapnik_enumeration.hpp"
#include <boost/tuple/tuple.hpp>
using mapnik::glyph_symbolizer;
using mapnik::position;
using mapnik::enumeration_;
using mapnik::angle_mode_e;
using mapnik::AZIMUTH;
using mapnik::TRIGONOMETRIC;
using namespace boost::python;
namespace {
using namespace boost::python;
tuple get_displacement(const glyph_symbolizer& s)
{
boost::tuple<double,double> pos = s.get_displacement();
return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos));
}
void set_displacement(glyph_symbolizer & s, boost::python::tuple arg)
{
s.set_displacement(extract<double>(arg[0]),extract<double>(arg[1]));
}
}
void export_glyph_symbolizer()
{
enumeration_<angle_mode_e>("angle_mode")
.value("AZIMUTH", AZIMUTH)
.value("TRIGONOMETRIC", TRIGONOMETRIC)
;
class_<glyph_symbolizer>("GlyphSymbolizer",
init<std::string,mapnik::expression_ptr>())
.add_property("face_name",
make_function(&glyph_symbolizer::get_face_name,
return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_face_name,
"Get/Set the name of the font face (eg:\"DejaVu Sans "
"Book\") which contains the glyph"
)
.add_property("char",
&glyph_symbolizer::get_char,
&glyph_symbolizer::set_char,
"Get/Set the char expression. The char is the unicode "
"character indexing the glyph in the font referred by "
"face_name."
)
.add_property("allow_overlap",
&glyph_symbolizer::get_allow_overlap,
&glyph_symbolizer::set_allow_overlap,
"Get/Set the flag which controls if glyphs should "
"overlap any symbols previously rendered"
)
.add_property("avoid_edges",
&glyph_symbolizer::get_avoid_edges,
&glyph_symbolizer::set_avoid_edges,
"Get/Set the flag which controls if glyphs should be "
"partially drawn beside the edge of a tile."
)
.add_property("displacement",
&get_displacement,
&set_displacement)
.add_property("halo_fill",
make_function(&glyph_symbolizer::get_halo_fill,
return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_halo_fill)
.add_property("halo_radius",
&glyph_symbolizer::get_halo_radius,
&glyph_symbolizer::set_halo_radius)
.add_property("size",
&glyph_symbolizer::get_size,
&glyph_symbolizer::set_size,
"Get/Set the size expression used to size the glyph."
)
.add_property("angle",
&glyph_symbolizer::get_angle,
&glyph_symbolizer::set_angle,
"Get/Set the angle expression used to rotate the glyph "
"along its center."
)
.add_property("angle_mode",
&glyph_symbolizer::get_angle_mode,
&glyph_symbolizer::set_angle_mode,
"Get/Set the angle_mode property. This controls how the "
"angle is interpreted. Valid values are AZIMUTH and "
"TRIGONOMETRIC."
)
.add_property("value",
&glyph_symbolizer::get_value,
&glyph_symbolizer::set_value,
"Get/set the value expression which will be used to "
"retrieve a a value for the colorizer to use to choose "
"a color."
)
.add_property("color",
&glyph_symbolizer::get_color,
&glyph_symbolizer::set_color,
"Get/Set the color expression used to color the glyph. "
"(See also the 'colorizer' attribute)"
)
.add_property("colorizer",
&glyph_symbolizer::get_colorizer,
&glyph_symbolizer::set_colorizer,
"Get/Set the RasterColorizer used to color the glyph "
"depending on the 'value' expression (which must be "
"defined).\n"
"Only needed if no explicit color is provided"
)
;
}

View File

@ -66,7 +66,6 @@ void export_projection();
void export_proj_transform();
void export_view_transform();
void export_raster_colorizer();
void export_glyph_symbolizer();
void export_inmem_metawriter();
void export_label_collision_detector();
@ -84,6 +83,7 @@ void export_label_collision_detector();
#include <mapnik/scale_denominator.hpp>
#include <mapnik/value_error.hpp>
#include <mapnik/save_map.hpp>
#include <mapnik/scale_denominator.hpp>
#include "python_grid_utils.hpp"
#include "mapnik_value_converter.hpp"
#include "python_optional.hpp"
@ -434,7 +434,6 @@ BOOST_PYTHON_MODULE(_mapnik)
export_coord();
export_map();
export_raster_colorizer();
export_glyph_symbolizer();
export_inmem_metawriter();
export_label_collision_detector();

View File

@ -44,7 +44,6 @@ using mapnik::shield_symbolizer;
using mapnik::text_symbolizer;
using mapnik::building_symbolizer;
using mapnik::markers_symbolizer;
using mapnik::glyph_symbolizer;
using mapnik::symbolizer;
using mapnik::to_expression_string;
@ -162,7 +161,6 @@ void export_rule()
implicitly_convertible<raster_symbolizer,symbolizer>();
implicitly_convertible<shield_symbolizer,symbolizer>();
implicitly_convertible<text_symbolizer,symbolizer>();
implicitly_convertible<glyph_symbolizer,symbolizer>();
implicitly_convertible<markers_symbolizer,symbolizer>();
class_<rule::symbolizers>("Symbolizers",init<>("TODO"))

View File

@ -64,17 +64,6 @@ void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg)
t.set_displacement(extract<double>(arg[0]),extract<double>(arg[1]));
}
tuple get_anchor(const shield_symbolizer& t)
{
boost::tuple<double,double> pos = t.get_anchor();
return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos));
}
void set_anchor(shield_symbolizer & t, boost::python::tuple arg)
{
t.set_anchor(extract<double>(arg[0]),extract<double>(arg[1]));
}
const std::string get_filename(shield_symbolizer const& t)
{
return path_processor_type::to_string(*t.get_filename());
@ -137,9 +126,6 @@ void export_shield_symbolizer()
path_expression_ptr>("TODO")
)
//.def_pickle(shield_symbolizer_pickle_suite())
.add_property("anchor",
&get_anchor,
&set_anchor)
.add_property("allow_overlap",
&shield_symbolizer::get_allow_overlap,
&shield_symbolizer::set_allow_overlap,

View File

@ -39,7 +39,6 @@ using mapnik::shield_symbolizer;
using mapnik::text_symbolizer;
using mapnik::building_symbolizer;
using mapnik::markers_symbolizer;
using mapnik::glyph_symbolizer;
struct get_symbolizer_type : public boost::static_visitor<std::string>
{
@ -95,12 +94,6 @@ public:
{
return "markers";
}
std::string operator () ( const glyph_symbolizer & /*sym*/ )
{
return "glyph";
}
};
std::string get_symbol_type(const symbolizer& symbol)
@ -160,11 +153,6 @@ const markers_symbolizer& markers_( const symbolizer& symbol )
return boost::get<markers_symbolizer>(symbol);
}
const glyph_symbolizer& glyph_( const symbolizer& symbol )
{
return boost::get<glyph_symbolizer>(symbol);
}
void export_symbolizer()
{
using namespace boost::python;
@ -202,10 +190,6 @@ void export_symbolizer()
.def("markers",markers_,
return_value_policy<copy_const_reference>())
.def("glyph",glyph_,
return_value_policy<copy_const_reference>())
;
}

View File

@ -48,27 +48,6 @@ void set_text_displacement(text_symbolizer & t, boost::python::tuple arg)
t.set_displacement(extract<double>(arg[0]),extract<double>(arg[1]));
}
tuple get_anchor(const text_symbolizer& t)
{
position pos = t.get_anchor();
return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos));
}
void set_anchor(text_symbolizer & t, boost::python::tuple arg)
{
t.set_anchor(extract<double>(arg[0]),extract<double>(arg[1]));
}
void set_placement_options(text_symbolizer & t, placement_type_e arg, std::string const& placements)
{
t.set_placement_options(arg, placements);
}
void set_placement_options_2(text_symbolizer & t, placement_type_e arg)
{
t.set_placement_options(arg, "");
}
}
struct text_symbolizer_pickle_suite : boost::python::pickle_suite
@ -86,7 +65,6 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite
getstate(const text_symbolizer& t)
{
boost::python::tuple disp = get_text_displacement(t);
boost::python::tuple anchor = get_anchor(t);
// so we do not exceed max args accepted by make_tuple,
// lets put the increasing list of parameters in a list
@ -105,7 +83,7 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite
return boost::python::make_tuple(disp,t.get_label_placement(),
t.get_vertical_alignment(),t.get_halo_radius(),t.get_halo_fill(),t.get_text_ratio(),
t.get_wrap_width(),t.get_label_spacing(),t.get_minimum_distance(),t.get_allow_overlap(),
anchor,t.get_force_odd_labels(),t.get_max_char_angle_delta(),extras
t.get_force_odd_labels(),t.get_max_char_angle_delta(),extras
);
}
@ -146,15 +124,10 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite
t.set_allow_overlap(extract<bool>(state[9]));
tuple anch = extract<tuple>(state[10]);
double x = extract<double>(anch[0]);
double y = extract<double>(anch[1]);
t.set_anchor(x,y);
t.set_force_odd_labels(extract<bool>(state[10]));
t.set_force_odd_labels(extract<bool>(state[11]));
t.set_max_char_angle_delta(extract<double>(state[12]));
list extras = extract<list>(state[13]);
t.set_max_char_angle_delta(extract<double>(state[11]));
list extras = extract<list>(state[12]);
t.set_wrap_char_from_string(extract<std::string>(extras[0]));
t.set_line_spacing(extract<unsigned>(extras[1]));
t.set_character_spacing(extract<unsigned>(extras[2]));
@ -190,6 +163,7 @@ void export_text_symbolizer()
.value("LEFT",H_LEFT)
.value("MIDDLE",H_MIDDLE)
.value("RIGHT",H_RIGHT)
.value("AUTO",H_AUTO)
;
enumeration_<justify_alignment_e>("justify_alignment")
@ -205,11 +179,6 @@ void export_text_symbolizer()
.value("CAPITALIZE",CAPITALIZE)
;
enumeration_<placement_type_e>("placement_type")
.value("SIMPLE",T_SIMPLE)
.value("DUMMY",T_DUMMY)
;
class_<text_symbolizer>("TextSymbolizer",init<expression_ptr,std::string const&, unsigned,color const&>())
/*
// todo - all python classes can have kwargs and default constructors
@ -226,9 +195,6 @@ void export_text_symbolizer()
*/
//.def_pickle(text_symbolizer_pickle_suite())
.add_property("anchor",
&get_anchor,
&set_anchor)
.add_property("allow_overlap",
&text_symbolizer::get_allow_overlap,
&text_symbolizer::set_allow_overlap,
@ -302,10 +268,6 @@ void export_text_symbolizer()
&text_symbolizer::get_text_opacity,
&text_symbolizer::set_text_opacity,
"Set/get the text opacity")
.add_property("placement",
&text_symbolizer::get_label_placement,
&text_symbolizer::set_label_placement,
"Set/get the placement of the label")
.add_property("text_transform",
&text_symbolizer::get_text_transform,
&text_symbolizer::set_text_transform,
@ -329,9 +291,5 @@ void export_text_symbolizer()
.add_property("wrap_before",
&text_symbolizer::get_wrap_before,
&text_symbolizer::set_wrap_before)
.add_property("placement_type", &text_symbolizer::get_placement_type)
.add_property("placements", &text_symbolizer::get_placements)
.def("set_placement_options", set_placement_options)
.def("set_placement_options", set_placement_options_2)
;
}

24
docs/textrendering.gv Normal file
View File

@ -0,0 +1,24 @@
#process with: dot textrendering.gv -Tpng > textrendering.png
digraph textrendering {
# Classes without important virtual members: Round
# Classes with important virtual members: Rect
# Pointers [style=dashed]
# Red: function is called
text_placements[shape=box]
text_placement_info[shape=box]
Renderer
TextSymbolizer -> text_placements [label="placement_options_", style=dashed]
text_placements -> text_symbolizer_properties [label="properties"]
text_placements -> text_placement_info [label="get_placement_info()", style=dashed]
text_placement_info -> text_symbolizer_properties [label="properties"]
text_placement_info -> text_path [label="placements", style=dashed]
text_placement_info -> text_placement_info [label="next()"]
text_symbolizer_properties -> text_processor [label="processor"]
text_processor -> processed_text [label="process()", style=dashed]
processed_text -> string_info [label="get_string_info()"]
text_path -> Renderer [color=red, label="used by"]
Renderer -> text_placement_info [color=red, label="init()"]
Renderer -> processed_text [color=red, label="initializes"]
}

View File

@ -101,10 +101,7 @@ public:
proj_transform const& prj_trans);
void process(markers_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
void process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
proj_transform const& prj_trans);
inline bool process(rule::symbolizers const& /*syms*/,
Feature const& /*feature*/,
proj_transform const& /*prj_trans*/)

View File

@ -24,16 +24,11 @@
#define MAPNIK_ATTRIBUTE_COLLECTOR_HPP
// mapnik
#include <mapnik/feature_layer_desc.hpp>
#include <mapnik/rule.hpp>
#include <mapnik/path_expression_grammar.hpp>
#include <mapnik/parse_path.hpp>
// boost
#include <boost/utility.hpp>
#include <boost/variant.hpp>
#include <boost/concept_check.hpp>
// stl
#include <set>
#include <iostream>
@ -93,18 +88,12 @@ struct symbolizer_attributes : public boost::static_visitor<>
void operator () (text_symbolizer const& sym)
{
expression_ptr const& name_expr = sym.get_name();
if (name_expr)
std::set<expression_ptr>::const_iterator it;
std::set<expression_ptr> expressions = sym.get_placement_options()->get_all_expressions();
expression_attributes f_attr(names_);
for (it=expressions.begin(); it != expressions.end(); it++)
{
expression_attributes f_attr(names_);
boost::apply_visitor(f_attr,*name_expr);
}
expression_ptr const& orientation_expr = sym.get_orientation();
if (orientation_expr)
{
expression_attributes f_attr(names_);
boost::apply_visitor(f_attr,*orientation_expr);
if (*it) boost::apply_visitor(f_attr, **it);
}
collect_metawriter(sym);
}
@ -152,11 +141,12 @@ struct symbolizer_attributes : public boost::static_visitor<>
void operator () (shield_symbolizer const& sym)
{
expression_ptr const& name_expr = sym.get_name();
if (name_expr)
std::set<expression_ptr>::const_iterator it;
std::set<expression_ptr> expressions = sym.get_placement_options()->get_all_expressions();
expression_attributes f_attr(names_);
for (it=expressions.begin(); it != expressions.end(); it++)
{
expression_attributes name_attr(names_);
boost::apply_visitor(name_attr,*name_expr);
if (*it) boost::apply_visitor(f_attr, **it);
}
path_expression_ptr const& filename_expr = sym.get_filename();
@ -167,45 +157,6 @@ struct symbolizer_attributes : public boost::static_visitor<>
collect_metawriter(sym);
}
void operator () (glyph_symbolizer const& sym)
{
expression_ptr const& char_expr = sym.get_char();
if (char_expr)
{
expression_attributes f_attr(names_);
boost::apply_visitor(f_attr,*char_expr);
}
expression_ptr const& angle_expr = sym.get_angle();
if (angle_expr)
{
expression_attributes f_attr(names_);
boost::apply_visitor(f_attr,*angle_expr);
}
expression_ptr const& value_expr = sym.get_value();
if (value_expr)
{
expression_attributes f_attr(names_);
boost::apply_visitor(f_attr,*value_expr);
}
expression_ptr const& size_expr = sym.get_size();
if (size_expr)
{
expression_attributes f_attr(names_);
boost::apply_visitor(f_attr,*size_expr);
}
expression_ptr const& color_expr = sym.get_color();
if (color_expr)
{
expression_attributes f_attr(names_);
boost::apply_visitor(f_attr,*color_expr);
}
collect_metawriter(sym);
}
void operator () (markers_symbolizer const& sym)
{
collect_metawriter(sym);

View File

@ -111,9 +111,6 @@ public:
void process(markers_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
void process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
inline bool process(rule::symbolizers const& /*syms*/,
Feature const& /*feature*/,
proj_transform const& /*prj_trans*/)

View File

@ -19,23 +19,34 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
// mapnik
#include <mapnik/grid/grid_renderer.hpp>
#include <iostream>
#ifndef CHAR_INFO_HPP
#define CHAR_INFO_HPP
#include <boost/shared_ptr.hpp>
namespace mapnik {
struct char_properties;
template <typename T>
void grid_renderer<T>::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
std::clog << "grid_renderer does not yet support glyph_symbolizer\n";
}
class char_info {
public:
char_info(unsigned c_, double width_, double ymax_, double ymin_, double line_height_)
: c(c_), width(width_), line_height(line_height_), ymin(ymin_), ymax(ymax_)
{
}
char_info()
: c(0), width(0), line_height(0), ymin(0), ymax(0)
{
}
template void grid_renderer<grid>::process(glyph_symbolizer const&,
Feature const&,
proj_transform const&);
unsigned c;
double width;
double line_height;
double ymin;
double ymax;
double avg_height;
char_properties *format;
double height() const { return ymax-ymin; }
};
}
#endif

View File

@ -145,13 +145,6 @@ private:
class MAPNIK_DECL font_face_set : private boost::noncopyable
{
public:
class dimension_t {
public:
dimension_t(unsigned width_, int ymax_, int ymin_) : width(width_), height(ymax_-ymin_), ymin(ymin_) {}
unsigned width, height;
int ymin;
};
font_face_set(void)
: faces_() {}
@ -179,9 +172,9 @@ public:
return boost::make_shared<font_glyph>(*faces_.begin(), 0);
}
dimension_t character_dimensions(const unsigned c);
char_info character_dimensions(const unsigned c);
void get_string_info(string_info & info);
void get_string_info(string_info & info, UnicodeString const& ustr, char_properties *format);
void set_pixel_sizes(unsigned size)
{
@ -200,7 +193,7 @@ public:
}
private:
std::vector<face_ptr> faces_;
std::map<unsigned, dimension_t> dimension_cache_;
std::map<unsigned, char_info> dimension_cache_;
};
// FT_Stroker wrapper
@ -313,6 +306,18 @@ public:
return face_set;
}
face_set_ptr get_face_set(std::string const& name, font_set const& fset)
{
if (fset.size() > 0)
{
return get_face_set(fset);
}
else
{
return get_face_set(name);
}
}
stroker_ptr get_stroker()
{
return stroker_;
@ -330,247 +335,21 @@ struct text_renderer : private boost::noncopyable
struct glyph_t : boost::noncopyable
{
FT_Glyph image;
glyph_t(FT_Glyph image_) : image(image_) {}
char_properties *properties;
glyph_t(FT_Glyph image_, char_properties *properties_) : image(image_), properties(properties_) {}
~glyph_t () { FT_Done_Glyph(image);}
};
typedef boost::ptr_vector<glyph_t> glyphs_t;
typedef T pixmap_type;
text_renderer (pixmap_type & pixmap, face_set_ptr faces, stroker & s)
: pixmap_(pixmap),
faces_(faces),
stroker_(s),
fill_(0,0,0),
halo_fill_(255,255,255),
halo_radius_(0.0),
opacity_(1.0) {}
text_renderer (pixmap_type & pixmap, face_manager<freetype_engine> &font_manager_, stroker & s);
box2d<double> prepare_glyphs(text_path *path);
void render(double x0, double y0);
void render_id(int feature_id,double x0, double y0, double min_radius=1.0);
void set_pixel_size(unsigned size)
{
faces_->set_pixel_sizes(size);
}
void set_character_size(float size)
{
faces_->set_character_sizes(size);
}
void set_fill(mapnik::color const& fill)
{
fill_=fill;
}
void set_halo_fill(mapnik::color const& halo)
{
halo_fill_=halo;
}
void set_halo_radius( double radius=1.0)
{
halo_radius_=radius;
}
void set_opacity( double opacity=1.0)
{
opacity_=opacity;
}
box2d<double> prepare_glyphs(text_path *path)
{
//clear glyphs
glyphs_.clear();
FT_Matrix matrix;
FT_Vector pen;
FT_Error error;
FT_BBox bbox;
bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we
bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later
for (int i = 0; i < path->num_nodes(); i++)
{
int c;
double x, y, angle;
path->vertex(&c, &x, &y, &angle);
#ifdef MAPNIK_DEBUG
// TODO Enable when we have support for setting verbosity
//std::clog << "prepare_glyphs: " << c << "," << x <<
// "," << y << "," << angle << std::endl;
#endif
FT_BBox glyph_bbox;
FT_Glyph image;
pen.x = int(x * 64);
pen.y = int(y * 64);
glyph_ptr glyph = faces_->get_glyph(unsigned(c));
FT_Face face = glyph->get_face()->get_face();
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
FT_Set_Transform(face, &matrix, &pen);
error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING);
if ( error )
continue;
error = FT_Get_Glyph(face->glyph, &image);
if ( error )
continue;
FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox);
if (glyph_bbox.xMin < bbox.xMin)
bbox.xMin = glyph_bbox.xMin;
if (glyph_bbox.yMin < bbox.yMin)
bbox.yMin = glyph_bbox.yMin;
if (glyph_bbox.xMax > bbox.xMax)
bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax)
bbox.yMax = glyph_bbox.yMax;
// Check if we properly grew the bbox
if ( bbox.xMin > bbox.xMax )
{
bbox.xMin = 0;
bbox.yMin = 0;
bbox.xMax = 0;
bbox.yMax = 0;
}
// take ownership of the glyph
glyphs_.push_back(new glyph_t(image));
}
return box2d<double>(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);
}
void render(double x0, double y0)
{
FT_Error error;
FT_Vector start;
unsigned height = pixmap_.height();
start.x = static_cast<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((height - y0) * (1 << 6));
// now render transformed glyphs
typename glyphs_t::iterator pos;
//make sure we've got reasonable values.
if (halo_radius_ > 0.0 && halo_radius_ < 1024.0)
{
stroker_.init(halo_radius_);
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
{
FT_Glyph g;
error = FT_Glyph_Copy(pos->image, &g);
if (!error)
{
FT_Glyph_Transform(g,0,&start);
FT_Glyph_Stroke(&g,stroker_.get(),1);
error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1);
if ( ! error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)g;
render_bitmap(&bit->bitmap, halo_fill_.rgba(),
bit->left,
height - bit->top);
}
}
FT_Done_Glyph(g);
}
}
//render actual text
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
{
FT_Glyph_Transform(pos->image,0,&start);
error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1);
if ( ! error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
render_bitmap(&bit->bitmap, fill_.rgba(),
bit->left,
height - bit->top);
}
}
}
void render_id(int feature_id,double x0, double y0, double min_radius=1.0)
{
FT_Error error;
FT_Vector start;
unsigned height = pixmap_.height();
start.x = static_cast<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((height - y0) * (1 << 6));
// now render transformed glyphs
typename glyphs_t::iterator pos;
stroker_.init(std::max(halo_radius_,min_radius));
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
{
FT_Glyph g;
error = FT_Glyph_Copy(pos->image, &g);
if (!error)
{
FT_Glyph_Transform(g,0,&start);
FT_Glyph_Stroke(&g,stroker_.get(),1);
error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1);
//error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_MONO,0,1);
if ( ! error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)g;
render_bitmap_id(&bit->bitmap, feature_id,
bit->left,
height - bit->top);
}
}
FT_Done_Glyph(g);
}
}
private:
// unused currently, stroker is the new method for drawing halos
/*
void render_halo(FT_Bitmap *bitmap,unsigned rgba,int x,int y,int radius)
{
int x_max=x+bitmap->width;
int y_max=y+bitmap->rows;
int i,p,j,q;
for (i=x,p=0;i<x_max;++i,++p)
{
for (j=y,q=0;j<y_max;++j,++q)
{
int gray = bitmap->buffer[q*bitmap->width+p];
if (gray)
{
for (int n=-halo_radius_; n <=halo_radius_; ++n)
for (int m=-halo_radius_;m <= halo_radius_; ++m)
pixmap_.blendPixel2(i+m,j+n,rgba,gray,opacity_);
}
}
}
}
*/
void render_bitmap(FT_Bitmap *bitmap,unsigned rgba,int x,int y)
void render_bitmap(FT_Bitmap *bitmap, unsigned rgba, int x, int y, double opacity)
{
int x_max=x+bitmap->width;
int y_max=y+bitmap->rows;
@ -583,7 +362,7 @@ private:
int gray=bitmap->buffer[q*bitmap->width+p];
if (gray)
{
pixmap_.blendPixel2(i,j,rgba,gray,opacity_);
pixmap_.blendPixel2(i, j, rgba, gray, opacity);
}
}
}
@ -610,14 +389,11 @@ private:
}
pixmap_type & pixmap_;
face_set_ptr faces_;
face_manager<freetype_engine> &font_manager_;
stroker & stroker_;
color fill_;
color halo_fill_;
double halo_radius_;
glyphs_t glyphs_;
double opacity_;
};
typedef face_manager<freetype_engine> face_manager_freetype;
}
#endif // MAPNIK_FONT_ENGINE_FREETYPE_HPP

View File

@ -1,215 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_GLYPH_SYMBOLIZER_HPP
#define MAPNIK_GLYPH_SYMBOLIZER_HPP
// mapnik
#include <mapnik/enumeration.hpp>
#include <mapnik/raster_colorizer.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/color.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/symbolizer.hpp>
// boost
#include <boost/tuple/tuple.hpp>
namespace mapnik
{
typedef boost::tuple<double,double> position;
enum angle_mode_enum {
AZIMUTH,
TRIGONOMETRIC,
angle_mode_enum_MAX
};
DEFINE_ENUM(angle_mode_e, angle_mode_enum);
struct MAPNIK_DECL glyph_symbolizer : public symbolizer_base
{
glyph_symbolizer(std::string face_name, expression_ptr c)
: symbolizer_base(),
face_name_(face_name),
char_(c),
angle_(),
value_(),
size_(),
color_(),
colorizer_(),
allow_overlap_(false),
avoid_edges_(false),
displacement_(0.0, 0.0),
halo_fill_(color(255,255,255)),
halo_radius_(0),
angle_mode_(TRIGONOMETRIC) {}
std::string const& get_face_name() const
{
return face_name_;
}
void set_face_name(std::string face_name)
{
face_name_ = face_name;
}
expression_ptr get_char() const
{
return char_;
}
void set_char(expression_ptr c)
{
char_ = c;
}
bool get_allow_overlap() const
{
return allow_overlap_;
}
void set_allow_overlap(bool allow_overlap)
{
allow_overlap_ = allow_overlap;
}
bool get_avoid_edges() const
{
return avoid_edges_;
}
void set_avoid_edges(bool avoid_edges)
{
avoid_edges_ = avoid_edges;
}
void set_displacement(double x, double y)
{
displacement_ = boost::make_tuple(x,y);
}
position const& get_displacement() const
{
return displacement_;
}
void set_halo_fill(color const& fill)
{
halo_fill_ = fill;
}
color const& get_halo_fill() const
{
return halo_fill_;
}
void set_halo_radius(unsigned radius)
{
halo_radius_ = radius;
}
unsigned get_halo_radius() const
{
return halo_radius_;
}
expression_ptr get_angle() const
{
return angle_;
}
void set_angle(expression_ptr angle)
{
angle_ = angle;
}
expression_ptr get_value() const
{
return value_;
}
void set_value(expression_ptr value)
{
value_ = value;
}
expression_ptr get_size() const
{
return size_;
}
void set_size(expression_ptr size)
{
size_ = size;
}
expression_ptr get_color() const
{
return color_;
}
void set_color(expression_ptr color)
{
color_ = color;
}
raster_colorizer_ptr get_colorizer() const
{
return colorizer_;
}
void set_colorizer(raster_colorizer_ptr const& colorizer)
{
colorizer_ = colorizer;
}
void set_angle_mode(angle_mode_e angle_mode)
{
angle_mode_ = angle_mode;
}
angle_mode_e get_angle_mode() const
{
return angle_mode_;
}
text_path_ptr get_text_path(face_set_ptr const& faces,
Feature const& feature) const;
UnicodeString eval_char(Feature const& feature) const;
double eval_angle(Feature const& feature) const;
unsigned eval_size(Feature const& feature) const;
color eval_color(Feature const& feature) const;
private:
std::string face_name_;
expression_ptr char_;
expression_ptr angle_;
expression_ptr value_;
expression_ptr size_;
expression_ptr color_;
raster_colorizer_ptr colorizer_;
bool allow_overlap_;
bool avoid_edges_;
position displacement_;
color halo_fill_;
unsigned halo_radius_;
angle_mode_e angle_mode_;
};
} // end mapnik namespace
#endif // MAPNIK_GLYPH_SYMBOLIZER_HPP

View File

@ -24,7 +24,6 @@
#define MAPNIK_GRID_HPP
// mapnik
#include <mapnik/config.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/grid/grid_view.hpp>

View File

@ -98,9 +98,6 @@ public:
void process(markers_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
void process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
inline bool process(rule::symbolizers const& /*syms*/,
Feature const& /*feature*/,
proj_transform const& /*prj_trans*/)

View File

@ -27,8 +27,11 @@
#include <mapnik/global.hpp>
#include <ostream>
extern "C"
{
#include <stdio.h>
#include <jpeglib.h>
}

View File

@ -1,53 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_LABEL_PLACEMENT_HPP
#define MAPNIK_LABEL_PLACEMENT_HPP
namespace mapnik
{
struct point_
{
double x;
double y;
point_()
: x(0),y(0) {}
point_(double x_,double y_)
: x(x_),y(y_) {}
};
class label_placement
{
private:
point_ anchor_;
point_ displacement_;
double rotation_;
public:
label_placement()
: anchor_(),
displacement_(),
rotation_(0.0) {}
};
}
#endif // MAPNIK_LABEL_PLACEMENT_HPP

View File

@ -41,7 +41,7 @@
namespace mapnik {
struct placement;
class text_placement_info;
/** Implementation of std::map that also returns const& for operator[]. */
class metawriter_property_map
@ -103,8 +103,8 @@ public:
virtual void add_box(box2d<double> const& box, Feature const& feature,
CoordTransform const& t,
metawriter_properties const& properties)=0;
virtual void add_text(placement const& placement,
face_set_ptr face,
virtual void add_text(text_placement_info const& placement,
face_manager_freetype &font_manager,
Feature const& feature,
CoordTransform const& t,
metawriter_properties const& properties)=0;

View File

@ -65,8 +65,8 @@ public:
virtual void add_box(box2d<double> const& box, Feature const& feature,
CoordTransform const& t,
metawriter_properties const& properties);
virtual void add_text(placement const& p,
face_set_ptr face,
virtual void add_text(text_placement_info const& p,
face_manager_freetype &font_manager,
Feature const& feature,
CoordTransform const& t,
metawriter_properties const& properties);

View File

@ -45,8 +45,8 @@ public:
virtual void add_box(box2d<double> const& box, Feature const& feature,
CoordTransform const& t,
metawriter_properties const& properties);
virtual void add_text(placement const& p,
face_set_ptr face,
virtual void add_text(text_placement_info const& p,
face_manager_freetype &font_manager,
Feature const& feature,
CoordTransform const& t,
metawriter_properties const& properties);

View File

@ -34,6 +34,7 @@
#include <vector>
#include <iostream>
#include <deque>
#include <algorithm>
namespace mapnik {

View File

@ -23,86 +23,34 @@
#ifndef MAPNIK_PLACEMENT_FINDER_HPP
#define MAPNIK_PLACEMENT_FINDER_HPP
// mapnik
#include <mapnik/ctrans.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/text_placements.hpp>
// stl
#include <queue>
namespace mapnik
{
typedef text_path placement_element;
struct placement : boost::noncopyable
{
placement(string_info & info_, shield_symbolizer const& sym, double scale_factor, unsigned w, unsigned h, bool has_dimensions_= false);
placement(string_info & info_, text_symbolizer const& sym, double scale_factor);
~placement();
string_info & info; // should only be used for finding placement. doesn't necessarily match placements.vertex() values
double scale_factor_;
label_placement_e label_placement;
std::queue< box2d<double> > envelopes;
//output
boost::ptr_vector<placement_element> placements;
int wrap_width;
bool wrap_before; // wraps text at wrap_char immediately before current word
unsigned char wrap_char;
int text_ratio;
int label_spacing; // distance between repeated labels on a single geometry
unsigned label_position_tolerance; //distance the label can be moved on the line to fit, if 0 the default is used
bool force_odd_labels; //Always try render an odd amount of labels
double max_char_angle_delta;
double minimum_distance;
double minimum_padding;
double minimum_path_length;
bool avoid_edges;
bool has_dimensions;
bool allow_overlap;
std::pair<double, double> dimensions;
bool collect_extents;
box2d<double> extents;
// additional boxes attached to the text labels which must also be
// placed in order for the text placement to succeed. e.g: shields.
std::vector<box2d<double> > additional_boxes;
};
template <typename DetectorT>
class placement_finder : boost::noncopyable
{
public:
placement_finder(DetectorT & detector);
placement_finder(DetectorT & detector, box2d<double> const& extent);
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
void find_point_placement(placement & p, text_placement_info_ptr po, double pos_x, double pos_y, double angle=0.0, unsigned line_spacing=0, unsigned character_spacing=0);
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
template <typename T>
void find_point_placements(placement & p, text_placement_info_ptr po, T & path);
void find_point_placements(T & path);
//Iterate over the given path, placing line-following labels with respect to label_spacing
template <typename T>
void find_line_placements(placement & p, text_placement_info_ptr po, T & path);
void find_line_placements(T & path);
void update_detector(placement & p);
//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();
@ -117,15 +65,14 @@ private:
// otherwise it will autodetect the orientation.
// If >= 50% of the characters end up upside down, it will be retried the other way.
// RETURN: 1/-1 depending which way up the string ends up being.
std::auto_ptr<placement_element> get_placement_offset(placement & p,
const std::vector<vertex2d> & path_positions,
std::auto_ptr<placement_element> get_placement_offset(const std::vector<vertex2d> & path_positions,
const std::vector<double> & path_distances,
int & orientation, unsigned index, double distance);
///Tests wether the given placement_element be placed without a collision
// Returns true if it can
// NOTE: This edits p.envelopes so it can be used afterwards (you must clear it otherwise)
bool test_placement(placement & p, const std::auto_ptr<placement_element> & current_placement, const int & orientation);
bool test_placement(const std::auto_ptr<placement_element> & current_placement, const int & orientation);
///Does a line-circle intersect calculation
// NOTE: Follow the strict pre conditions
@ -137,14 +84,26 @@ private:
const double &x1, const double &y1, const double &x2, const double &y2,
double &ix, double &iy);
void find_line_breaks();
void init_string_size();
void init_alignment();
void adjust_position(placement_element *current_placement, double label_x, double label_y);
///General Internals
DetectorT & detector_;
box2d<double> const& dimensions_;
string_info &info_;
text_symbolizer_properties &p;
text_placement_info &pi;
double string_width_;
double string_height_;
double first_line_space_;
vertical_alignment_e valign_;
horizontal_alignment_e halign_;
std::vector<unsigned> line_breaks_;
std::vector<std::pair<double, double> > line_sizes_;
};
}
#endif // MAPNIK_PLACEMENT_FINDER_HPP

View File

@ -258,7 +258,7 @@ T get(const boost::property_tree::ptree & node, const std::string & name, bool i
}
else
{
str = node.get_optional<std::string>(name );
str = node.get_optional<std::string>(name+".<xmltext>");
}
if ( str ) {
@ -289,7 +289,7 @@ inline color get(boost::property_tree::ptree const& node, std::string const& nam
}
else
{
str = node.get_optional<std::string>(name );
str = node.get_optional<std::string>(name+".<xmltext>");
}
if ( str )
@ -322,7 +322,7 @@ T get(const boost::property_tree::ptree & node, const std::string & name, bool i
}
else
{
str = node.get_optional<std::string>(name);
str = node.get_optional<std::string>(name+".<xmltext>");
}
if ( ! str ) {
@ -348,7 +348,18 @@ T get_value(const boost::property_tree::ptree & node, const std::string & name)
{
try
{
return node.get_value<T>();
/* NOTE: get_child works as long as there is only one child with that name.
If this function is used this used this condition must always be satisfied.
*/
return node.get_child("<xmltext>").get_value<T>();
}
catch (boost::property_tree::ptree_bad_path)
{
/* If the XML parser did not find any non-empty data element the is no
<xmltext> node. But we don't want to fail here but simply return a
default constructed value of the requested type.
*/
return T();
}
catch (...)
{
@ -369,7 +380,7 @@ boost::optional<T> get_optional(const boost::property_tree::ptree & node, const
}
else
{
str = node.get_optional<std::string>(name);
str = node.get_optional<std::string>(name+".<xmltext>");
}
boost::optional<T> result;
@ -403,7 +414,7 @@ inline boost::optional<color> get_optional(const boost::property_tree::ptree & n
}
else
{
str = node.get_optional<std::string>(name);
str = node.get_optional<std::string>(name+".<xmltext>");
}
boost::optional<color> result;

View File

@ -34,7 +34,6 @@
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/glyph_symbolizer.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/expression_string.hpp>
@ -109,11 +108,6 @@ inline bool operator==(markers_symbolizer const& lhs,
return (&lhs == &rhs);
}
inline bool operator==(glyph_symbolizer const& lhs,
glyph_symbolizer const& rhs)
{
return (&lhs == &rhs);
}
typedef boost::variant<point_symbolizer,
line_symbolizer,
line_pattern_symbolizer,
@ -123,8 +117,7 @@ typedef boost::variant<point_symbolizer,
shield_symbolizer,
text_symbolizer,
building_symbolizer,
markers_symbolizer,
glyph_symbolizer> symbolizer;
markers_symbolizer> symbolizer;

View File

@ -36,6 +36,8 @@ namespace mapnik
struct MAPNIK_DECL shield_symbolizer : public text_symbolizer,
public symbolizer_with_image
{
shield_symbolizer(text_placements_ptr placements = text_placements_ptr(
boost::make_shared<text_placements_dummy>()));
shield_symbolizer(expression_ptr name,
std::string const& face_name,
float size,

View File

@ -83,9 +83,6 @@ namespace mapnik
void process(markers_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
void process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
/*!
* @brief Overload that process the whole set of symbolizers of a rule.

View File

@ -96,10 +96,10 @@ public:
void set_opacity(float opacity);
float get_opacity() const;
protected:
symbolizer_with_image(path_expression_ptr filename);
symbolizer_with_image(path_expression_ptr filename = path_expression_ptr());
symbolizer_with_image(symbolizer_with_image const& rhs);
path_expression_ptr image_filename_;
float opacity_;
float image_opacity_;
transform_type matrix_;
};
}

View File

@ -0,0 +1,165 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef SYMBOLIZER_HELPERS_HPP
#define SYMBOLIZER_HELPERS_HPP
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/text_processing.hpp>
#include <mapnik/placement_finder.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/feature.hpp>
#include <boost/shared_ptr.hpp>
namespace mapnik {
template <typename FaceManagerT, typename DetectorT>
class text_symbolizer_helper
{
public:
text_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_()
{
}
text_placement_info_ptr get_placement(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
private:
bool initialize_geometries(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! */
// Use a boost::ptr_vector here instread of std::vector?
std::vector<geometry_type*> geometries_to_process_;
};
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_placement(
text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
if (!initialize_geometries(sym, feature, prj_trans)) return text_placement_info_ptr();
text_ = boost::shared_ptr<processed_text>(new processed_text(font_manager_, scale_factor_));
metawriter_with_properties writer = sym.get_metawriter();
box2d<double> dims(0, 0, width_, height_);
text_placement_info_ptr placement = sym.get_placement_options()->get_placement_info();
placement->init(scale_factor_, width_, height_);
if (writer.first)
placement->collect_extents = true;
unsigned num_geom = feature.num_geometries();
if (!num_geom) return text_placement_info_ptr(); //Nothing to do
while (placement->next())
{
text_processor &processor = placement->properties.processor;
text_symbolizer_properties const& p = placement->properties;
/* TODO: Simplify this. */
text_->clear();
processor.process(*text_, feature);
string_info &info = text_->get_string_info();
/* END TODO */
double angle = 0.0;
if (p.orientation)
{
angle = boost::apply_visitor(evaluate<Feature,value_type>(feature),*(p.orientation)).to_double();
}
placement_finder<DetectorT> finder(*placement, info, detector_, dims);
unsigned num_geom = feature.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() == 0) continue; // don't bother with empty geometries
finder.find_placement(angle, geom, t_, prj_trans);
if (!placement->placements.size())
continue;
if (writer.first) writer.first->add_text(*placement, font_manager_, feature, t_, writer.second);
return placement;
}
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
bool text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries(
text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
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));
}
if (!geometries_to_process_.size() > 0)
{
// early return to avoid significant overhead of rendering setup
return false;
}
return true;
}
}
#endif // SYMBOLIZER_HELPERS_HPP

View File

@ -23,6 +23,12 @@
#ifndef MAPNIK_TEXT_PATH_HPP
#define MAPNIK_TEXT_PATH_HPP
// mapnik
#include <mapnik/char_info.hpp>
//stl
#include <vector>
// boost
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
@ -32,41 +38,39 @@
namespace mapnik
{
struct character_info
{
int character;
double width, height;
character_info() : character(0), width(0), height(0) {}
character_info(int c_, double width_, double height_) : character(c_), width(width_), height(height_) {}
~character_info() {}
character_info(const character_info &ci)
: character(ci.character), width(ci.width), height(ci.height)
{
}
};
class string_info : private boost::noncopyable
{
protected:
typedef boost::ptr_vector<character_info> characters_t;
typedef std::vector<char_info> characters_t;
characters_t characters_;
UnicodeString const& text_;
double width_;
double height_;
UnicodeString text_;
bool is_rtl;
public:
string_info(UnicodeString const& text)
: text_(text),
width_(0),
height_(0),
is_rtl(false) {}
void add_info(int c, double width, double height)
: characters_(),
text_(text),
is_rtl(false)
{
characters_.push_back(new character_info(c, width, height));
}
string_info()
: characters_(),
text_(),
is_rtl(false)
{
}
void add_info(char_info const& info)
{
characters_.push_back(info);
}
void add_text(UnicodeString text)
{
text_ += text;
}
unsigned num_characters() const
@ -74,29 +78,25 @@ public:
return characters_.size();
}
void set_rtl(bool value) {is_rtl = value;}
bool get_rtl() const {return is_rtl;}
void set_rtl(bool value)
{
is_rtl = value;
}
bool get_rtl() const
{
return is_rtl;
}
character_info at(unsigned i) const
char_info const& at(unsigned i) const
{
return characters_[i];
}
character_info operator[](unsigned i) const
char_info const& operator[](unsigned i) const
{
return at(i);
}
void set_dimensions(double width, double height)
{
width_ = width;
height_ = height;
}
std::pair<double, double> get_dimensions() const
{
return std::pair<double, double>(width_, height_);
}
UnicodeString const& get_string() const
{
@ -108,69 +108,84 @@ public:
UChar break_char = '\n';
return (text_.indexOf(break_char) >= 0);
}
/** Resets object to initial state. */
void clear(void)
{
text_ = "";
characters_.clear();
}
};
struct text_path : boost::noncopyable
/** List of all characters and their positions and formats for a placement. */
class text_path : boost::noncopyable
{
struct character_node
{
int c;
double x, y, angle;
char_properties *format;
character_node(int c_, double x_, double y_, double angle_)
: c(c_), x(x_), y(y_), angle(angle_) {}
character_node(int c_, double x_, double y_, double angle_, char_properties *format_)
: c(c_), x(x_), y(y_), angle(angle_), format(format_) {}
~character_node() {}
void vertex(int *c_, double *x_, double *y_, double *angle_)
void vertex(int *c_, double *x_, double *y_, double *angle_, char_properties **format_)
{
*c_ = c;
*x_ = x;
*y_ = y;
*angle_ = angle;
*format_ = format;
}
};
int itr_;
public:
typedef std::vector<character_node> character_nodes_t;
character_nodes_t nodes_;
double starting_x;
double starting_y;
character_nodes_t nodes_;
int itr_;
std::pair<unsigned,unsigned> string_dimensions;
// std::pair<unsigned,unsigned> string_dimensions;
text_path()
: starting_x(0),
starting_y(0),
itr_(0) {}
//text_path(text_path const& other) :
// itr_(0),
// nodes_(other.nodes_),
// string_dimensions(other.string_dimensions)
//{}
: itr_(0),
starting_x(0),
starting_y(0)
{
}
~text_path() {}
void add_node(int c, double x, double y, double angle)
/** Adds a new char to the list. */
void add_node(int c, double x, double y, double angle, char_properties *format)
{
nodes_.push_back(character_node(c, x, y, angle));
nodes_.push_back(character_node(c, x, y, angle, format));
}
void vertex(int *c, double *x, double *y, double *angle)
/** Return node. Always returns a new node. Has no way to report that there are no more nodes. */
void vertex(int *c, double *x, double *y, double *angle, char_properties **format)
{
nodes_[itr_++].vertex(c, x, y, angle);
nodes_[itr_++].vertex(c, x, y, angle, format);
}
/** Start again at first node. */
void rewind()
{
itr_ = 0;
}
/** Number of nodes. */
int num_nodes() const
{
return nodes_.size();
}
/** Delete all nodes. */
void clear()
{
nodes_.clear();

View File

@ -24,20 +24,30 @@
#define MAPNIK_TEXT_PLACEMENTS_HPP
// mapnik
#include <mapnik/config.hpp>
#include <mapnik/enumeration.hpp>
#include <mapnik/color.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/text_processing.hpp>
// stl
#include <vector>
#include <string>
#include <queue>
#include <set>
// boost
#include <boost/tuple/tuple.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <boost/property_tree/ptree.hpp>
namespace mapnik {
class text_placements;
typedef text_path placement_element;
typedef boost::tuple<double,double> position;
enum label_placement_enum {
@ -82,89 +92,140 @@ enum justify_alignment
DEFINE_ENUM( justify_alignment_e, justify_alignment );
enum text_transform
/** Contains all text symbolizer properties which are not directly related to text formating. */
struct text_symbolizer_properties
{
NONE = 0,
UPPERCASE,
LOWERCASE,
CAPITALIZE,
text_transform_MAX
};
text_symbolizer_properties();
/** Load all values and also the ```processor``` object from XML ptree. */
void set_values_from_xml(boost::property_tree::ptree const &sym, std::map<std::string,font_set> const & fontsets);
/** Save all values to XML ptree (but does not create a new parent node!). */
void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_symbolizer_properties const &dfl=text_symbolizer_properties()) const;
DEFINE_ENUM( text_transform_e, text_transform );
enum placement_type
{
T_SIMPLE = 0,
T_DUMMY,
placement_type_MAX
};
DEFINE_ENUM( placement_type_e, placement_type );
class text_placements;
class text_placement_info : boost::noncopyable
{
public:
text_placement_info(text_placements const* parent);
/** Get next placement.
* This function is also called before the first placement is tried. */
virtual bool next()=0;
/** Get next placement position.
* This function is also called before the first position is used.
* Each class has to return at least one position!
* If this functions returns false the placement data should be considered invalid!
*/
virtual bool next_position_only()=0;
virtual ~text_placement_info() {}
/* NOTE: Values are public and non-virtual to avoid any performance problems. */
//Per symbolizer options
expression_ptr orientation;
position displacement;
float text_size;
label_placement_e label_placement;
horizontal_alignment_e halign;
justify_alignment_e jalign;
vertical_alignment_e valign;
/** distance between repeated labels on a single geometry */
unsigned label_spacing;
/** distance the label can be moved on the line to fit, if 0 the default is used */
unsigned label_position_tolerance;
bool avoid_edges;
double minimum_distance;
double minimum_padding;
double minimum_path_length;
double max_char_angle_delta;
/** Always try render an odd amount of labels */
bool force_odd_labels;
bool allow_overlap;
unsigned text_ratio;
unsigned wrap_width;
/** Contains everything related to text formating */
text_processor processor;
};
/** Generate a possible placement and store results of placement_finder.
* This placement has first to be tested by placement_finder to verify it
* can actually be used.
*/
class text_placement_info : boost::noncopyable
{
public:
/** Constructor. Takes the parent text_placements object as a parameter
* to read defaults from it. */
text_placement_info(text_placements const* parent);
/** Get next placement.
* This function is also called before the first placement is tried.
* Each class has to return at least one position!
* If this functions returns false the placement data should be
* considered invalid!
*/
virtual bool next()=0;
virtual ~text_placement_info() {}
/** Initialize values used by placement finder. Only has to be done once
* per object.
*/
void init(double scale_factor_,
unsigned w = 0, unsigned h = 0, bool has_dimensions_ = false);
/** Properties actually used by placement finder and renderer. Values in
* here are modified each time next() is called. */
text_symbolizer_properties properties;
/** Scale factor used by the renderer. */
double scale_factor;
/* TODO: Don't know what this is used for. */
bool has_dimensions;
/* TODO: Don't know what this is used for. */
std::pair<double, double> dimensions;
/** Set scale factor. */
void set_scale_factor(double factor) { scale_factor = factor; }
/** Get scale factor. */
double get_scale_factor() { return scale_factor; }
/** Get label spacing taking the scale factor into account. */
double get_actual_label_spacing() { return scale_factor * properties.label_spacing; }
/** Get minimum distance taking the scale factor into account. */
double get_actual_minimum_distance() { return scale_factor * properties.minimum_distance; }
/** Get minimum padding taking the scale factor into account. */
double get_actual_minimum_padding() { return scale_factor * properties.minimum_padding; }
/** Collect a bounding box of all texts placed. */
bool collect_extents;
//Output by placement finder
/** Bounding box of all texts placed. */
box2d<double> extents;
/* TODO */
std::queue< box2d<double> > envelopes;
/* TODO */
boost::ptr_vector<placement_element> placements;
};
typedef boost::shared_ptr<text_placement_info> text_placement_info_ptr;
/** This object handles the management of all TextSymbolizer properties. It can
* be used as a base class for own objects which implement new processing
* semantics. Basically this class just makes sure a pointer of the right
* class is returned by the get_placement_info call.
*/
class text_placements
{
public:
text_placements() :
text_size_(10), halign_(H_MIDDLE), jalign_(J_MIDDLE), valign_(V_MIDDLE) {}
text_placements();
/** Get a text_placement_info object to use in rendering.
* The returned object creates a list of settings which is
* used to try to find a placement and stores all
* information that is generated by
* the placement finder.
*
* This function usually is implemented as
* text_placement_info_ptr text_placements_XXX::get_placement_info() const
* {
* return text_placement_info_ptr(new text_placement_info_XXX(this));
* }
*/
virtual text_placement_info_ptr get_placement_info() const =0;
/** Get a list of all expressions used in any placement.
* This function is used to collect attributes.
*/
virtual std::set<expression_ptr> get_all_expressions();
virtual void set_default_text_size(float size) { text_size_ = size; }
float get_default_text_size() const { return text_size_; }
virtual void set_default_displacement(position const& displacement) { displacement_ = displacement;}
position const& get_default_displacement() { return displacement_; }
virtual void set_default_halign(horizontal_alignment_e const& align) { halign_ = align;}
horizontal_alignment_e const& get_default_halign() { return halign_; }
virtual void set_default_jalign(justify_alignment_e const& align) { jalign_ = align;}
justify_alignment_e const& get_default_jalign() { return jalign_; }
virtual void set_default_valign(vertical_alignment_e const& align) { valign_ = align;}
vertical_alignment_e const& get_default_valign() { return valign_; }
/** Destructor. */
virtual ~text_placements() {}
protected:
float text_size_;
position displacement_;
horizontal_alignment_e halign_;
justify_alignment_e jalign_;
vertical_alignment_e valign_;
friend class text_placement_info;
/** List of all properties used as the default for the subclasses. */
text_symbolizer_properties properties;
};
/** Pointer to object of class text_placements */
typedef boost::shared_ptr<text_placements> text_placements_ptr;
class text_placements_info_dummy;
/** Dummy placement algorithm. Always takes the default value. */
class MAPNIK_DECL text_placements_dummy: public text_placements
{
public:
@ -172,20 +233,20 @@ public:
friend class text_placement_info_dummy;
};
/** Placement info object for dummy placement algorithm. Always takes the default value. */
class MAPNIK_DECL text_placement_info_dummy : public text_placement_info
{
public:
text_placement_info_dummy(text_placements_dummy const* parent) : text_placement_info(parent),
state(0), position_state(0), parent_(parent) {}
state(0), parent_(parent) {}
bool next();
bool next_position_only();
private:
unsigned state;
unsigned position_state;
text_placements_dummy const* parent_;
};
} //namespace
#endif // MAPNIK_TEXT_PLACEMENTS_HPP

View File

@ -0,0 +1,61 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Hermann Kraus
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef TEXT_PLACEMENTS_LIST_HPP
#define TEXT_PLACEMENTS_LIST_HPP
#include <mapnik/text_placements.hpp>
namespace mapnik {
class text_placement_info_list;
/** Tries a list of placements. */
class text_placements_list: public text_placements
{
public:
text_placements_list();
text_placement_info_ptr get_placement_info() const;
virtual std::set<expression_ptr> get_all_expressions();
text_symbolizer_properties & add();
text_symbolizer_properties & get(unsigned i);
unsigned size() const;
private:
std::vector<text_symbolizer_properties> list_;
friend class text_placement_info_list;
};
/** List placement strategy.
* See parent class for documentation of each function. */
class text_placement_info_list : public text_placement_info
{
public:
text_placement_info_list(text_placements_list const* parent) :
text_placement_info(parent), state(0), parent_(parent) {}
bool next();
private:
unsigned state;
text_placements_list const* parent_;
};
} //namespace
#endif

View File

@ -67,8 +67,8 @@ public:
text_placement_info_simple(text_placements_simple const* parent) :
text_placement_info(parent), state(0), position_state(0), parent_(parent) {}
bool next();
protected:
bool next_position_only();
private:
unsigned state;
unsigned position_state;
text_placements_simple const* parent_;

View File

@ -0,0 +1,134 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_TEXT_PROCESSING_HPP
#define MAPNIK_TEXT_PROCESSING_HPP
#include <boost/property_tree/ptree.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/ctrans.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/enumeration.hpp>
#include <mapnik/filter_factory.hpp>
#include <list>
#include <vector>
#include <set>
namespace mapnik
{
enum text_transform
{
NONE = 0,
UPPERCASE,
LOWERCASE,
CAPITALIZE,
text_transform_MAX
};
DEFINE_ENUM( text_transform_e, text_transform );
struct char_properties
{
char_properties();
/** Construct object from XML. */
void set_values_from_xml(boost::property_tree::ptree const &sym, std::map<std::string,font_set> const & fontsets);
/** Write object to XML ptree. */
void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const &dfl=char_properties()) const;
std::string face_name;
font_set fontset;
float text_size;
double character_spacing;
double line_spacing; //Largest total height (fontsize+line_spacing) per line is chosen
double text_opacity;
bool wrap_before;
unsigned wrap_char;
text_transform_e text_transform; //Per expression
color fill;
color halo_fill;
double halo_radius;
};
class processed_expression
{
public:
processed_expression(char_properties const& properties, UnicodeString const& text) :
p(properties), str(text) {}
char_properties p;
UnicodeString str;
};
class processed_text
{
public:
processed_text(face_manager<freetype_engine> & font_manager, double scale_factor);
void push_back(processed_expression const& exp);
unsigned size() const { return expr_list_.size(); }
unsigned empty() const { return expr_list_.empty(); }
void clear();
typedef std::list<processed_expression> expression_list;
expression_list::const_iterator begin();
expression_list::const_iterator end();
string_info &get_string_info();
private:
expression_list expr_list_;
face_manager<freetype_engine> & font_manager_;
double scale_factor_;
string_info info_;
};
class abstract_token;
/** Stores formating information and uses this to produce formated text for a given feature. */
class text_processor
{
public:
text_processor();
/** Construct object from XML. */
void from_xml(boost::property_tree::ptree const& pt, std::map<std::string,font_set> const &fontsets);
/** Write object to XML ptree. */
void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_processor const& dfl) const;
/** Takes a feature and produces formated text as output.
* The output object has to be created by the caller and passed in for thread safety.
*/
void process(processed_text &output, Feature const& feature);
/** Automatically create processing instructions for a single expression. */
void set_old_style_expression(expression_ptr expr);
/** Add a new formating token. */
void push_back(abstract_token *token);
/** Get a list of all expressions used in any placement. This function is used to collect attributes. */
std::set<expression_ptr> get_all_expressions() const;
/** Default values for char_properties. */
char_properties defaults;
protected:
void from_xml_recursive(boost::property_tree::ptree const& pt, std::map<std::string,font_set> const &fontsets);
private:
std::list<abstract_token *> list_;
bool clear_on_write; //Clear list once
};
} /* namespace */
#endif

View File

@ -26,8 +26,6 @@
// mapnik
#include <mapnik/color.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/text_placements.hpp>
@ -38,6 +36,13 @@
// stl
#include <string>
// Warning disabled for the moment
#if (0 && __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define func_deprecated __attribute__ ((deprecated))
#else
#define func_deprecated
#endif
namespace mapnik
{
@ -45,6 +50,7 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base
{
// Note - we do not use boost::make_shared below as VC2008 and VC2010 are
// not able to compile make_shared used within a constructor
text_symbolizer(text_placements_ptr placements = text_placements_ptr(new text_placements_dummy));
text_symbolizer(expression_ptr name, std::string const& face_name,
float size, color const& fill,
text_placements_ptr placements = text_placements_ptr(new text_placements_dummy)
@ -54,73 +60,71 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base
);
text_symbolizer(text_symbolizer const& rhs);
text_symbolizer& operator=(text_symbolizer const& rhs);
expression_ptr get_name() const;
expression_ptr get_name() const func_deprecated;
void set_name(expression_ptr expr);
expression_ptr get_orientation() const; // orienation (rotation angle atm)
expression_ptr get_orientation() const func_deprecated; // orienation (rotation angle atm)
void set_orientation(expression_ptr expr);
unsigned get_text_ratio() const; // target ratio for text bounding box in pixels
unsigned get_text_ratio() const func_deprecated; // target ratio for text bounding box in pixels
void set_text_ratio(unsigned ratio);
unsigned get_wrap_width() const; // width to wrap text at, or trigger ratio
unsigned get_wrap_width() const func_deprecated; // width to wrap text at, or trigger ratio
void set_wrap_width(unsigned ratio);
unsigned char get_wrap_char() const; // character used to wrap lines
std::string get_wrap_char_string() const; // character used to wrap lines as std::string
unsigned char get_wrap_char() const func_deprecated; // character used to wrap lines
std::string get_wrap_char_string() const func_deprecated; // character used to wrap lines as std::string
void set_wrap_char(unsigned char character);
void set_wrap_char_from_string(std::string const& character);
text_transform_e get_text_transform() const; // text conversion on strings before display
text_transform_e get_text_transform() const func_deprecated; // text conversion on strings before display
void set_text_transform(text_transform_e convert);
unsigned get_line_spacing() const; // spacing between lines of text
unsigned get_line_spacing() const func_deprecated; // spacing between lines of text
void set_line_spacing(unsigned spacing);
unsigned get_character_spacing() const; // spacing between characters in text
unsigned get_character_spacing() const func_deprecated; // spacing between characters in text
void set_character_spacing(unsigned spacing);
unsigned get_label_spacing() const; // spacing between repeated labels on lines
unsigned get_label_spacing() const func_deprecated; // spacing between repeated labels on lines
void set_label_spacing(unsigned spacing);
unsigned get_label_position_tolerance() const; //distance the label can be moved on the line to fit, if 0 the default is used
unsigned get_label_position_tolerance() const func_deprecated; //distance the label can be moved on the line to fit, if 0 the default is used
void set_label_position_tolerance(unsigned tolerance);
bool get_force_odd_labels() const; // try render an odd amount of labels
bool get_force_odd_labels() const func_deprecated; // try render an odd amount of labels
void set_force_odd_labels(bool force);
double get_max_char_angle_delta() const; // maximum change in angle between adjacent characters
double get_max_char_angle_delta() const func_deprecated; // maximum change in angle between adjacent characters
void set_max_char_angle_delta(double angle);
float get_text_size() const;
float get_text_size() const func_deprecated;
void set_text_size(float size);
std::string const& get_face_name() const;
std::string const& get_face_name() const func_deprecated;
void set_face_name(std::string face_name);
font_set const& get_fontset() const;
font_set const& get_fontset() const func_deprecated;
void set_fontset(font_set const& fset);
color const& get_fill() const;
color const& get_fill() const func_deprecated;
void set_fill(color const& fill);
void set_halo_fill(color const& fill);
color const& get_halo_fill() const;
color const& get_halo_fill() const func_deprecated;
void set_halo_radius(double radius);
double get_halo_radius() const;
double get_halo_radius() const func_deprecated;
void set_label_placement(label_placement_e label_p);
label_placement_e get_label_placement() const;
label_placement_e get_label_placement() const func_deprecated;
void set_vertical_alignment(vertical_alignment_e valign);
vertical_alignment_e get_vertical_alignment() const;
void set_anchor(double x, double y);
position const& get_anchor() const;
vertical_alignment_e get_vertical_alignment() const func_deprecated;
void set_displacement(double x, double y);
void set_displacement(position const& p);
position const& get_displacement() const;
position const& get_displacement() const func_deprecated;
void set_avoid_edges(bool avoid);
bool get_avoid_edges() const;
bool get_avoid_edges() const func_deprecated;
void set_minimum_distance(double distance);
double get_minimum_distance() const;
double get_minimum_distance() const func_deprecated;
void set_minimum_padding(double distance);
double get_minimum_padding() const;
double get_minimum_padding() const func_deprecated;
void set_minimum_path_length(double size);
double get_minimum_path_length() const;
double get_minimum_path_length() const func_deprecated;
void set_allow_overlap(bool overlap);
bool get_allow_overlap() const;
bool get_allow_overlap() const func_deprecated;
void set_text_opacity(double opacity);
double get_text_opacity() const;
bool get_wrap_before() const; // wrap text at wrap_char immediately before current work
double get_text_opacity() const func_deprecated;
void set_wrap_before(bool wrap_before);
bool get_wrap_before() const func_deprecated; // wrap text at wrap_char immediately before current work
void set_horizontal_alignment(horizontal_alignment_e valign);
horizontal_alignment_e get_horizontal_alignment() const;
horizontal_alignment_e get_horizontal_alignment() const func_deprecated;
void set_justify_alignment(justify_alignment_e valign);
justify_alignment_e get_justify_alignment() const;
justify_alignment_e get_justify_alignment() const func_deprecated;
text_placements_ptr get_placement_options() const;
void set_placement_options(text_placements_ptr placement_options);
void set_placement_options(placement_type_e arg, std::string const& placements);
@ -128,32 +132,6 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base
std::string get_placements() const;
private:
expression_ptr name_;
expression_ptr orientation_;
std::string face_name_;
font_set fontset_;
unsigned text_ratio_;
unsigned wrap_width_;
unsigned char wrap_char_;
text_transform_e text_transform_;
unsigned line_spacing_;
unsigned character_spacing_;
unsigned label_spacing_;
unsigned label_position_tolerance_;
bool force_odd_labels_;
double max_char_angle_delta_;
color fill_;
color halo_fill_;
double halo_radius_;
label_placement_e label_p_;
position anchor_;
bool avoid_edges_;
double minimum_distance_;
double minimum_padding_;
double minimum_path_length_;
bool overlap_;
double text_opacity_;
bool wrap_before_;
text_placements_ptr placement_options_;
};
}

View File

@ -31,6 +31,7 @@
#include <mapnik/font_set.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/map.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>

View File

@ -1,100 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
// mapnik
#include <mapnik/agg_renderer.hpp>
// agg
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_scanline_u.h"
namespace mapnik {
template <typename T>
void agg_renderer<T>::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
face_set_ptr faces = font_manager_.get_face_set(sym.get_face_name());
stroker_ptr strk = font_manager_.get_stroker();
if (faces->size() > 0 && strk)
{
// Get x and y from geometry and translate to pixmap coords.
double x, y, z=0.0;
feature.get_geometry(0).label_position(&x, &y);
prj_trans.backward(x,y,z);
t_.forward(&x, &y);
text_renderer<T> ren(pixmap_, faces, *strk);
// set fill and halo colors
color fill = sym.eval_color(feature);
ren.set_fill(fill);
if (fill != color("transparent")) {
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
}
// set font size
float size = sym.eval_size(feature);
ren.set_character_size(size * scale_factor_);
faces->set_character_sizes(size * scale_factor_);
// Get and render text path
//
text_path_ptr path = sym.get_text_path(faces, feature);
// apply displacement
position pos = sym.get_displacement();
double dx = boost::get<0>(pos);
double dy = boost::get<1>(pos);
path->starting_x = x = x+dx;
path->starting_y = y = y+dy;
// Prepare glyphs to set internal state and calculate the marker's
// 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)))
{
// Placement is valid, render glyph and update detector.
ren.render(x, y);
detector_->insert(ext);
metawriter_with_properties writer = sym.get_metawriter();
if (writer.first) writer.first->add_box(ext, feature, t_, writer.second);
}
}
else
{
throw config_error(
"Unable to find specified font face in GlyphSymbolizer"
);
}
}
template void agg_renderer<image_32>::process(glyph_symbolizer const&,
Feature const&,
proj_transform const&);
}

View File

@ -27,6 +27,7 @@
#include <mapnik/agg_pattern_source.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/line_pattern_symbolizer.hpp>
// agg
#include "agg_basics.h"

View File

@ -24,6 +24,7 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/line_symbolizer.hpp>
// agg
#include "agg_basics.h"

View File

@ -30,6 +30,7 @@
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/markers_placement.hpp>
#include <mapnik/arrow.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"

View File

@ -24,6 +24,7 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/polygon_symbolizer.hpp>
// agg
#include "agg_basics.h"

View File

@ -45,6 +45,7 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
#if 0
typedef coord_transform2<CoordTransform,geometry_type> path_type;
@ -139,7 +140,7 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
string_info info(text);
faces->get_string_info(info);
faces->get_string_info(info, text, 0);
metawriter_with_properties writer = sym.get_metawriter();
@ -264,6 +265,7 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
}
}
}
#endif
}

View File

@ -24,231 +24,32 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/symbolizer_helpers.hpp>
namespace mapnik {
struct largest_bbox_comp
{
bool operator() (geometry_type const* g0, geometry_type const* g1) const
{
box2d<double> b0 = g0->envelope();
box2d<double> b1 = g1->envelope();
return b0.width()*b0.height() < b1.width()*b1.height();
}
};
typedef boost::tuple<double,double> point_type;
typedef std::vector<point_type> label_points_type;
void get_label_points(label_points_type & labels,
Feature const& feature,
text_symbolizer const& sym,
proj_transform const& prj_trans,
CoordTransform const& t
)
{
std::vector<geometry_type*> geometries_to_process;
unsigned num_geom = feature.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() == 0) continue; // don't bother with empty geometries
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;
}
}
geometries_to_process.push_back(const_cast<geometry_type*>(&geom));
}
vector<geometry_type*>::const_iterator largest = std::max_element(geometries_to_process.begin(),
geometries_to_process.end(),
largest_bbox_comp());
if (largest != geometries_to_process.end())
{
double z=0.0;
double label_x,label_y;
if (sym.get_label_placement() == POINT_PLACEMENT)
(*largest)->label_position(&label_x, &label_y);
else if (sym.get_label_placement() == INTERIOR_PLACEMENT)
(*largest)->label_interior_position(&label_x, &label_y);
else // default to bbox center
{
box2d<double> box = (*largest)->envelope();
coord2d c = box.center();
label_x = c.x;
label_y = c.y;
}
prj_trans.backward(label_x,label_y, z);
t.forward(&label_x,&label_y);
labels.push_back(boost::make_tuple(label_x,label_y));
}
/*
std::sort(geometries_to_process.begin(), geometries_to_process.end(), largest_bbox_first());
BOOST_FOREACH( geometry_type * g, geometries_to_process)
{
double z=0.0;
double label_x,label_y;
if (sym.get_label_placement() == POINT_PLACEMENT)
g->label_position(&label_x, &label_y);
else
g->label_interior_position(&label_x, &label_y);
prj_trans.backward(label_x,label_y, z);
t.forward(&label_x,&label_y);
labels.push_back(boost::make_tuple(label_x,label_y));
}
*/
}
template <typename T>
void agg_renderer<T>::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
label_points_type labels;
get_label_points(labels, feature, sym, prj_trans, t_);
if (labels.size() == 0) return;
typedef coord_transform2<CoordTransform,geometry_type> path_type;
/* This could also be a member of the renderer class, but I would have
to check if any of the variables changes and notify the helper.
It could be done at a later point, but for now keep the code simple.
*/
text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4> helper(width_, height_, scale_factor_, t_, font_manager_, *detector_);
//////////////////////////////////////////////////////////////////////
text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans);
expression_ptr name_expr = sym.get_name();
if (!name_expr) return;
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*name_expr);
UnicodeString text = result.to_unicode();
if ( sym.get_text_transform() == UPPERCASE)
if (!placement) return;
text_renderer<T> ren(pixmap_, font_manager_, *(font_manager_.get_stroker()));
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
text = text.toUpper();
}
else if ( sym.get_text_transform() == LOWERCASE)
{
text = text.toLower();
}
else if ( sym.get_text_transform() == CAPITALIZE)
{
text = text.toTitle(0);
}
if ( text.length() <= 0 ) return;
color const& fill = sym.get_fill();
face_set_ptr faces;
if (sym.get_fontset().size() > 0)
{
faces = font_manager_.get_face_set(sym.get_fontset());
}
else
{
faces = font_manager_.get_face_set(sym.get_face_name());
}
stroker_ptr strk = font_manager_.get_stroker();
if (!(faces->size() > 0 && strk))
{
throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'");
}
/////////////////////////
////////////// renderer
text_renderer<T> ren(pixmap_, faces, *strk);
ren.set_fill(fill);
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
ren.set_opacity(sym.get_text_opacity());
/////////////////////////
//////
box2d<double> dims(0,0,width_,height_);
placement_finder<label_collision_detector4> finder(*detector_,dims);
///////////
bool placement_found = false;
text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info();
while (!placement_found && placement_options->next())
{
ren.set_character_size(placement_options->text_size * scale_factor_);
string_info info(text);
faces->get_string_info(info);
metawriter_with_properties writer = sym.get_metawriter();
//BOOST_FOREACH( geometry_type * geom, geometries_to_process )
BOOST_FOREACH (point_type const& pt, labels)
{
//while (!placement_found && placement_options->next_position_only())
{
placement text_placement(info, sym, scale_factor_);
text_placement.avoid_edges = sym.get_avoid_edges();
if (writer.first)
text_placement.collect_extents =true; // needed for inmem metawriter
//if (sym.get_label_placement() == POINT_PLACEMENT ||
// sym.get_label_placement() == INTERIOR_PLACEMENT)
{
//double label_x=0.0;
//double label_y=0.0;
//double z=0.0;
//if (sym.get_label_placement() == POINT_PLACEMENT)
// geom->label_position(&label_x, &label_y);
//else
// geom->label_interior_position(&label_x, &label_y);
//prj_trans.backward(label_x,label_y, z);
//t_.forward(&label_x,&label_y);
double angle = 0.0;
expression_ptr angle_expr = sym.get_orientation();
if (angle_expr)
{
// apply rotation
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*angle_expr);
angle = result.to_double();
}
finder.find_point_placement(text_placement, placement_options, boost::get<0>(pt),boost::get<1>(pt),
angle, sym.get_line_spacing(),
sym.get_character_spacing());
finder.update_detector(text_placement);
}
//else if ( geom->num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
// {
// path_type path(t_,*geom,prj_trans);
// finder.find_line_placements<path_type>(text_placement, placement_options, path);
// }
if (!text_placement.placements.size()) continue;
placement_found = true;
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii)
{
double x = text_placement.placements[ii].starting_x;
double y = text_placement.placements[ii].starting_y;
ren.prepare_glyphs(&text_placement.placements[ii]);
ren.render(x,y);
}
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);
}
}

View File

@ -147,11 +147,11 @@ source = Split(
symbolizer.cpp
arrow.cpp
unicode.cpp
glyph_symbolizer.cpp
markers_symbolizer.cpp
metawriter.cpp
raster_colorizer.cpp
text_placements.cpp
text_processing.cpp
wkt/wkt_factory.cpp
metawriter_inmem.cpp
metawriter_factory.cpp
@ -223,7 +223,6 @@ source += Split(
"""
agg/agg_renderer.cpp
agg/process_building_symbolizer.cpp
agg/process_glyph_symbolizer.cpp
agg/process_line_symbolizer.cpp
agg/process_line_pattern_symbolizer.cpp
agg/process_text_symbolizer.cpp
@ -244,7 +243,6 @@ source += Split(
"""
grid/grid_renderer.cpp
grid/process_building_symbolizer.cpp
grid/process_glyph_symbolizer.cpp
grid/process_line_pattern_symbolizer.cpp
grid/process_line_symbolizer.cpp
grid/process_markers_symbolizer.cpp
@ -264,7 +262,6 @@ if env['SVG_RENDERER']: # svg backend
svg/svg_output_attributes.cpp
svg/process_symbolizers.cpp
svg/process_building_symbolizer.cpp
svg/process_glyph_symbolizer.cpp
svg/process_line_pattern_symbolizer.cpp
svg/process_line_symbolizer.cpp
svg/process_markers_symbolizer.cpp

View File

@ -36,6 +36,7 @@
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/segment.hpp>
#include <mapnik/symbolizer_helpers.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/warp.hpp>
#include <mapnik/config.hpp>
@ -572,11 +573,7 @@ public:
void add_text(text_path & path,
cairo_face_manager & manager,
face_set_ptr const& faces,
unsigned text_size,
color const& fill,
unsigned halo_radius,
color const& halo_fill)
face_manager<freetype_engine> &font_manager)
{
double sx = path.starting_x;
double sy = path.starting_y;
@ -587,8 +584,13 @@ public:
{
int c;
double x, y, angle;
char_properties *format;
path.vertex(&c, &x, &y, &angle);
path.vertex(&c, &x, &y, &angle, &format);
face_set_ptr faces = font_manager.get_face_set(format->face_name, format->fontset);
float text_size = format->text_size;
faces->set_character_sizes(text_size);
glyph_ptr glyph = faces->get_glyph(c);
@ -608,42 +610,11 @@ public:
set_font_face(manager, glyph->get_face());
glyph_path(glyph->get_index(), sx + x, sy - y);
}
}
set_line_width(halo_radius);
set_line_join(ROUND_JOIN);
set_color(halo_fill);
stroke();
set_color(fill);
path.rewind();
for (int iii = 0; iii < path.num_nodes(); iii++)
{
int c;
double x, y, angle;
path.vertex(&c, &x, &y, &angle);
glyph_ptr glyph = faces->get_glyph(c);
if (glyph)
{
Cairo::Matrix matrix;
matrix.xx = text_size * cos(angle);
matrix.xy = text_size * sin(angle);
matrix.yx = text_size * -sin(angle);
matrix.yy = text_size * cos(angle);
matrix.x0 = 0;
matrix.y0 = 0;
set_font_matrix(matrix);
set_font_face(manager, glyph->get_face());
set_line_width(format->halo_radius);
set_line_join(ROUND_JOIN);
set_color(format->halo_fill);
stroke();
set_color(format->fill);
show_glyph(glyph->get_index(), sx + x, sy - y);
}
}
@ -1080,11 +1051,11 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
#if 0
typedef coord_transform2<CoordTransform,geometry_type> path_type;
text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info();
placement_options->next();
placement_options->next_position_only();
UnicodeString text;
if( sym.get_no_text() )
@ -1146,7 +1117,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
placement_finder<label_collision_detector4> finder(detector_);
faces->set_character_sizes(placement_options->text_size);
faces->get_string_info(info);
faces->get_string_info(info, text, 0);
int w = (*marker)->width();
int h = (*marker)->height();
@ -1280,6 +1251,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
}
}
}
#endif
}
void cairo_renderer_base::process(line_pattern_symbolizer const& sym,
@ -1459,187 +1431,21 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
}
}
void cairo_renderer_base::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
face_set_ptr faces = font_manager_.get_face_set(sym.get_face_name());
if (faces->size() > 0)
{
// Get x and y from geometry and translate to pixmap coords.
double x, y, z=0.0;
feature.get_geometry(0).label_position(&x, &y);
prj_trans.backward(x,y,z);
t_.forward(&x, &y);
// set font size
unsigned size = sym.eval_size(feature);
faces->set_character_sizes(size);
// Get and render text path
//
text_path_ptr path = sym.get_text_path(faces, feature);
// apply displacement
position pos = sym.get_displacement();
double dx = boost::get<0>(pos);
double dy = boost::get<1>(pos);
path->starting_x = x = x+dx;
path->starting_y = y = y+dy;
// get fill and halo params
color fill = sym.eval_color(feature);
color halo_fill = sym.get_halo_fill();
double halo_radius = sym.get_halo_radius();
if (fill==color("transparent"))
halo_radius = 0;
double bsize = size/2 + 1;
box2d<double> glyph_ext(
floor(x-bsize), floor(y-bsize), ceil(x+bsize), ceil(y+bsize)
);
if ((sym.get_allow_overlap() || detector_.has_placement(glyph_ext)) &&
(!sym.get_avoid_edges() || detector_.extent().contains(glyph_ext)))
{
// Placement is valid, render glyph and update detector.
cairo_context context(context_);
context.add_text(*path,
face_manager_,
faces,
size,
fill,
halo_radius,
halo_fill
);
detector_.insert(glyph_ext);
metawriter_with_properties writer = sym.get_metawriter();
if (writer.first) writer.first->add_box(glyph_ext, feature, t_, writer.second);
}
}
else
{
throw config_error(
"Unable to find specified font face in GlyphSymbolizer"
);
}
}
void cairo_renderer_base::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4> helper(detector_.extent().width(), detector_.extent().height(), 1.0 /*scale_factor*/, t_, font_manager_, detector_);
bool placement_found = false;
text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info();
while (!placement_found && placement_options->next())
text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans);
if (!placement) return;
cairo_context context(context_);
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
expression_ptr name_expr = sym.get_name();
if (!name_expr) return;
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*name_expr);
UnicodeString text = result.to_unicode();
if ( sym.get_text_transform() == UPPERCASE)
{
text = text.toUpper();
}
else if ( sym.get_text_transform() == LOWERCASE)
{
text = text.toLower();
}
else if ( sym.get_text_transform() == CAPITALIZE)
{
text = text.toTitle(NULL);
}
if (text.length() <= 0) continue;
face_set_ptr faces;
if (sym.get_fontset().size() > 0)
{
faces = font_manager_.get_face_set(sym.get_fontset());
}
else
{
faces = font_manager_.get_face_set(sym.get_face_name());
}
if (faces->size() == 0)
{
throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'");
}
cairo_context context(context_);
string_info info(text);
faces->set_character_sizes(placement_options->text_size);
faces->get_string_info(info);
placement_finder<label_collision_detector4> finder(detector_);
metawriter_with_properties writer = sym.get_metawriter();
unsigned num_geom = feature.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() == 0) continue;// don't bother with empty geometries
while (!placement_found && placement_options->next_position_only())
{
placement text_placement(info, sym, 1.0);
text_placement.avoid_edges = sym.get_avoid_edges();
if (writer.first)
text_placement.collect_extents = true; // needed for inmem metawriter
if (sym.get_label_placement() == POINT_PLACEMENT ||
sym.get_label_placement() == INTERIOR_PLACEMENT)
{
double label_x, label_y, z=0.0;
if (sym.get_label_placement() == POINT_PLACEMENT)
geom.label_position(&label_x, &label_y);
else
geom.label_interior_position(&label_x, &label_y);
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
double angle = 0.0;
expression_ptr angle_expr = sym.get_orientation();
if (angle_expr)
{
// apply rotation
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*angle_expr);
angle = result.to_double();
}
finder.find_point_placement(text_placement, placement_options,
label_x, label_y,
angle, sym.get_line_spacing(),
sym.get_character_spacing());
finder.update_detector(text_placement);
}
else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
{
path_type path(t_, geom, prj_trans);
finder.find_line_placements<path_type>(text_placement, placement_options, path);
}
if (!text_placement.placements.size()) continue;
placement_found = true;
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii)
{
context.add_text(text_placement.placements[ii],
face_manager_,
faces,
placement_options->text_size,
sym.get_fill(),
sym.get_halo_radius(),
sym.get_halo_fill()
);
}
if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second);
}
}
context.add_text(placement->placements[ii], face_manager_, font_manager_);
}
}

View File

@ -19,10 +19,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
// mapnik
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/text_placements.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/grid/grid.hpp>
// boost
#include <boost/algorithm/string.hpp>
@ -192,9 +194,10 @@ stroker_ptr freetype_engine::create_stroker()
return stroker_ptr();
}
font_face_set::dimension_t font_face_set::character_dimensions(const unsigned c)
char_info font_face_set::character_dimensions(const unsigned c)
{
std::map<unsigned, dimension_t>::const_iterator itr;
//Check if char is already in cache
std::map<unsigned, char_info>::const_iterator itr;
itr = dimension_cache_.find(c);
if (itr != dimension_cache_.end()) {
return itr->second;
@ -222,33 +225,30 @@ font_face_set::dimension_t font_face_set::character_dimensions(const unsigned c)
error = FT_Load_Glyph (face, glyph->get_index(), FT_LOAD_NO_HINTING);
if ( error )
return dimension_t(0, 0, 0);
return char_info();
error = FT_Get_Glyph(face->glyph, &image);
if ( error )
return dimension_t(0, 0, 0);
return char_info();
FT_Glyph_Get_CBox(image, ft_glyph_bbox_pixels, &glyph_bbox);
FT_Done_Glyph(image);
unsigned tempx = face->glyph->advance.x >> 6;
//std::clog << "glyph: " << glyph_index << " x: " << tempx << " y: " << tempy << std::endl;
dimension_t dim(tempx, glyph_bbox.yMax, glyph_bbox.yMin);
//dimension_cache_[c] = dim; would need an default constructor for dimension_t
dimension_cache_.insert(std::pair<unsigned, dimension_t>(c, dim));
char_info dim(c, tempx, glyph_bbox.yMax, glyph_bbox.yMin, face->size->metrics.height/64.0 /* >> 6 */);
dimension_cache_.insert(std::pair<unsigned, char_info>(c, dim));
return dim;
}
void font_face_set::get_string_info(string_info & info)
void font_face_set::get_string_info(string_info & info, UnicodeString const& ustr, char_properties *format)
{
unsigned width = 0;
unsigned height = 0;
double avg_height = character_dimensions('X').height();
UErrorCode err = U_ZERO_ERROR;
UnicodeString reordered;
UnicodeString shaped;
UnicodeString const& ustr = info.get_string();
int32_t length = ustr.length();
UBiDi *bidi = ubidi_openSized(length, 0, &err);
@ -270,10 +270,10 @@ void font_face_set::get_string_info(string_info & info)
StringCharacterIterator iter(shaped);
for (iter.setToStart(); iter.hasNext();) {
UChar ch = iter.nextPostInc();
dimension_t char_dim = character_dimensions(ch);
info.add_info(ch, char_dim.width, char_dim.height);
width += char_dim.width;
height = (char_dim.height > height) ? char_dim.height : height;
char_info char_dim = character_dimensions(ch);
char_dim.format = format;
char_dim.avg_height = avg_height;
info.add_info(char_dim);
}
}
@ -286,11 +286,198 @@ void font_face_set::get_string_info(string_info & info)
#endif
ubidi_close(bidi);
info.set_dimensions(width, height);
}
template <typename T>
text_renderer<T>::text_renderer (pixmap_type & pixmap, face_manager<freetype_engine> &font_manager_, stroker & s)
: pixmap_(pixmap),
font_manager_(font_manager_),
stroker_(s)
{
}
template <typename T>
box2d<double> text_renderer<T>::prepare_glyphs(text_path *path)
{
//clear glyphs
glyphs_.clear();
FT_Matrix matrix;
FT_Vector pen;
FT_Error error;
FT_BBox bbox;
bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we
bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later
for (int i = 0; i < path->num_nodes(); i++)
{
int c;
double x, y, angle;
char_properties *properties;
path->vertex(&c, &x, &y, &angle, &properties);
#ifdef MAPNIK_DEBUG
// TODO Enable when we have support for setting verbosity
//std::clog << "prepare_glyphs: " << c << "," << x <<
// "," << y << "," << angle << std::endl;
#endif
FT_BBox glyph_bbox;
FT_Glyph image;
pen.x = int(x * 64);
pen.y = int(y * 64);
face_set_ptr faces = font_manager_.get_face_set(properties->face_name, properties->fontset);
faces->set_character_sizes(properties->text_size);
glyph_ptr glyph = faces->get_glyph(unsigned(c));
FT_Face face = glyph->get_face()->get_face();
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
FT_Set_Transform(face, &matrix, &pen);
error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING);
if ( error )
continue;
error = FT_Get_Glyph(face->glyph, &image);
if ( error )
continue;
FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox);
if (glyph_bbox.xMin < bbox.xMin)
bbox.xMin = glyph_bbox.xMin;
if (glyph_bbox.yMin < bbox.yMin)
bbox.yMin = glyph_bbox.yMin;
if (glyph_bbox.xMax > bbox.xMax)
bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax)
bbox.yMax = glyph_bbox.yMax;
// Check if we properly grew the bbox
if ( bbox.xMin > bbox.xMax )
{
bbox.xMin = 0;
bbox.yMin = 0;
bbox.xMax = 0;
bbox.yMax = 0;
}
// take ownership of the glyph
glyphs_.push_back(new glyph_t(image, properties));
}
return box2d<double>(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);
}
template <typename T>
void text_renderer<T>::render(double x0, double y0)
{
FT_Error error;
FT_Vector start;
unsigned height = pixmap_.height();
start.x = static_cast<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((height - y0) * (1 << 6));
// now render transformed glyphs
typename glyphs_t::iterator pos;
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
{
double halo_radius = pos->properties->halo_radius;
//make sure we've got reasonable values.
if (halo_radius <= 0.0 || halo_radius > 1024.0) continue;
stroker_.init(halo_radius);
FT_Glyph g;
error = FT_Glyph_Copy(pos->image, &g);
if (!error)
{
FT_Glyph_Transform(g,0,&start);
FT_Glyph_Stroke(&g,stroker_.get(),1);
error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1);
if ( ! error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)g;
render_bitmap(&bit->bitmap, pos->properties->halo_fill.rgba(),
bit->left,
height - bit->top, pos->properties->text_opacity);
}
}
FT_Done_Glyph(g);
}
//render actual text
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
{
FT_Glyph_Transform(pos->image,0,&start);
error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1);
if ( ! error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
render_bitmap(&bit->bitmap, pos->properties->fill.rgba(),
bit->left,
height - bit->top, pos->properties->text_opacity);
}
}
}
template <typename T>
void text_renderer<T>::render_id(int feature_id,double x0, double y0, double min_radius)
{
FT_Error error;
FT_Vector start;
unsigned height = pixmap_.height();
start.x = static_cast<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((height - y0) * (1 << 6));
// now render transformed glyphs
typename glyphs_t::iterator pos;
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
{
stroker_.init(std::max(pos->properties->halo_radius, min_radius));
FT_Glyph g;
error = FT_Glyph_Copy(pos->image, &g);
if (!error)
{
FT_Glyph_Transform(g,0,&start);
FT_Glyph_Stroke(&g,stroker_.get(),1);
error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1);
//error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_MONO,0,1);
if ( ! error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)g;
render_bitmap_id(&bit->bitmap, feature_id,
bit->left,
height - bit->top);
}
}
FT_Done_Glyph(g);
}
}
#ifdef MAPNIK_THREADSAFE
boost::mutex freetype_engine::mutex_;
#endif
std::map<std::string,std::pair<int,std::string> > freetype_engine::name2file_;
template void text_renderer<image_32>::render(double, double);
template text_renderer<image_32>::text_renderer(image_32&, face_manager<freetype_engine>&, stroker&);
template box2d<double>text_renderer<image_32>::prepare_glyphs(text_path*);
template void text_renderer<grid>::render_id(int, double, double, double);
template text_renderer<grid>::text_renderer(grid&, face_manager<freetype_engine>&, stroker&);
template box2d<double>text_renderer<grid>::prepare_glyphs(text_path*);
}

View File

@ -1,149 +0,0 @@
#include <mapnik/glyph_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
namespace mapnik
{
static const char * angle_mode_strings[] = {
"azimuth",
"trigonometric",
""
};
IMPLEMENT_ENUM( angle_mode_e, angle_mode_strings )
text_path_ptr glyph_symbolizer::get_text_path(face_set_ptr const& faces,
Feature const& feature) const
{
// Try to evaulate expressions against feature
UnicodeString char_ = eval_char(feature);
double angle = eval_angle(feature);
// calculate displacement so glyph is rotated along center (default pivot is
// lowerbottom corner)
string_info info(char_);
faces->get_string_info(info);
// XXX: Perhaps this limitation can be overcomed?
if (info.num_characters() != 1)
{
throw config_error("'char' length must be exactly 1");
}
character_info ci = info.at(0);
font_face_set::dimension_t cdim = faces->character_dimensions(ci.character);
double cwidth = static_cast<double>(cdim.width)/2.0;
double cheight = static_cast<double>(cdim.height)/2.0;
double xoff = cwidth*cos(angle) - cheight*sin(angle);
double yoff = cwidth*sin(angle) + cheight*cos(angle);
// Create text path and add character with displacement and angle
text_path_ptr path_ptr = text_path_ptr(new text_path());
path_ptr->add_node(ci.character, -xoff, -yoff, angle);
return path_ptr;
}
UnicodeString glyph_symbolizer::eval_char(Feature const& feature) const
{
expression_ptr expr = get_char();
if (!expr)
throw config_error("No 'char' expression");
value_type result = boost::apply_visitor(
evaluate<Feature,value_type>(feature),
*expr
);
#ifdef MAPNIK_DEBUG
std::clog << "char_result=" << result.to_string() << "\n";
#endif
return result.to_unicode();
}
double glyph_symbolizer::eval_angle(Feature const& feature) const
{
double angle = 0.0;
expression_ptr expr = get_angle();
if (expr) {
value_type result = boost::apply_visitor(
evaluate<Feature,value_type>(feature),
*expr
);
#ifdef MAPNIK_DEBUG
std::clog << "angle_result=" << result.to_string() << "\n";
#endif
angle = result.to_double();
// normalize to first rotation in case an expression has made it go past
angle = std::fmod(angle, 360);
angle *= (M_PI/180); // convert to radians
if (get_angle_mode()==AZIMUTH) {
// angle is an azimuth, convert into trigonometric angle
angle = std::atan2(std::cos(angle), std::sin(angle));
}
if (angle<0)
angle += 2*M_PI;
}
return angle;
}
unsigned glyph_symbolizer::eval_size(Feature const& feature) const
{
expression_ptr expr = get_size();
if (!expr) throw config_error("No 'size' expression");
value_type result = boost::apply_visitor(
evaluate<Feature,value_type>(feature),
*expr
);
#ifdef MAPNIK_DEBUG
std::clog << "size_result=" << result.to_string() << "\n";
#endif
float size = static_cast<float>(result.to_double());
#ifdef MAPNIK_DEBUG
std::clog << "size=" << size << "\n";
#endif
return size;
}
color glyph_symbolizer::eval_color(Feature const& feature) const
{
raster_colorizer_ptr colorizer = get_colorizer();
if (colorizer)
{
expression_ptr value_expr = get_value();
if (!value_expr)
{
throw config_error(
"Must define a 'value' expression to use a colorizer"
);
}
value_type value_result = boost::apply_visitor(
evaluate<Feature,value_type>(feature),
*value_expr
);
#ifdef MAPNIK_DEBUG
std::clog << "value_result=" << value_result.to_string() << "\n";
#endif
return colorizer->get_color((float)value_result.to_double());
}
else
{
expression_ptr color_expr = get_color();
if (color_expr)
{
value_type color_result = boost::apply_visitor(
evaluate<Feature,value_type>(feature),
*color_expr
);
#ifdef MAPNIK_DEBUG
std::clog << "color_result=" << color_result.to_string() << "\n";
#endif
return color(color_result.to_string());
}
else
{
return color(0,0,0); // black
}
}
}
} // end mapnik namespace

View File

@ -28,6 +28,7 @@
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/placement_finder.hpp>
@ -35,10 +36,12 @@
#include <mapnik/font_set.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/map.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
// boost
#include <boost/utility.hpp>

View File

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/line_pattern_symbolizer.hpp>
// agg
#include "agg_rasterizer_scanline_aa.h"

View File

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/line_symbolizer.hpp>
// agg
#include "agg_rasterizer_scanline_aa.h"

View File

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>

View File

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>

View File

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/polygon_pattern_symbolizer.hpp>
// agg
#include "agg_rasterizer_scanline_aa.h"

View File

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/polygon_symbolizer.hpp>
// agg
#include "agg_rasterizer_scanline_aa.h"

View File

@ -44,6 +44,7 @@ void grid_renderer<T>::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
#if 0
typedef coord_transform2<CoordTransform,geometry_type> path_type;
bool placement_found = false;
@ -120,7 +121,7 @@ void grid_renderer<T>::process(shield_symbolizer const& sym,
string_info info(text);
faces->get_string_info(info);
faces->get_string_info(info, text, 0);
// TODO- clamp to at least 4 px otherwise interactivity is too small
int w = (*marker)->width()/pixmap_.get_resolution();
@ -237,7 +238,7 @@ void grid_renderer<T>::process(shield_symbolizer const& sym,
}
if (placement_found)
pixmap_.add_feature(feature);
#endif
}
template void grid_renderer<grid>::process(shield_symbolizer const&,

View File

@ -25,6 +25,7 @@
#include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/symbolizer_helpers.hpp>
namespace mapnik {
@ -33,120 +34,26 @@ void grid_renderer<T>::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
text_symbolizer_helper<face_manager<freetype_engine>,
label_collision_detector4> helper(
width_, height_,
scale_factor_ * (1.0/pixmap_.get_resolution()),
t_, font_manager_, detector_);
bool placement_found = false;
text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info();
while (!placement_found && placement_options->next())
text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans);
if (!placement) return;
text_renderer<T> ren(pixmap_, font_manager_, *(font_manager_.get_stroker()));
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
expression_ptr name_expr = sym.get_name();
if (!name_expr) return;
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*name_expr);
UnicodeString text = result.to_unicode();
if ( sym.get_text_transform() == UPPERCASE)
{
text = text.toUpper();
}
else if ( sym.get_text_transform() == LOWERCASE)
{
text = text.toLower();
}
else if ( sym.get_text_transform() == CAPITALIZE)
{
text = text.toTitle(NULL);
}
if ( text.length() <= 0 ) continue;
color const& fill = sym.get_fill();
face_set_ptr faces;
if (sym.get_fontset().size() > 0)
{
faces = font_manager_.get_face_set(sym.get_fontset());
}
else
{
faces = font_manager_.get_face_set(sym.get_face_name());
}
stroker_ptr strk = font_manager_.get_stroker();
if (!(faces->size() > 0 && strk))
{
throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'");
}
text_renderer<T> ren(pixmap_, faces, *strk);
ren.set_character_size(placement_options->text_size * (scale_factor_ * (1.0/pixmap_.get_resolution())));
ren.set_fill(fill);
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
ren.set_opacity(sym.get_text_opacity());
// /pixmap_.get_resolution() ?
box2d<double> dims(0,0,width_,height_);
placement_finder<label_collision_detector4> finder(detector_,dims);
string_info info(text);
faces->get_string_info(info);
unsigned num_geom = feature.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() == 0) continue; // don't bother with empty geometries
while (!placement_found && placement_options->next_position_only())
{
placement text_placement(info, sym, scale_factor_);
text_placement.avoid_edges = sym.get_avoid_edges();
if (sym.get_label_placement() == POINT_PLACEMENT ||
sym.get_label_placement() == INTERIOR_PLACEMENT)
{
double label_x, label_y, z=0.0;
if (sym.get_label_placement() == POINT_PLACEMENT)
geom.label_position(&label_x, &label_y);
else
geom.label_interior_position(&label_x, &label_y);
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
double angle = 0.0;
expression_ptr angle_expr = sym.get_orientation();
if (angle_expr)
{
// apply rotation
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*angle_expr);
angle = result.to_double();
}
finder.find_point_placement(text_placement, placement_options,
label_x, label_y,
angle, sym.get_line_spacing(),
sym.get_character_spacing());
finder.update_detector(text_placement);
}
else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
{
path_type path(t_,geom,prj_trans);
finder.find_line_placements<path_type>(text_placement, placement_options, path);
}
if (!text_placement.placements.size()) continue;
placement_found = true;
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii)
{
double x = text_placement.placements[ii].starting_x;
double y = text_placement.placements[ii].starting_y;
ren.prepare_glyphs(&text_placement.placements[ii]);
ren.render_id(feature.id(),x,y,2);
}
}
}
double x = placement->placements[ii].starting_x;
double y = placement->placements[ii].starting_y;
ren.prepare_glyphs(&(placement->placements[ii]));
ren.render_id(feature.id(),x,y,2);
}
if (placement_found)
pixmap_.add_feature(feature);
pixmap_.add_feature(feature);
}
template void grid_renderer<grid>::process(text_symbolizer const&,

View File

@ -29,6 +29,7 @@
#include <boost/utility.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <libxml/parser.h>
#include <libxml/tree.h>
@ -200,8 +201,13 @@ private:
}
break;
case XML_TEXT_NODE:
pt.put_value( (char*) cur_node->content );
break;
{
std::string trimmed = boost::algorithm::trim_copy(std::string((char*)cur_node->content));
if (trimmed.empty()) break;
ptree::iterator it = pt.push_back(ptree::value_type("<xmltext>", ptree()));
it->second.put_value(trimmed);
}
break;
case XML_COMMENT_NODE:
{
ptree::iterator it = pt.push_back(

View File

@ -48,6 +48,10 @@
#include <mapnik/metawriter_factory.hpp>
#include <mapnik/text_placements_simple.hpp>
#include <mapnik/text_placements_list.hpp>
#include <mapnik/text_processing.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/rule.hpp>
// boost
#include <boost/optional.hpp>
@ -108,7 +112,6 @@ private:
void parse_building_symbolizer(rule & rule, ptree const & sym );
void parse_raster_symbolizer(rule & rule, ptree const & sym );
void parse_markers_symbolizer(rule & rule, ptree const & sym );
void parse_glyph_symbolizer(rule & rule, ptree const & sym );
void parse_raster_colorizer(raster_colorizer_ptr const& rc, ptree const& node );
void parse_stroke(stroke & strk, ptree const & sym);
@ -653,7 +656,7 @@ void map_parser::parse_layer( Map & map, ptree const & lay )
if (child.first == "StyleName")
{
ensure_attrs(child.second, "StyleName", "none");
std::string style_name = child.second.data();
std::string style_name = get_value<std::string>(child.second, "style name");
if (style_name.empty())
{
std::ostringstream ss;
@ -846,10 +849,6 @@ void map_parser::parse_rule( feature_type_style & style, ptree const & r )
{
parse_markers_symbolizer(rule, sym.second);
}
else if ( sym.first == "GlyphSymbolizer")
{
parse_glyph_symbolizer( rule, sym.second );
}
else if ( sym.first != "MinScaleDenominator" &&
sym.first != "MaxScaleDenominator" &&
@ -1246,28 +1245,34 @@ void map_parser::parse_polygon_pattern_symbolizer( rule & rule,
void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
{
std::stringstream s;
s << "name,face-name,fontset-name,size,fill,orientation,"
std::stringstream s_common;
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,"
<< "placements,placement-type,"
<< "horizontal-alignment,justify-alignment";
std::stringstream s_symbolizer;
s_symbolizer << s_common.str() << ",placements,placement-type,"
<< "meta-writer,meta-output";
ensure_attrs(sym, "TextSymbolizer", s.str());
ensure_attrs(sym, "TextSymbolizer", s_symbolizer.str());
try
{
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+"'"));
}
@ -1276,214 +1281,25 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
placement_finder = text_placements_ptr(new text_placements_dummy());
}
std::string name;
optional<std::string> old_name = get_opt_attr<std::string>(sym, "name");
if (old_name) {
std::clog << ": ### WARNING: Using 'name' in TextSymbolizer is deprecated (http://trac.mapnik.org/wiki/TextSymbolizer)\n";
name = *old_name;
} else {
name = get_value<std::string>(sym, "TextSymbolizer");
if (name.empty()) throw config_error(std::string("TextSymbolizer needs a non-empty text"));
}
optional<std::string> face_name =
get_opt_attr<std::string>(sym, "face-name");
optional<std::string> fontset_name =
get_opt_attr<std::string>(sym, "fontset-name");
float size = get_attr(sym, "size", 10.0f);
color c = get_attr(sym, "fill", color(0,0,0));
text_symbolizer text_symbol = text_symbolizer(parse_expression(name, "utf8"), size, c, placement_finder);
optional<std::string> orientation = get_opt_attr<std::string>(sym, "orientation");
if (orientation)
{
text_symbol.set_orientation(parse_expression(*orientation, "utf8"));
}
if (fontset_name && face_name)
{
throw config_error(std::string("Can't have both face-name and fontset-name"));
}
else if (fontset_name)
{
std::map<std::string,font_set>::const_iterator itr = fontsets_.find(*fontset_name);
if (itr != fontsets_.end())
{
text_symbol.set_fontset(itr->second);
}
else
{
throw config_error("Unable to find any fontset named '" + *fontset_name + "'");
text_symbolizer text_symbol = text_symbolizer(placement_finder);
placement_finder->properties.set_values_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.str());
text_symbolizer_properties & p = list->add();
p.set_values_from_xml(symIter->second, fontsets_);
if (strict_) ensure_font_face(p.processor.defaults.face_name);
}
}
else if (face_name)
{
if ( strict_ )
{
ensure_font_face(*face_name);
}
text_symbol.set_face_name(*face_name);
}
else
{
throw config_error(std::string("Must have face-name or fontset-name"));
}
double dx = get_attr(sym, "dx", 0.0);
double dy = get_attr(sym, "dy", 0.0);
text_symbol.set_displacement(dx,dy);
label_placement_e placement =
get_attr<label_placement_e>(sym, "placement", POINT_PLACEMENT);
text_symbol.set_label_placement( placement );
// vertical alignment
vertical_alignment_e default_vertical_alignment = V_AUTO;
vertical_alignment_e valign = get_attr<vertical_alignment_e>(sym, "vertical-alignment", default_vertical_alignment);
text_symbol.set_vertical_alignment(valign);
// halo fill and radius
optional<color> halo_fill = get_opt_attr<color>(sym, "halo-fill");
if (halo_fill)
{
text_symbol.set_halo_fill( * halo_fill );
}
optional<double> halo_radius =
get_opt_attr<double>(sym, "halo-radius");
if (halo_radius)
{
text_symbol.set_halo_radius(*halo_radius);
}
// text ratio and wrap width
optional<unsigned> text_ratio =
get_opt_attr<unsigned>(sym, "text-ratio");
if (text_ratio)
{
text_symbol.set_text_ratio(*text_ratio);
}
optional<unsigned> wrap_width =
get_opt_attr<unsigned>(sym, "wrap-width");
if (wrap_width)
{
text_symbol.set_wrap_width(*wrap_width);
}
optional<boolean> wrap_before =
get_opt_attr<boolean>(sym, "wrap-before");
if (wrap_before)
{
text_symbol.set_wrap_before(*wrap_before);
}
// character used to break long strings
optional<std::string> wrap_char =
get_opt_attr<std::string>(sym, "wrap-character");
if (wrap_char && (*wrap_char).size() > 0)
{
text_symbol.set_wrap_char((*wrap_char)[0]);
}
// text conversion before rendering
text_transform_e tconvert =
get_attr<text_transform_e>(sym, "text-transform", NONE);
text_symbol.set_text_transform(tconvert);
// spacing between text lines
optional<unsigned> line_spacing = get_opt_attr<unsigned>(sym, "line-spacing");
if (line_spacing)
{
text_symbol.set_line_spacing(*line_spacing);
}
// tolerance between label spacing along line
optional<unsigned> label_position_tolerance = get_opt_attr<unsigned>(sym, "label-position-tolerance");
if (label_position_tolerance)
{
text_symbol.set_label_position_tolerance(*label_position_tolerance);
}
// spacing between characters in text
optional<unsigned> character_spacing = get_opt_attr<unsigned>(sym, "character-spacing");
if (character_spacing)
{
text_symbol.set_character_spacing(*character_spacing);
}
// spacing between repeated labels on lines
optional<unsigned> spacing = get_opt_attr<unsigned>(sym, "spacing");
if (spacing)
{
text_symbol.set_label_spacing(*spacing);
}
// minimum distance between labels
optional<unsigned> min_distance = get_opt_attr<unsigned>(sym, "minimum-distance");
if (min_distance)
{
text_symbol.set_minimum_distance(*min_distance);
}
// minimum distance from edge of the map
optional<unsigned> min_padding = get_opt_attr<unsigned>(sym, "minimum-padding");
if (min_padding)
{
text_symbol.set_minimum_padding(*min_padding);
}
// minimum path length
optional<unsigned> min_path_length = get_opt_attr<unsigned>(sym, "minimum-path-length");
if (min_path_length)
{
text_symbol.set_minimum_path_length(*min_path_length);
}
// do not render labels around edges
optional<boolean> avoid_edges =
get_opt_attr<boolean>(sym, "avoid-edges");
if (avoid_edges)
{
text_symbol.set_avoid_edges( * avoid_edges);
}
// allow_overlap
optional<boolean> allow_overlap =
get_opt_attr<boolean>(sym, "allow-overlap");
if (allow_overlap)
{
text_symbol.set_allow_overlap( * allow_overlap );
}
// opacity
optional<double> opacity =
get_opt_attr<double>(sym, "opacity");
if (opacity)
{
text_symbol.set_text_opacity( * opacity );
}
// max_char_angle_delta
optional<double> max_char_angle_delta =
get_opt_attr<double>(sym, "max-char-angle-delta");
if (max_char_angle_delta)
{
text_symbol.set_max_char_angle_delta( (*max_char_angle_delta)*(M_PI/180));
}
// horizontal alignment
horizontal_alignment_e halign = get_attr<horizontal_alignment_e>(sym, "horizontal-alignment", H_AUTO);
text_symbol.set_horizontal_alignment(halign);
// justify alignment
justify_alignment_e jalign = get_attr<justify_alignment_e>(sym, "justify-alignment", J_MIDDLE);
text_symbol.set_justify_alignment(jalign);
parse_metawriter_in_symbolizer(text_symbol, sym);
rule.append(text_symbol);
}
@ -1498,7 +1314,6 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
{
std::stringstream s;
//std::string a[] = {"a","b"};
s << "name,face-name,fontset-name,size,fill,"
<< "dx,dy,placement,vertical-alignment,halo-fill,"
<< "halo-radius,text-ratio,wrap-width,wrap-before,"
@ -1508,39 +1323,74 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
<< "avoid-edges,allow-overlap,opacity,max-char-angle-delta,"
<< "horizontal-alignment,justify-alignment,"
// additional for shield
/* transform instead of orientation */
/* transform instead of orientation */
<< "file,base,transform,shield-dx,shield-dy,"
<< "text-opacity,unlock-image,no-text,"
<< "meta-writer,meta-output";
<< "meta-writer,meta-output";
ensure_attrs(sym, "ShieldSymbolizer", s.str());
try
{
optional<boolean> no_text =
get_opt_attr<boolean>(sym, "no-text");
std::string name;
optional<std::string> old_name = get_opt_attr<std::string>(sym, "name");
if (old_name) {
std::clog << ": ### WARNING: Using 'name' in ShieldSymbolizer is deprecated (http://trac.mapnik.org/wiki/TextSymbolizer)\n";
name = *old_name;
} else {
name = get_value<std::string>(sym, "ShieldSymbolizer");
if (name.empty() && (!no_text || !*no_text) ) throw config_error(std::string("ShieldSymbolizer needs a non-empty text"));
shield_symbolizer shield_symbol = shield_symbolizer();
optional<std::string> transform_wkt = get_opt_attr<std::string>(sym, "transform");
if (transform_wkt)
{
agg::trans_affine tr;
if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr))
{
std::stringstream ss;
ss << "Could not parse transform from '" << transform_wkt << "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'";
if (strict_)
throw config_error(ss.str()); // value_error here?
else
std::clog << "### WARNING: " << ss << endl;
}
boost::array<double,6> matrix;
tr.store_to(&matrix[0]);
shield_symbol.set_transform(matrix);
}
// shield displacement
double shield_dx = get_attr(sym, "shield-dx", 0.0);
double shield_dy = get_attr(sym, "shield-dy", 0.0);
shield_symbol.set_shield_displacement(shield_dx,shield_dy);
// opacity
optional<double> opacity = get_opt_attr<double>(sym, "opacity");
if (opacity)
{
shield_symbol.set_opacity(*opacity);
}
optional<std::string> face_name =
get_opt_attr<std::string>(sym, "face-name");
// text-opacity
// TODO: Could be problematic because it is named opacity in TextSymbolizer but opacity has a diffrent meaning here.
optional<double> text_opacity =
get_opt_attr<double>(sym, "text-opacity");
if (text_opacity)
{
shield_symbol.set_text_opacity( * text_opacity );
}
optional<std::string> fontset_name =
get_opt_attr<std::string>(sym, "fontset-name");
// unlock_image
optional<boolean> unlock_image =
get_opt_attr<boolean>(sym, "unlock-image");
if (unlock_image)
{
shield_symbol.set_unlock_image( * unlock_image );
}
float size = get_attr(sym, "size", 10.0f);
color fill = get_attr(sym, "fill", color(0,0,0));
// 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");
optional<std::string> base = get_opt_attr<std::string>(sym, "base");
optional<std::string> transform_wkt = get_opt_attr<std::string>(sym, "transform");
try
{
if( base )
@ -1553,202 +1403,7 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
}
image_file = ensure_relative_to_xml(image_file);
shield_symbolizer shield_symbol(parse_expression(name, "utf8"),size,fill,parse_path(image_file));
if (fontset_name && face_name)
{
throw config_error(std::string("Can't have both face-name and fontset-name"));
}
else if (fontset_name)
{
std::map<std::string,font_set>::const_iterator itr = fontsets_.find(*fontset_name);
if (itr != fontsets_.end())
{
shield_symbol.set_fontset(itr->second);
}
else
{
throw config_error("Unable to find any fontset named '" + *fontset_name + "'");
}
}
else if (face_name)
{
if ( strict_ )
{
ensure_font_face(*face_name);
}
shield_symbol.set_face_name(*face_name);
}
else
{
throw config_error(std::string("Must have face-name or fontset-name"));
}
// text displacement (relative to shield_displacement)
double dx = get_attr(sym, "dx", 0.0);
double dy = get_attr(sym, "dy", 0.0);
shield_symbol.set_displacement(dx,dy);
// shield displacement
double shield_dx = get_attr(sym, "shield-dx", 0.0);
double shield_dy = get_attr(sym, "shield-dy", 0.0);
shield_symbol.set_shield_displacement(shield_dx,shield_dy);
label_placement_e placement =
get_attr<label_placement_e>(sym, "placement", POINT_PLACEMENT);
shield_symbol.set_label_placement( placement );
// don't render shields around edges
optional<boolean> avoid_edges =
get_opt_attr<boolean>(sym, "avoid-edges");
if (avoid_edges)
{
shield_symbol.set_avoid_edges( *avoid_edges);
}
// halo fill and radius
optional<color> halo_fill = get_opt_attr<color>(sym, "halo-fill");
if (halo_fill)
{
shield_symbol.set_halo_fill( * halo_fill );
}
optional<double> halo_radius =
get_opt_attr<double>(sym, "halo-radius");
if (halo_radius)
{
shield_symbol.set_halo_radius(*halo_radius);
}
// minimum distance between labels
optional<unsigned> min_distance = get_opt_attr<unsigned>(sym, "minimum-distance");
if (min_distance)
{
shield_symbol.set_minimum_distance(*min_distance);
}
// minimum distance from edge of the map
optional<unsigned> min_padding = get_opt_attr<unsigned>(sym, "minimum-padding");
if (min_padding)
{
shield_symbol.set_minimum_padding(*min_padding);
}
// spacing between repeated labels on lines
optional<unsigned> spacing = get_opt_attr<unsigned>(sym, "spacing");
if (spacing)
{
shield_symbol.set_label_spacing(*spacing);
}
// allow_overlap
optional<boolean> allow_overlap =
get_opt_attr<boolean>(sym, "allow-overlap");
if (allow_overlap)
{
shield_symbol.set_allow_overlap( * allow_overlap );
}
// vertical alignment
vertical_alignment_e valign = get_attr<vertical_alignment_e>(sym, "vertical-alignment", V_MIDDLE);
shield_symbol.set_vertical_alignment(valign);
// horizontal alignment
horizontal_alignment_e halign = get_attr<horizontal_alignment_e>(sym, "horizontal-alignment", H_MIDDLE);
shield_symbol.set_horizontal_alignment(halign);
// justify alignment
justify_alignment_e jalign = get_attr<justify_alignment_e>(sym, "justify-alignment", J_MIDDLE);
shield_symbol.set_justify_alignment(jalign);
optional<unsigned> wrap_width =
get_opt_attr<unsigned>(sym, "wrap-width");
if (wrap_width)
{
shield_symbol.set_wrap_width(*wrap_width);
}
optional<boolean> wrap_before =
get_opt_attr<boolean>(sym, "wrap-before");
if (wrap_before)
{
shield_symbol.set_wrap_before(*wrap_before);
}
// character used to break long strings
optional<std::string> wrap_char =
get_opt_attr<std::string>(sym, "wrap-character");
if (wrap_char && (*wrap_char).size() > 0)
{
shield_symbol.set_wrap_char((*wrap_char)[0]);
}
// text conversion before rendering
text_transform_e tconvert =
get_attr<text_transform_e>(sym, "text-transform", NONE);
shield_symbol.set_text_transform(tconvert);
// spacing between text lines
optional<unsigned> line_spacing = get_opt_attr<unsigned>(sym, "line-spacing");
if (line_spacing)
{
shield_symbol.set_line_spacing(*line_spacing);
}
// spacing between characters in text
optional<unsigned> character_spacing = get_opt_attr<unsigned>(sym, "character-spacing");
if (character_spacing)
{
shield_symbol.set_character_spacing(*character_spacing);
}
// opacity
optional<double> opacity =
get_opt_attr<double>(sym, "opacity");
if (opacity)
{
shield_symbol.set_opacity( * opacity );
}
// text-opacity
optional<double> text_opacity =
get_opt_attr<double>(sym, "text-opacity");
if (text_opacity)
{
shield_symbol.set_text_opacity( * text_opacity );
}
if (transform_wkt)
{
agg::trans_affine tr;
if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr))
{
std::stringstream ss;
ss << "Could not parse transform from '" << transform_wkt << "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'";
if (strict_)
throw config_error(ss.str()); // value_error here?
else
std::clog << "### WARNING: " << ss << endl;
}
boost::array<double,6> matrix;
tr.store_to(&matrix[0]);
shield_symbol.set_transform(matrix);
}
// unlock_image
optional<boolean> unlock_image =
get_opt_attr<boolean>(sym, "unlock-image");
if (unlock_image)
{
shield_symbol.set_unlock_image( * unlock_image );
}
// no text
if (no_text)
{
shield_symbol.set_no_text( * no_text );
}
parse_metawriter_in_symbolizer(shield_symbol, sym);
rule.append(shield_symbol);
shield_symbol.set_filename(parse_path(image_file));
}
catch (image_reader_exception const & ex )
{
@ -1763,7 +1418,9 @@ 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.set_values_from_xml(sym, fontsets_);
rule.append(shield_symbol);
}
catch (const config_error & ex)
{
@ -1994,117 +1651,6 @@ void map_parser::parse_raster_symbolizer( rule & rule, ptree const & sym )
}
}
void map_parser::parse_glyph_symbolizer(rule & rule, ptree const & sym)
{
ensure_attrs(sym, "GlyphSymbolizer", "face-name,char,angle,angle-mode,value,size,color,halo-fill,halo-radius,allow-overlap,avoid-edges,dx,dy,meta-writer,meta-output");
try
{
// Parse required constructor args
std::string face_name = get_attr<std::string>(sym, "face-name");
std::string _char = get_attr<std::string>(sym, "char");
glyph_symbolizer glyph_sym = glyph_symbolizer(
face_name,
parse_expression(_char, "utf8")
);
//
// parse and set optional attrs.
//
// angle
optional<std::string> angle =
get_opt_attr<std::string>(sym, "angle");
if (angle)
glyph_sym.set_angle(parse_expression(*angle, "utf8"));
angle_mode_e angle_mode =
get_attr<angle_mode_e>(sym, "angle-mode", TRIGONOMETRIC);
glyph_sym.set_angle_mode(angle_mode);
// value
optional<std::string> value =
get_opt_attr<std::string>(sym, "value");
if (value)
glyph_sym.set_value(parse_expression(*value, "utf8"));
// size
std::string size =
get_attr<std::string>(sym, "size");
glyph_sym.set_size(parse_expression(size, "utf8"));
// color
optional<std::string> _color =
get_opt_attr<std::string>(sym, "color");
if (_color)
glyph_sym.set_color(parse_expression(*_color, "utf8"));
// halo_fill
optional<color> halo_fill = get_opt_attr<color>(sym, "halo-fill");
if (halo_fill)
glyph_sym.set_halo_fill(*halo_fill);
// halo_radius
optional<double> halo_radius = get_opt_attr<double>(
sym,
"halo-radius");
if (halo_radius)
glyph_sym.set_halo_radius(*halo_radius);
// allow_overlap
optional<boolean> allow_overlap = get_opt_attr<boolean>(
sym,
"allow-overlap"
);
if (allow_overlap)
glyph_sym.set_allow_overlap(*allow_overlap);
// avoid_edges
optional<boolean> avoid_edges = get_opt_attr<boolean>(
sym,
"avoid-edges"
);
if (avoid_edges)
glyph_sym.set_avoid_edges(*avoid_edges);
// displacement
optional<double> dx = get_opt_attr<double>(sym, "dx");
optional<double> dy = get_opt_attr<double>(sym, "dy");
if (dx && dy)
glyph_sym.set_displacement(*dx, *dy);
// colorizer
ptree::const_iterator childIter = sym.begin();
ptree::const_iterator endChild = sym.end();
for (; childIter != endChild; ++childIter)
{
ptree::value_type const& tag = *childIter;
if (tag.first == "RasterColorizer")
{
raster_colorizer_ptr colorizer(new raster_colorizer());
glyph_sym.set_colorizer(colorizer);
parse_raster_colorizer(colorizer, tag.second);
}
else if (tag.first!="<xmlcomment>" && tag.first!="<xmlattr>" )
{
throw config_error(std::string("Unknown child node. ") +
"Expected 'RasterColorizer' but got '" +
tag.first + "'");
}
}
parse_metawriter_in_symbolizer(glyph_sym, sym);
rule.append(glyph_sym);
}
catch (const config_error & ex)
{
ex.append_context("in GlyphSymbolizer");
throw;
}
}
void map_parser::parse_raster_colorizer(raster_colorizer_ptr const& rc,
ptree const& node )
{

View File

@ -23,7 +23,7 @@
// Mapnik
#include <mapnik/metawriter.hpp>
#include <mapnik/metawriter_json.hpp>
#include <mapnik/placement_finder.hpp>
#include <mapnik/text_placements.hpp>
// Boost
#include <boost/foreach.hpp>
@ -174,8 +174,8 @@ void metawriter_json_stream::add_box(box2d<double> const &box, Feature const& fe
}
void metawriter_json_stream::add_text(placement const& p,
face_set_ptr face,
void metawriter_json_stream::add_text(text_placement_info const& p,
face_manager_freetype &font_manager,
Feature const& feature,
CoordTransform const& t,
metawriter_properties const& properties)
@ -192,18 +192,19 @@ void metawriter_json_stream::add_text(placement const& p,
Hightest y = baseline of top line
*/
// if (p.placements.size()) std::cout << p.info.get_string() << "\n";
for (unsigned n = 0; n < p.placements.size(); n++) {
placement_element & current_placement = const_cast<placement_element &>(p.placements[n]);
bool inside = false;
bool inside = false; /* Part of text is inside rendering region */
bool straight = true;
int c; double x, y, angle;
int c;
double x, y, angle;
char_properties *format;
current_placement.rewind();
for (int i = 0; i < current_placement.num_nodes(); ++i) {
int cx = current_placement.starting_x;
int cy = current_placement.starting_y;
current_placement.vertex(&c, &x, &y, &angle);
current_placement.vertex(&c, &x, &y, &angle, &format);
if (cx+x >= 0 && cx+x < width_ && cy-y >= 0 && cy-y < height_) inside = true;
if (angle > 0.001 || angle < -0.001) straight = false;
if (inside && !straight) break;
@ -216,14 +217,13 @@ void metawriter_json_stream::add_text(placement const& p,
//Reduce number of polygons
double minx = INT_MAX, miny = INT_MAX, maxx = INT_MIN, maxy = INT_MIN;
for (int i = 0; i < current_placement.num_nodes(); ++i) {
current_placement.vertex(&c, &x, &y, &angle);
font_face_set::dimension_t ci = face->character_dimensions(c);
if (x < minx) minx = x;
if (x+ci.width > maxx) maxx = x+ci.width;
if (y+ci.height+ci.ymin > maxy) maxy = y+ci.height+ci.ymin;
if (y+ci.ymin < miny) miny = y+ci.ymin;
// std::cout << (char) c << " height:" << ci.height << " ymin:" << ci.ymin << " y:" << y << " miny:"<< miny << " maxy:"<< maxy <<"\n";
current_placement.vertex(&c, &x, &y, &angle, &format);
face_set_ptr face = font_manager.get_face_set(format->face_name, format->fontset);
char_info ci = face->character_dimensions(c);
minx = std::min(minx, x);
maxx = std::max(maxx, x+ci.width);
maxy = std::max(maxy, y+ci.ymax);
miny = std::min(miny, y+ci.ymin);
}
add_box(box2d<double>(current_placement.starting_x+minx,
current_placement.starting_y-miny,
@ -239,9 +239,10 @@ void metawriter_json_stream::add_text(placement const& p,
if (c != ' ') {
*f_ << ",";
}
current_placement.vertex(&c, &x, &y, &angle);
current_placement.vertex(&c, &x, &y, &angle, &format);
if (c == ' ') continue;
font_face_set::dimension_t ci = face->character_dimensions(c);
face_set_ptr face = font_manager.get_face_set(format->face_name, format->fontset);
char_info ci = face->character_dimensions(c);
double x0, y0, x1, y1, x2, y2, x3, y3;
double sina = sin(angle);
@ -250,10 +251,10 @@ void metawriter_json_stream::add_text(placement const& p,
y0 = current_placement.starting_y - y - cosa*ci.ymin;
x1 = x0 + ci.width * cosa;
y1 = y0 - ci.width * sina;
x2 = x0 + (ci.width * cosa - ci.height * sina);
y2 = y0 - (ci.width * sina + ci.height * cosa);
x3 = x0 - ci.height * sina;
y3 = y0 - ci.height * cosa;
x2 = x0 + (ci.width * cosa - ci.height() * sina);
y2 = y0 - (ci.width * sina + ci.height() * cosa);
x3 = x0 - ci.height() * sina;
y3 = y0 - ci.height() * cosa;
*f_ << "\n [[";
write_point(t, x0, y0);

View File

@ -69,9 +69,9 @@ metawriter_inmem::add_box(box2d<double> const& box, Feature const& feature,
instances_.push_back(inst);
}
void
metawriter_inmem::add_text(placement const& p,
face_set_ptr /*face*/,
void
metawriter_inmem::add_text(text_placement_info const& p,
face_manager_freetype & /*face*/,
Feature const& feature,
CoordTransform const& /*t*/,
metawriter_properties const& properties) {

View File

@ -26,6 +26,7 @@
#include <mapnik/placement_finder.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/fastmath.hpp>
// agg
@ -49,60 +50,6 @@
namespace mapnik
{
placement::placement(string_info & info_,
shield_symbolizer const& sym,
double scale_factor,
unsigned w, unsigned h,
bool has_dimensions_)
: info(info_),
scale_factor_(scale_factor),
label_placement(sym.get_label_placement()),
wrap_width(sym.get_wrap_width()),
wrap_before(sym.get_wrap_before()),
wrap_char(sym.get_wrap_char()),
text_ratio(sym.get_text_ratio()),
label_spacing(scale_factor_ * sym.get_label_spacing()),
label_position_tolerance(sym.get_label_position_tolerance()),
force_odd_labels(sym.get_force_odd_labels()),
max_char_angle_delta(sym.get_max_char_angle_delta()),
minimum_distance(scale_factor_ * sym.get_minimum_distance()),
minimum_padding(scale_factor_ * sym.get_minimum_padding()),
minimum_path_length(0),
avoid_edges(sym.get_avoid_edges()),
has_dimensions(has_dimensions_),
allow_overlap(false),
dimensions(std::make_pair(w,h)),
collect_extents(false),
extents()
{}
placement::placement(string_info & info_,
text_symbolizer const& sym,
double scale_factor)
: info(info_),
scale_factor_(scale_factor),
label_placement(sym.get_label_placement()),
wrap_width(sym.get_wrap_width()),
wrap_before(sym.get_wrap_before()),
wrap_char(sym.get_wrap_char()),
text_ratio(sym.get_text_ratio()),
label_spacing(scale_factor_ * sym.get_label_spacing()),
label_position_tolerance(sym.get_label_position_tolerance()),
force_odd_labels(sym.get_force_odd_labels()),
max_char_angle_delta(sym.get_max_char_angle_delta()),
minimum_distance(scale_factor_ * sym.get_minimum_distance()),
minimum_padding(scale_factor_ * sym.get_minimum_padding()),
minimum_path_length(scale_factor_ * sym.get_minimum_path_length()),
avoid_edges(sym.get_avoid_edges()),
has_dimensions(false),
allow_overlap(sym.get_allow_overlap()),
dimensions(),
collect_extents(false),
extents()
{}
placement::~placement() {}
template<typename T>
std::pair<double, double> get_position_at_distance(double target_distance, T & shape_path)
@ -151,22 +98,24 @@ double get_total_distance(T & shape_path)
}
template <typename DetectorT>
placement_finder<DetectorT>::placement_finder(DetectorT & detector)
placement_finder<DetectorT>::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector)
: detector_(detector),
dimensions_(detector_.extent())
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_()
{
}
template <typename DetectorT>
placement_finder<DetectorT>::placement_finder(DetectorT & detector, box2d<double> const& extent)
placement_finder<DetectorT>::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector, box2d<double> const& extent)
: detector_(detector),
dimensions_(extent)
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_()
{
}
template <typename DetectorT>
template <typename T>
void placement_finder<DetectorT>::find_point_placements(placement & p, text_placement_info_ptr po, T & shape_path)
void placement_finder<DetectorT>::find_point_placements(T & shape_path)
{
unsigned cmd;
double new_x = 0.0;
@ -182,15 +131,15 @@ void placement_finder<DetectorT>::find_point_placements(placement & p, text_plac
{
double x, y;
shape_path.vertex(&x,&y);
find_point_placement(p, po, x, y);
find_point_placement(x, y);
return;
}
int num_labels = 1;
if (p.label_spacing > 0)
num_labels = static_cast<int> (floor(total_distance / p.label_spacing));
num_labels = static_cast<int> (floor(total_distance / pi.get_actual_label_spacing()));
if (p.force_odd_labels && num_labels%2 == 0)
if (p.force_odd_labels && num_labels % 2 == 0)
num_labels--;
if (num_labels <= 0)
num_labels = 1;
@ -217,7 +166,7 @@ void placement_finder<DetectorT>::find_point_placements(placement & p, text_plac
{
//Try place at the specified place
double new_weight = (segment_length - (distance - target_distance))/segment_length;
find_point_placement(p, po, old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight);
find_point_placement(old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight);
distance -= target_distance; //Consume the spacing gap we have used up
target_distance = spacing; //Need to reset the target_distance as it is spacing/2 for the first label.
@ -231,245 +180,270 @@ void placement_finder<DetectorT>::find_point_placements(placement & p, text_plac
}
template <typename DetectorT>
void placement_finder<DetectorT>::find_point_placement(placement & p,
text_placement_info_ptr po,
double label_x,
double label_y,
double angle,
unsigned line_spacing,
unsigned character_spacing)
void placement_finder<DetectorT>::init_string_size()
{
double x, y;
std::auto_ptr<placement_element> current_placement(new placement_element);
std::pair<double, double> string_dimensions = p.info.get_dimensions();
double string_width = string_dimensions.first + (character_spacing *(p.info.num_characters()-1));
double string_height = string_dimensions.second;
// Get total string size
string_width_ = 0;
string_height_ = 0;
first_line_space_ = 0;
if (!info_.num_characters()) return; //At least one character is required
for (unsigned i = 0; i < info_.num_characters(); i++)
{
char_info const& ci = info_.at(i);
if (!ci.width || !ci.line_height) continue; //Skip empty chars (add no character_spacing for them)
string_width_ += ci.width + ci.format->character_spacing;
string_height_ = std::max(string_height_, ci.line_height+ci.format->line_spacing);
first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height);
}
string_width_ -= info_.at(info_.num_characters()-1).format->character_spacing; //Remove last space
}
// use height of tallest character in the string for the 'line' spacing to obtain consistent line spacing
double max_character_height = string_height; // height of the tallest character in the string
template <typename DetectorT>
void placement_finder<DetectorT>::find_line_breaks()
{
bool first_line = true;
line_breaks_.clear();
line_sizes_.clear();
// check if we need to wrap the string
double wrap_at = string_width + 1.0;
if (p.wrap_width && string_width > p.wrap_width)
double wrap_at = string_width_ + 1.0;
if (p.wrap_width && string_width_ > p.wrap_width)
{
if (p.text_ratio)
for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > p.text_ratio && (string_width/i) > p.wrap_width; i += 1.0) ;
for (double i = 1.0; ((wrap_at = string_width_/i)/(string_height_*i)) > p.text_ratio && (string_width_/i) > p.wrap_width; i += 1.0) ;
else
wrap_at = p.wrap_width;
}
// work out where our line breaks need to be and the resultant width to the 'wrapped' string
std::vector<int> line_breaks;
std::vector<double> line_widths;
if ((p.info.num_characters() > 0) && ((wrap_at < string_width) || p.info.has_line_breaks()))
if ((wrap_at < string_width_) || info_.has_line_breaks())
{
int last_wrap_char = 0;
int last_wrap_char_width = 0;
string_width = 0.0;
string_height = 0.0;
first_line_space_ = 0.0;
int last_wrap_char_pos = 0; //Position of last char where wrapping is possible
double last_char_spacing = 0.0;
double last_wrap_char_width = 0.0; //Include char_spacing before and after
string_width_ = 0.0;
string_height_ = 0.0;
double line_width = 0.0;
double word_width = 0.0;
double line_height = 0.0; //Height of tallest char in line
double word_width = 0.0; //Current unfinished word width
double word_height = 0.0;
//line_width, word_width does include char width + spacing, but not the spacing after the last char
for (unsigned int ii = 0; ii < p.info.num_characters(); ii++)
for (unsigned int ii = 0; ii < info_.num_characters(); ii++)
{
character_info ci;
ci = p.info.at(ii);
char_info const& ci = info_.at(ii);
unsigned c = ci.c;
double cwidth = ci.width + character_spacing;
unsigned c = ci.character;
word_width += cwidth;
if ((c == p.wrap_char) || (c == '\n'))
if ((c == ci.format->wrap_char) || (c == '\n'))
{
last_wrap_char = ii;
last_wrap_char_width = cwidth;
line_width += word_width;
last_wrap_char_pos = ii;
//No wrap at previous position
line_width += word_width + last_wrap_char_width;
line_height = std::max(line_height, word_height);
last_wrap_char_width = last_char_spacing + ci.width + ci.format->character_spacing;
last_char_spacing = 0.0;
word_width = 0.0;
word_height = 0.0;
} else {
//No wrap char
word_width += last_char_spacing + ci.width;
last_char_spacing = ci.format->character_spacing;
word_height = std::max(word_height, ci.line_height + ci.format->line_spacing);
if (first_line) first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height);
}
// wrap text at first wrap_char after (default) the wrap width or immediately before the current word
if ((c == '\n') ||
(line_width > 0 && (((line_width - character_spacing) > wrap_at && !p.wrap_before) ||
((line_width + word_width - character_spacing) > wrap_at && p.wrap_before)) ))
(line_width > 0 && ((line_width > wrap_at && !ci.format->wrap_before) ||
((line_width + last_wrap_char_width + word_width) > wrap_at && ci.format->wrap_before)) ))
{
// Remove width of breaking space character since it is not rendered and the character_spacing for the last character on the line
line_width -= (last_wrap_char_width + character_spacing);
string_width = string_width > line_width ? string_width : line_width;
string_height += max_character_height;
line_breaks.push_back(last_wrap_char);
line_widths.push_back(line_width);
ii = last_wrap_char;
string_width_ = std::max(string_width_, line_width); //Total width is the longest line
string_height_ += line_height;
line_breaks_.push_back(last_wrap_char_pos);
line_sizes_.push_back(std::make_pair(line_width, line_height));
line_width = 0.0;
word_width = 0.0;
line_height = 0.0;
last_wrap_char_width = 0; //Wrap char supressed
first_line = false;
}
}
line_width += (word_width - character_spacing); // remove character_spacing from last character on the line
string_width = string_width > line_width ? string_width : line_width;
string_height += max_character_height;
line_breaks.push_back(p.info.num_characters());
line_widths.push_back(line_width);
line_width += last_wrap_char_width + word_width;
line_height = std::max(line_height, word_height);
string_width_ = std::max(string_width_, line_width);
string_height_ += line_height;
line_sizes_.push_back(std::make_pair(line_width, line_height));
} else {
//No linebreaks
line_sizes_.push_back(std::make_pair(string_width_, string_height_));
}
if (line_breaks.size() == 0)
{
line_breaks.push_back(p.info.num_characters());
line_widths.push_back(string_width);
line_breaks_.push_back(info_.num_characters());
}
template <typename DetectorT>
void placement_finder<DetectorT>::init_alignment()
{
valign_ = p.valign;
if (valign_ == V_AUTO) {
if (p.displacement.get<1>() > 0.0)
valign_ = V_BOTTOM;
else if (p.displacement.get<1>() < 0.0)
valign_ = V_TOP;
else
valign_ = V_MIDDLE;
}
int total_lines = line_breaks.size();
p.info.set_dimensions( string_width, (string_height + (line_spacing * (total_lines-1))) );
halign_ = p.halign;
if (halign_ == H_AUTO) {
if (p.displacement.get<0>() > 0.0)
halign_ = H_RIGHT;
else if (p.displacement.get<0>() < 0.0)
halign_ = H_LEFT;
else
halign_ = H_MIDDLE;
}
}
template <typename DetectorT>
void placement_finder<DetectorT>::adjust_position(placement_element *current_placement, double label_x, double label_y)
{
// if needed, adjust for desired vertical alignment
current_placement->starting_y = label_y; // no adjustment, default is MIDDLE
vertical_alignment_e real_valign = po->valign;
if (real_valign == V_AUTO) {
if (po->displacement.get<1>() > 0.0)
real_valign = V_BOTTOM;
else if (po->displacement.get<1>() < 0.0)
real_valign = V_TOP;
else
real_valign = V_MIDDLE;
if (valign_ == V_TOP)
current_placement->starting_y -= 0.5 * string_height_; // move center up by 1/2 the total height
else if (valign_ == V_BOTTOM) {
current_placement->starting_y += 0.5 * string_height_; // move center down by the 1/2 the total height
current_placement->starting_y -= first_line_space_;
} else if (valign_ == V_MIDDLE) {
current_placement->starting_y -= first_line_space_/2.0;
}
horizontal_alignment_e real_halign = po->halign;
if (real_halign == H_AUTO) {
if (po->displacement.get<0>() > 0.0)
real_halign = H_RIGHT;
else if (po->displacement.get<0>() < 0.0)
real_halign = H_LEFT;
else
real_halign = H_MIDDLE;
}
if (real_valign == V_TOP)
current_placement->starting_y -= 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center up by 1/2 the total height
else if (real_valign == V_BOTTOM)
current_placement->starting_y += 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center down by the 1/2 the total height
// correct placement for error, but BOTTOM does not need to be adjusted
// (text rendering is at text_size, but line placement is by line_height (max_character_height),
// and the rendering adds the extra space below the characters)
if (real_valign == V_TOP )
current_placement->starting_y -= (po->text_size - max_character_height); // move up by the error
else if (real_valign == V_MIDDLE)
current_placement->starting_y -= ((po->text_size - max_character_height) / 2.0); // move up by 1/2 the error
// set horizontal position to middle of text
current_placement->starting_x = label_x; // no adjustment, default is MIDDLE
if (real_halign == H_LEFT)
current_placement->starting_x -= 0.5 * string_width; // move center left by 1/2 the string width
else if (real_halign == H_RIGHT)
current_placement->starting_x += 0.5 * string_width; // move center right by 1/2 the string width
if (halign_ == H_LEFT)
current_placement->starting_x -= 0.5 * string_width_; // move center left by 1/2 the string width
else if (halign_ == H_RIGHT)
current_placement->starting_x += 0.5 * string_width_; // move center right by 1/2 the string width
// adjust text envelope position by user's x-y displacement (dx, dy)
current_placement->starting_x += p.scale_factor_ * boost::tuples::get<0>(po->displacement);
current_placement->starting_y += p.scale_factor_ * boost::tuples::get<1>(po->displacement);
current_placement->starting_x += pi.get_scale_factor() * boost::tuples::get<0>(p.displacement);
current_placement->starting_y += pi.get_scale_factor() * boost::tuples::get<1>(p.displacement);
}
template <typename DetectorT>
void placement_finder<DetectorT>::find_point_placement(double label_x, double label_y, double angle)
{
init_string_size();
find_line_breaks();
init_alignment();
double rad = M_PI * angle/180.0;
double cosa = std::cos(rad);
double sina = std::sin(rad);
double x, y;
std::auto_ptr<placement_element> current_placement(new placement_element);
adjust_position(current_placement.get(), label_x, label_y);
// presets for first line
unsigned int line_number = 0;
unsigned int index_to_wrap_at = line_breaks[0];
double line_width = line_widths[0];
unsigned int index_to_wrap_at = line_breaks_[0];
double line_width = line_sizes_[0].first;
double line_height = line_sizes_[0].second;
//TODO: Understand and document this
// set for upper left corner of text envelope for the first line, bottom left of first character
x = -(line_width / 2.0);
if (p.info.get_rtl()==false)
{
y = (0.5 * (string_height + (line_spacing * (total_lines-1)))) - max_character_height;
}
y = (string_height_ / 2.0) - line_height;
// adjust for desired justification
//TODO: Understand and document this
if (p.jalign == J_LEFT)
x = -(string_width_ / 2.0);
else if (p.jalign == J_RIGHT)
x = (string_width_ / 2.0) - line_width;
else
{
y = -(0.5 * (string_height + (line_spacing * (total_lines-1)))) + max_character_height;
}
// if needed, adjust for desired justification (J_MIDDLE is the default)
if( po->jalign == J_LEFT )
x = -(string_width / 2.0);
else if (po->jalign == J_RIGHT)
x = (string_width / 2.0) - line_width;
x = -(line_width / 2.0);
// save each character rendering position and build envelope as go thru loop
std::queue< box2d<double> > c_envelopes;
for (unsigned i = 0; i < p.info.num_characters(); i++)
for (unsigned i = 0; i < info_.num_characters(); i++)
{
character_info ci;
ci = p.info.at(i);
char_info const& ci = info_.at(i);
double cwidth = ci.width + character_spacing;
double cwidth = ci.width + ci.format->character_spacing;
unsigned c = ci.character;
unsigned c = ci.c;
if (i == index_to_wrap_at)
{
index_to_wrap_at = line_breaks[++line_number];
line_width = line_widths[line_number];
index_to_wrap_at = line_breaks_[++line_number];
line_width = line_sizes_[line_number].first;
line_height= line_sizes_[line_number].second;
if (p.info.get_rtl()==false)
{
y -= (max_character_height + line_spacing); // move position down to line start
}
else
{
y += (max_character_height + line_spacing); // move position up to line start
}
y -= line_height; // move position down to line start
// reset to begining of line position
x = ((po->jalign == J_LEFT)? -(string_width / 2.0): ((po->jalign == J_RIGHT)? ((string_width /2.0) - line_width): -(line_width / 2.0)));
if (p.jalign == J_LEFT)
x = -(string_width_ / 2.0);
else if (p.jalign == J_RIGHT)
x = (string_width_ / 2.0) - line_width;
else
x = -(line_width / 2.0);
continue;
}
else
{
// place the character relative to the center of the string envelope
double rad = M_PI * angle/180.0;
double cosa = fast_cos(rad);
double sina = fast_sin(rad);
double dx = x * cosa - y*sina;
double dy = x * sina + y*cosa;
current_placement->add_node(c, dx, dy, rad);
current_placement->add_node(c, dx, dy, rad, ci.format);
// compute the Bounding Box for each character and test for:
// overlap, minimum distance or edge avoidance - exit if condition occurs
box2d<double> e;
if (p.has_dimensions)
/*x axis: left to right, y axis: top to bottom (negative values higher)*/
if (pi.has_dimensions)
{
e.init(current_placement->starting_x - (p.dimensions.first/2.0), // Top Left
current_placement->starting_y - (p.dimensions.second/2.0),
e.init(current_placement->starting_x - (pi.dimensions.first/2.0), // Top Left
current_placement->starting_y - (pi.dimensions.second/2.0),
current_placement->starting_x + (p.dimensions.first/2.0), // Bottom Right
current_placement->starting_y + (p.dimensions.second/2.0));
current_placement->starting_x + (pi.dimensions.first/2.0), // Bottom Right
current_placement->starting_y + (pi.dimensions.second/2.0));
}
else
{
e.init(current_placement->starting_x + dx, // Bottom Left
current_placement->starting_y - dy,
current_placement->starting_y - dy - ci.ymin, /*ymin usually <0 */
current_placement->starting_x + dx + ci.width, // Top Right
current_placement->starting_y - dy - max_character_height);
current_placement->starting_y - dy - ci.ymax);
}
// if there is an overlap with existing envelopes, then exit - no placement
if (!detector_.extent().intersects(e) || (!p.allow_overlap && !detector_.has_point_placement(e,p.minimum_distance)))
if (!detector_.extent().intersects(e) || (!p.allow_overlap && !detector_.has_point_placement(e, pi.get_actual_minimum_distance()))) {
return;
}
// if avoid_edges test dimensions contains e
if (p.avoid_edges && !dimensions_.contains(e))
if (p.avoid_edges && !dimensions_.contains(e)) {
return;
}
if (p.minimum_padding > 0)
if (p.minimum_padding > 0)
{
box2d<double> epad(e.minx()-p.minimum_padding,
e.miny()-p.minimum_padding,
e.maxx()+p.minimum_padding,
e.maxy()+p.minimum_padding);
double min_pad = pi.get_actual_minimum_padding();
box2d<double> epad(e.minx()-min_pad,
e.miny()-min_pad,
e.maxx()+min_pad,
e.maxy()+min_pad);
if (!dimensions_.contains(epad))
{
return;
@ -482,6 +456,8 @@ void placement_finder<DetectorT>::find_point_placement(placement & p,
x += cwidth; // move position to next character
}
#if 0
//TODO
// check the placement of any additional envelopes
if (!p.allow_overlap && !p.additional_boxes.empty())
{
@ -498,22 +474,24 @@ void placement_finder<DetectorT>::find_point_placement(placement & p,
c_envelopes.push(pt);
}
}
#endif
// since there was no early exit, add the character envelopes to the placements' envelopes
while( !c_envelopes.empty() )
{
p.envelopes.push( c_envelopes.front() );
pi.envelopes.push( c_envelopes.front() );
c_envelopes.pop();
}
p.placements.push_back(current_placement.release());
pi.placements.push_back(current_placement.release());
}
template <typename DetectorT>
template <typename PathT>
void placement_finder<DetectorT>::find_line_placements(placement & p, text_placement_info_ptr po, PathT & shape_path)
void placement_finder<DetectorT>::find_line_placements(PathT & shape_path)
{
init_string_size();
unsigned cmd;
double new_x = 0.0;
double new_y = 0.0;
@ -556,29 +534,26 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
return;
double distance = 0.0;
std::pair<double, double> string_dimensions = p.info.get_dimensions();
double string_width = string_dimensions.first;
double displacement = boost::tuples::get<1>(po->displacement); // displace by dy
double displacement = boost::tuples::get<1>(p.displacement); // displace by dy
//Calculate a target_distance that will place the labels centered evenly rather than offset from the start of the linestring
if (total_distance < string_width) //Can't place any strings
if (total_distance < string_width_) //Can't place any strings
return;
//If there is no spacing then just do one label, otherwise calculate how many there should be
int num_labels = 1;
if (p.label_spacing > 0)
num_labels = static_cast<int> (floor(total_distance / (p.label_spacing + string_width)));
num_labels = static_cast<int> (floor(total_distance / (pi.get_actual_label_spacing() + string_width_)));
if (p.force_odd_labels && num_labels%2 == 0)
if (p.force_odd_labels && (num_labels % 2 == 0))
num_labels--;
if (num_labels <= 0)
num_labels = 1;
//Now we know how many labels we are going to place, calculate the spacing so that they will get placed evenly
double spacing = total_distance / num_labels;
double target_distance = (spacing - string_width) / 2; // first label should be placed at half the spacing
double target_distance = (spacing - string_width_) / 2; // first label should be placed at half the spacing
//Calculate or read out the tolerance
double tolerance_delta, tolerance;
@ -620,7 +595,7 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
{
//Record details for the start of the string placement
int orientation = 0;
std::auto_ptr<placement_element> current_placement = get_placement_offset(p, path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir));
std::auto_ptr<placement_element> current_placement = get_placement_offset(path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir));
//We were unable to place here
if (current_placement.get() == NULL)
@ -639,22 +614,20 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
}
anglesum /= current_placement->nodes_.size(); //Now it is angle average
double disp_x = p.scale_factor_ * displacement*fast_cos(anglesum+M_PI/2);
double disp_y = p.scale_factor_ * displacement*fast_sin(anglesum+M_PI/2);
//Offset all the characters by this angle
for (unsigned i = 0; i < current_placement->nodes_.size(); i++)
{
current_placement->nodes_[i].x += disp_x;
current_placement->nodes_[i].y += disp_y;
current_placement->nodes_[i].x += pi.get_scale_factor() * displacement*cos(anglesum+M_PI/2);
current_placement->nodes_[i].y += pi.get_scale_factor() * displacement*sin(anglesum+M_PI/2);
}
}
bool status = test_placement(p, current_placement, orientation);
bool status = test_placement(current_placement, orientation);
if (status) //We have successfully placed one
{
p.placements.push_back(current_placement.release());
update_detector(p);
pi.placements.push_back(current_placement.release());
update_detector();
//Totally break out of the loops
diff = tolerance;
@ -663,8 +636,8 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
else
{
//If we've failed to place, remove all the envelopes we've added up
while (!p.envelopes.empty())
p.envelopes.pop();
while (!pi.envelopes.empty())
pi.envelopes.pop();
}
//Don't need to loop twice when diff = 0
@ -684,7 +657,7 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
}
template <typename DetectorT>
std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offset(placement & p, const std::vector<vertex2d> &path_positions, const std::vector<double> &path_distances, int &orientation, unsigned index, double distance)
std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offset(const std::vector<vertex2d> &path_positions, const std::vector<double> &path_distances, int &orientation, unsigned index, double distance)
{
//Check that the given distance is on the given index and find the correct index and distance if not
while (distance < 0 && index > 1)
@ -710,7 +683,6 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
std::auto_ptr<placement_element> current_placement(new placement_element);
double string_height = p.info.get_dimensions().second;
double old_x = path_positions[index-1].x;
double old_y = path_positions[index-1].y;
@ -728,7 +700,7 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
current_placement->starting_x = old_x + dx*distance/segment_length;
current_placement->starting_y = old_y + dy*distance/segment_length;
double angle = fast_atan2(-dy, dx);
double angle = atan2(-dy, dx);
bool orientation_forced = (orientation != 0); //Wether the orientation was set by the caller
if (!orientation_forced)
@ -736,17 +708,14 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
unsigned upside_down_char_count = 0; //Count of characters that are placed upside down.
for (unsigned i = 0; i < p.info.num_characters(); ++i)
for (unsigned i = 0; i < info_.num_characters(); ++i)
{
character_info ci;
unsigned c;
// grab the next character according to the orientation
char_info const &ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1);
unsigned c = ci.c;
double last_character_angle = angle;
// grab the next character according to the orientation
ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1);
c = ci.character;
//Coordinates this character will start at
if (segment_length == 0) {
// Not allowed to place across on 0 length segments or discontinuities
@ -826,19 +795,20 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
double render_y = start_y;
//Center the text on the line
render_x += (((double)string_height/2.0) - 1.0)*sina;
render_y += (((double)string_height/2.0) - 1.0)*cosa;
double char_height = ci.avg_height;
render_x -= char_height/2.0*cos(render_angle+M_PI/2);
render_y += char_height/2.0*sin(render_angle+M_PI/2);
if (orientation < 0)
{
// rotate in place
render_x += ci.width*cosa - (string_height-2)*sina;
render_y -= ci.width*sina + (string_height-2)*cosa;
render_x += ci.width*cos(render_angle) - (char_height-2)*sin(render_angle);
render_y -= ci.width*sin(render_angle) + (char_height-2)*cos(render_angle);
render_angle += M_PI;
}
current_placement->add_node(c,render_x - current_placement->starting_x,
-render_y + current_placement->starting_y,
render_angle);
render_angle, ci.format);
//Normalise to 0 <= angle < 2PI
while (render_angle >= 2*M_PI)
@ -851,13 +821,13 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
}
//If we placed too many characters upside down
if (upside_down_char_count >= p.info.num_characters()/2.0)
if (upside_down_char_count >= info_.num_characters()/2.0)
{
//if we auto-detected the orientation then retry with the opposite orientation
if (!orientation_forced)
{
orientation = -orientation;
current_placement = get_placement_offset(p, path_positions, path_distances, orientation, initial_index, initial_distance);
current_placement = get_placement_offset(path_positions, path_distances, orientation, initial_index, initial_distance);
}
else
{
@ -871,57 +841,50 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
}
template <typename DetectorT>
bool placement_finder<DetectorT>::test_placement(placement & p, const std::auto_ptr<placement_element> & current_placement, const int & orientation)
bool placement_finder<DetectorT>::test_placement(const std::auto_ptr<placement_element> & current_placement, const int & orientation)
{
std::pair<double, double> string_dimensions = p.info.get_dimensions();
double string_height = string_dimensions.second;
//Create and test envelopes
bool status = true;
for (unsigned i = 0; i < p.info.num_characters(); ++i)
for (unsigned i = 0; i < info_.num_characters(); ++i)
{
// grab the next character according to the orientation
character_info ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1);
char_info const& ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1);
int c;
double x, y, angle;
current_placement->vertex(&c, &x, &y, &angle);
char_properties *properties;
current_placement->vertex(&c, &x, &y, &angle, &properties);
x = current_placement->starting_x + x;
y = current_placement->starting_y - y;
if (orientation < 0)
{
double sina = fast_sin(angle);
double cosa = fast_cos(angle);
// rotate in place
x += ci.width*cosa - (string_height-2)*sina;
y -= ci.width*sina + (string_height-2)*cosa;
/* TODO: What's the meaning of -2? */
x += ci.width*cos(angle) - (string_height_-2)*sin(angle);
y -= ci.width*sin(angle) + (string_height_-2)*cos(angle);
angle += M_PI;
}
box2d<double> e;
if (p.has_dimensions)
if (pi.has_dimensions)
{
e.init(x, y, x + p.dimensions.first, y + p.dimensions.second);
e.init(x, y, x + pi.dimensions.first, y + pi.dimensions.second);
}
else
{
double sina = fast_sin(angle);
double cosa = fast_cos(angle);
// put four corners of the letter into envelope
e.init(x, y, x + ci.width*cosa,
y - ci.width*sina);
e.expand_to_include(x - ci.height*sina,
y - ci.height*cosa);
e.expand_to_include(x + (ci.width*cosa - ci.height*sina),
y - (ci.width*sina + ci.height*cosa));
e.init(x, y, x + ci.width*cos(angle),
y - ci.width*sin(angle));
e.expand_to_include(x - ci.height()*sin(angle),
y - ci.height()*cos(angle));
e.expand_to_include(x + (ci.width*cos(angle) - ci.height()*sin(angle)),
y - (ci.width*sin(angle) + ci.height()*cos(angle)));
}
if (!detector_.extent().intersects(e) ||
!detector_.has_placement(e, p.info.get_string(), p.minimum_distance))
!detector_.has_placement(e, info_.get_string(), pi.get_actual_minimum_distance()))
{
//std::clog << "No Intersects:" << !dimensions_.intersects(e) << ": " << e << " @ " << dimensions_ << std::endl;
//std::clog << "No Placements:" << !detector_.has_placement(e, p.info.get_string(), p.minimum_distance) << std::endl;
//std::clog << "No Placements:" << !detector_.has_placement(e, info.get_string(), p.minimum_distance) << std::endl;
status = false;
break;
}
@ -932,19 +895,20 @@ bool placement_finder<DetectorT>::test_placement(placement & p, const std::auto_
status = false;
break;
}
if (p.minimum_padding > 0)
if (p.minimum_padding > 0)
{
box2d<double> epad(e.minx()-p.minimum_padding,
e.miny()-p.minimum_padding,
e.maxx()+p.minimum_padding,
e.maxy()+p.minimum_padding);
double min_pad = pi.get_actual_minimum_padding();
box2d<double> epad(e.minx()-min_pad,
e.miny()-min_pad,
e.maxx()+min_pad,
e.maxy()+min_pad);
if (!dimensions_.contains(epad))
{
status = false;
break;
}
}
p.envelopes.push(e);
pi.envelopes.push(e);
}
current_placement->rewind();
@ -1000,27 +964,27 @@ void placement_finder<DetectorT>::find_line_circle_intersection(
}
template <typename DetectorT>
void placement_finder<DetectorT>::update_detector(placement & p)
void placement_finder<DetectorT>::update_detector()
{
bool first = true;
// add the bboxes to the detector and remove from the placement
while (!p.envelopes.empty())
while (!pi.envelopes.empty())
{
box2d<double> e = p.envelopes.front();
detector_.insert(e, p.info.get_string());
p.envelopes.pop();
box2d<double> e = pi.envelopes.front();
detector_.insert(e, info_.get_string());
pi.envelopes.pop();
if (p.collect_extents)
if (pi.collect_extents)
{
if(first)
{
first = false;
p.extents = e;
pi.extents = e;
}
else
{
p.extents.expand_to_include(e);
pi.extents.expand_to_include(e);
}
}
}
@ -1032,11 +996,58 @@ 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;
template class placement_finder<DetectorType>;
template void placement_finder<DetectorType>::find_point_placements<PathType> (placement&, text_placement_info_ptr po, PathType & );
template void placement_finder<DetectorType>::find_line_placements<PathType> (placement&, text_placement_info_ptr po, PathType & );
template void placement_finder<DetectorType>::find_point_placements<PathType>(PathType &);
template void placement_finder<DetectorType>::find_line_placements<PathType>(PathType &);
} // namespace

View File

@ -28,6 +28,8 @@
#include <mapnik/expression_string.hpp>
#include <mapnik/raster_colorizer.hpp>
#include <mapnik/metawriter_factory.hpp>
#include <mapnik/text_placements_simple.hpp>
#include <mapnik/text_placements_list.hpp>
// boost
#include <boost/algorithm/string.hpp>
@ -310,100 +312,6 @@ public:
add_metawriter_attributes(sym_node, sym);
}
void operator () ( glyph_symbolizer const& sym)
{
ptree &node = rule_.push_back(
ptree::value_type("GlyphSymbolizer", ptree())
)->second;
glyph_symbolizer dfl("<no default>", expression_ptr());
// face_name
set_attr( node, "face-name", sym.get_face_name() );
// char
if (sym.get_char()) {
const std::string &str =
to_expression_string(*sym.get_char());
set_attr( node, "char", str );
}
// angle
if (sym.get_angle()) {
const std::string &str =
to_expression_string(*sym.get_angle());
set_attr( node, "angle", str );
}
// value
if (sym.get_value()) {
const std::string &str =
to_expression_string(*sym.get_value());
set_attr( node, "value", str );
}
// size
if (sym.get_size()) {
const std::string &str =
to_expression_string(*sym.get_size());
set_attr( node, "size", str );
}
// color
if (sym.get_color()) {
const std::string &str =
to_expression_string(*sym.get_color());
set_attr( node, "color", str );
}
// colorizer
if (sym.get_colorizer()) {
serialize_raster_colorizer(node, sym.get_colorizer(),
explicit_defaults_);
}
// allow_overlap
if (sym.get_allow_overlap() != dfl.get_allow_overlap() || explicit_defaults_ )
{
set_attr( node, "allow-overlap", sym.get_allow_overlap() );
}
// avoid_edges
if (sym.get_avoid_edges() != dfl.get_avoid_edges() || explicit_defaults_ )
{
set_attr( node, "avoid-edges", sym.get_avoid_edges() );
}
// displacement
position displacement = sym.get_displacement();
if ( displacement.get<0>() != dfl.get_displacement().get<0>() || explicit_defaults_ )
{
set_attr( node, "dx", displacement.get<0>() );
}
if ( displacement.get<1>() != dfl.get_displacement().get<1>() || explicit_defaults_ )
{
set_attr( node, "dy", displacement.get<1>() );
}
// halo fill & radius
const color & c = sym.get_halo_fill();
if ( c != dfl.get_halo_fill() || explicit_defaults_ )
{
set_attr( node, "halo-fill", c );
}
if (sym.get_halo_radius() != dfl.get_halo_radius() || explicit_defaults_ )
{
set_attr( node, "halo-radius", sym.get_halo_radius() );
}
// angle_mode
if (sym.get_angle_mode() != dfl.get_angle_mode() || explicit_defaults_ )
{
set_attr( node, "angle-mode", sym.get_angle_mode() );
}
add_metawriter_attributes(node, sym);
}
private:
serialize_symbolizer();
@ -451,134 +359,28 @@ private:
}
void add_font_attributes(ptree & node, const text_symbolizer & sym)
{
expression_ptr const& expr = sym.get_name();
const std::string & name = to_expression_string(*expr);
if (!name.empty()) {
ptree& text_node = node.push_back(ptree::value_type("<xmltext>", ptree()))->second;
text_node.put_value(name);
text_placements_ptr p = sym.get_placement_options();
p->properties.to_xml(node, explicit_defaults_);
/* Known types:
- text_placements_dummy: no handling required
- text_placements_simple: positions string
- text_placements_list: list string
*/
text_placements_simple *simple = dynamic_cast<text_placements_simple *>(p.get());
text_placements_list *list = dynamic_cast<text_placements_list *>(p.get());
if (simple) {
set_attr(node, "placment-type", "simple");
set_attr(node, "placements", simple->get_positions());
}
const std::string & face_name = sym.get_face_name();
if ( ! face_name.empty() ) {
set_attr( node, "face-name", face_name );
}
const std::string & fontset_name = sym.get_fontset().get_name();
if ( ! fontset_name.empty() ) {
set_attr( node, "fontset-name", fontset_name );
}
set_attr( node, "size", sym.get_text_size() );
set_attr( node, "fill", sym.get_fill() );
// pseudo-default-construct a text_symbolizer. It is used
// to avoid printing ofattributes with default values without
// repeating the default values here.
// maybe add a real, explicit default-ctor?
// FIXME
text_symbolizer dfl(expression_ptr(), "<no default>",
0, color(0,0,0) );
position displacement = sym.get_displacement();
if ( displacement.get<0>() != dfl.get_displacement().get<0>() || explicit_defaults_ )
{
set_attr( node, "dx", displacement.get<0>() );
}
if ( displacement.get<1>() != dfl.get_displacement().get<1>() || explicit_defaults_ )
{
set_attr( node, "dy", displacement.get<1>() );
}
if (sym.get_label_placement() != dfl.get_label_placement() || explicit_defaults_ )
{
set_attr( node, "placement", sym.get_label_placement() );
}
if (sym.get_vertical_alignment() != dfl.get_vertical_alignment() || explicit_defaults_ )
{
set_attr( node, "vertical-alignment", sym.get_vertical_alignment() );
}
if (sym.get_halo_radius() != dfl.get_halo_radius() || explicit_defaults_ )
{
set_attr( node, "halo-radius", sym.get_halo_radius() );
}
const color & c = sym.get_halo_fill();
if ( c != dfl.get_halo_fill() || explicit_defaults_ )
{
set_attr( node, "halo-fill", c );
}
if (sym.get_text_ratio() != dfl.get_text_ratio() || explicit_defaults_ )
{
set_attr( node, "text-ratio", sym.get_text_ratio() );
}
if (sym.get_wrap_width() != dfl.get_wrap_width() || explicit_defaults_ )
{
set_attr( node, "wrap-width", sym.get_wrap_width() );
}
if (sym.get_wrap_before() != dfl.get_wrap_before() || explicit_defaults_ )
{
set_attr( node, "wrap-before", sym.get_wrap_before() );
}
if (sym.get_wrap_char() != dfl.get_wrap_char() || explicit_defaults_ )
{
set_attr( node, "wrap-character", std::string(1, sym.get_wrap_char()) );
}
if (sym.get_text_transform() != dfl.get_text_transform() || explicit_defaults_ )
{
set_attr( node, "text-transform", sym.get_text_transform() );
}
if (sym.get_line_spacing() != dfl.get_line_spacing() || explicit_defaults_ )
{
set_attr( node, "line-spacing", sym.get_line_spacing() );
}
if (sym.get_character_spacing() != dfl.get_character_spacing() || explicit_defaults_ )
{
set_attr( node, "character-spacing", sym.get_character_spacing() );
}
if (sym.get_label_position_tolerance() != dfl.get_label_position_tolerance() || explicit_defaults_ )
{
set_attr( node, "label-position-tolerance", sym.get_label_position_tolerance() );
}
if (sym.get_label_spacing() != dfl.get_label_spacing() || explicit_defaults_ )
{
set_attr( node, "spacing", sym.get_label_spacing() );
}
if (sym.get_minimum_distance() != dfl.get_minimum_distance() || explicit_defaults_ )
{
set_attr( node, "minimum-distance", sym.get_minimum_distance() );
}
if (sym.get_minimum_padding() != dfl.get_minimum_padding() || explicit_defaults_ )
{
set_attr( node, "minimum-padding", sym.get_minimum_padding() );
}
if (sym.get_minimum_path_length() != dfl.get_minimum_path_length() || explicit_defaults_ )
{
set_attr( node, "minimum-path-length", sym.get_minimum_path_length() );
}
if (sym.get_allow_overlap() != dfl.get_allow_overlap() || explicit_defaults_ )
{
set_attr( node, "allow-overlap", sym.get_allow_overlap() );
}
if (sym.get_avoid_edges() != dfl.get_avoid_edges() || explicit_defaults_ )
{
set_attr( node, "avoid-edges", sym.get_avoid_edges() );
}
// for shield_symbolizer this is later overridden
if (sym.get_text_opacity() != dfl.get_text_opacity() || explicit_defaults_ )
{
set_attr( node, "opacity", sym.get_text_opacity() );
}
if (sym.get_max_char_angle_delta() != dfl.get_max_char_angle_delta() || explicit_defaults_ )
{
set_attr( node, "max-char-angle-delta", sym.get_max_char_angle_delta() );
}
if (sym.get_horizontal_alignment() != dfl.get_horizontal_alignment() || explicit_defaults_ )
{
set_attr( node, "horizontal-alignment", sym.get_horizontal_alignment() );
}
if (sym.get_justify_alignment() != dfl.get_justify_alignment() || explicit_defaults_ )
{
set_attr( node, "justify-alignment", sym.get_justify_alignment() );
if (list) {
set_attr(node, "placment-type", "list");
unsigned i;
text_symbolizer_properties *dfl = &(list->properties);
for (i=0; i < list->size(); i++) {
ptree &placement_node = node.push_back(ptree::value_type("Placement", ptree()))->second;
list->get(i).to_xml(placement_node, explicit_defaults_, *dfl);
dfl = &(list->get(i));
}
}
}

View File

@ -34,6 +34,15 @@
namespace mapnik
{
shield_symbolizer::shield_symbolizer(text_placements_ptr placements)
: text_symbolizer(placements),
symbolizer_with_image(),
unlock_image_(false),
no_text_(false),
shield_displacement_(boost::make_tuple<double,double>(0,0))
{
}
shield_symbolizer::shield_symbolizer(
expression_ptr name,
std::string const& face_name,

View File

@ -1,40 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
// mapnik
#include <mapnik/svg_renderer.hpp>
namespace mapnik
{
template <typename T>
void svg_renderer<T>::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
// nothing yet.
}
template void svg_renderer<std::ostream_iterator<char> >::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
}

View File

@ -72,7 +72,7 @@ metawriter_with_properties symbolizer_base::get_metawriter() const
symbolizer_with_image::symbolizer_with_image(path_expression_ptr file)
: image_filename_( file ),
opacity_(1.0f)
image_opacity_(1.0f)
{
matrix_[0] = 1.0;
@ -85,7 +85,7 @@ symbolizer_with_image::symbolizer_with_image(path_expression_ptr file)
symbolizer_with_image::symbolizer_with_image( symbolizer_with_image const& rhs)
: image_filename_(rhs.image_filename_),
opacity_(rhs.opacity_),
image_opacity_(rhs.image_opacity_),
matrix_(rhs.matrix_) {}
path_expression_ptr symbolizer_with_image::get_filename() const
@ -120,12 +120,12 @@ std::string const symbolizer_with_image::get_transform_string() const
void symbolizer_with_image::set_opacity(float opacity)
{
opacity_ = opacity;
image_opacity_ = opacity;
}
float symbolizer_with_image::get_opacity() const
{
return opacity_;
return image_opacity_;
}
} // end of namespace mapnik

View File

@ -22,11 +22,16 @@
#include <mapnik/text_placements.hpp>
#include <mapnik/text_placements_simple.hpp>
#include <mapnik/text_placements_list.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/text_processing.hpp>
#include <mapnik/ptree_helpers.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/optional.hpp>
namespace mapnik {
@ -36,14 +41,298 @@ using boost::spirit::ascii::space;
using phoenix::push_back;
using phoenix::ref;
using qi::_1;
using boost::optional;
text_symbolizer_properties::text_symbolizer_properties() :
label_placement(POINT_PLACEMENT),
halign(H_AUTO),
jalign(J_MIDDLE),
valign(V_AUTO),
label_spacing(0),
label_position_tolerance(0),
avoid_edges(false),
minimum_distance(0.0),
minimum_padding(0.0),
max_char_angle_delta(22.5 * M_PI/180.0),
force_odd_labels(false),
allow_overlap(false),
text_ratio(0),
wrap_width(0),
processor()
{
}
void text_symbolizer_properties::set_values_from_xml(boost::property_tree::ptree const &sym, std::map<std::string,font_set> const & fontsets)
{
optional<label_placement_e> placement_ = get_opt_attr<label_placement_e>(sym, "placement");
if (placement_) label_placement = *placement_;
optional<vertical_alignment_e> valign_ = get_opt_attr<vertical_alignment_e>(sym, "vertical-alignment");
if (valign_) valign = *valign_;
optional<unsigned> text_ratio_ = get_opt_attr<unsigned>(sym, "text-ratio");
if (text_ratio_) text_ratio = *text_ratio_;
optional<unsigned> wrap_width_ = get_opt_attr<unsigned>(sym, "wrap-width");
if (wrap_width_) wrap_width = *wrap_width_;
optional<unsigned> label_position_tolerance_ = get_opt_attr<unsigned>(sym, "label-position-tolerance");
if (label_position_tolerance_) label_position_tolerance = *label_position_tolerance_;
optional<unsigned> spacing_ = get_opt_attr<unsigned>(sym, "spacing");
if (spacing_) label_spacing = *spacing_;
optional<unsigned> minimum_distance_ = get_opt_attr<unsigned>(sym, "minimum-distance");
if (minimum_distance_) minimum_distance = *minimum_distance_;
optional<unsigned> min_padding_ = get_opt_attr<unsigned>(sym, "minimum-padding");
if (min_padding_) minimum_padding = *min_padding_;
optional<unsigned> min_path_length_ = get_opt_attr<unsigned>(sym, "minimum-path-length");
if (min_path_length_) minimum_path_length = *min_path_length_;
optional<boolean> avoid_edges_ = get_opt_attr<boolean>(sym, "avoid-edges");
if (avoid_edges_) avoid_edges = *avoid_edges_;
optional<boolean> allow_overlap_ = get_opt_attr<boolean>(sym, "allow-overlap");
if (allow_overlap_) allow_overlap = *allow_overlap_;
optional<horizontal_alignment_e> halign_ = get_opt_attr<horizontal_alignment_e>(sym, "horizontal-alignment");
if (halign_) halign = *halign_;
optional<justify_alignment_e> jalign_ = get_opt_attr<justify_alignment_e>(sym, "justify-alignment");
if (jalign_) jalign = *jalign_;
/* Attributes needing special care */
optional<std::string> orientation_ = get_opt_attr<std::string>(sym, "orientation");
if (orientation_) orientation = parse_expression(*orientation_, "utf8");
optional<double> dx = get_opt_attr<double>(sym, "dx");
if (dx) displacement.get<0>() = *dx;
optional<double> dy = get_opt_attr<double>(sym, "dy");
if (dy) displacement.get<1>() = *dy;
optional<double> max_char_angle_delta_ = get_opt_attr<double>(sym, "max-char-angle-delta");
if (max_char_angle_delta_) max_char_angle_delta=(*max_char_angle_delta_)*(M_PI/180);
processor.from_xml(sym, fontsets);
optional<std::string> name_ = get_opt_attr<std::string>(sym, "name");
if (name_) {
std::clog << "### WARNING: Using 'name' in TextSymbolizer/ShieldSymbolizer is deprecated!\n";
processor.set_old_style_expression(parse_expression(*name_, "utf8"));
}
}
void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_symbolizer_properties const &dfl) const
{
if (orientation)
{
const std::string & orientationstr = to_expression_string(*orientation);
if (!dfl.orientation || orientationstr != to_expression_string(*(dfl.orientation)) || explicit_defaults) {
set_attr(node, "orientation", orientationstr);
}
}
if (displacement.get<0>() != dfl.displacement.get<0>() || explicit_defaults)
{
set_attr(node, "dx", displacement.get<0>());
}
if (displacement.get<1>() != dfl.displacement.get<1>() || explicit_defaults)
{
set_attr(node, "dy", displacement.get<1>());
}
if (label_placement != dfl.label_placement || explicit_defaults)
{
set_attr(node, "placement", label_placement);
}
if (valign != dfl.valign || explicit_defaults)
{
set_attr(node, "vertical-alignment", valign);
}
if (text_ratio != dfl.text_ratio || explicit_defaults)
{
set_attr(node, "text-ratio", text_ratio);
}
if (wrap_width != dfl.wrap_width || explicit_defaults)
{
set_attr(node, "wrap-width", wrap_width);
}
if (label_position_tolerance != dfl.label_position_tolerance || explicit_defaults)
{
set_attr(node, "label-position-tolerance", label_position_tolerance);
}
if (label_spacing != dfl.label_spacing || explicit_defaults)
{
set_attr(node, "spacing", label_spacing);
}
if (minimum_distance != dfl.minimum_distance || explicit_defaults)
{
set_attr(node, "minimum-distance", minimum_distance);
}
if (minimum_padding != dfl.minimum_padding || explicit_defaults)
{
set_attr(node, "minimum-padding", minimum_padding);
}
if (minimum_path_length != dfl.minimum_path_length || explicit_defaults)
{
set_attr(node, "minimum-path-length", minimum_path_length);
}
if (allow_overlap != dfl.allow_overlap || explicit_defaults)
{
set_attr(node, "allow-overlap", allow_overlap);
}
if (avoid_edges != dfl.avoid_edges || explicit_defaults)
{
set_attr(node, "avoid-edges", avoid_edges);
}
if (max_char_angle_delta != dfl.max_char_angle_delta || explicit_defaults)
{
set_attr(node, "max-char-angle-delta", max_char_angle_delta);
}
if (halign != dfl.halign || explicit_defaults)
{
set_attr(node, "horizontal-alignment", halign);
}
if (jalign != dfl.jalign || explicit_defaults)
{
set_attr(node, "justify-alignment", jalign);
}
if (valign != dfl.valign || explicit_defaults)
{
set_attr(node, "vertical-alignment", valign);
}
processor.to_xml(node, explicit_defaults, dfl.processor);
}
char_properties::char_properties() :
text_size(10.0),
character_spacing(0),
line_spacing(0),
text_opacity(1.0),
wrap_before(false),
wrap_char(' '),
text_transform(NONE),
fill(color(0,0,0)),
halo_fill(color(255,255,255)),
halo_radius(0)
{
}
void char_properties::set_values_from_xml(boost::property_tree::ptree const &sym, std::map<std::string,font_set> const & fontsets)
{
optional<double> text_size_ = get_opt_attr<double>(sym, "size");
if (text_size_) text_size = *text_size_;
optional<double> character_spacing_ = get_opt_attr<double>(sym, "character-spacing");
if (character_spacing_) character_spacing = *character_spacing_;
optional<color> fill_ = get_opt_attr<color>(sym, "fill");
if (fill_) fill = *fill_;
optional<color> halo_fill_ = get_opt_attr<color>(sym, "halo-fill");
if (halo_fill_) halo_fill = *halo_fill_;
optional<double> halo_radius_ = get_opt_attr<double>(sym, "halo-radius");
if (halo_radius_) halo_radius = *halo_radius_;
optional<boolean> wrap_before_ = get_opt_attr<boolean>(sym, "wrap-before");
if (wrap_before_) wrap_before = *wrap_before_;
optional<text_transform_e> tconvert_ = get_opt_attr<text_transform_e>(sym, "text-transform");
if (tconvert_) text_transform = *tconvert_;
optional<double> line_spacing_ = get_opt_attr<double>(sym, "line-spacing");
if (line_spacing_) line_spacing = *line_spacing_;
optional<double> opacity_ = get_opt_attr<double>(sym, "opacity");
if (opacity_) text_opacity = *opacity_;
optional<std::string> wrap_char_ = get_opt_attr<std::string>(sym, "wrap-character");
if (wrap_char_ && (*wrap_char_).size() > 0) wrap_char = ((*wrap_char_)[0]);
optional<std::string> face_name_ = get_opt_attr<std::string>(sym, "face-name");
if (face_name_)
{
face_name = *face_name_;
}
optional<std::string> fontset_name_ = get_opt_attr<std::string>(sym, "fontset-name");
if (fontset_name_) {
std::map<std::string,font_set>::const_iterator itr = fontsets.find(*fontset_name_);
if (itr != fontsets.end())
{
fontset = itr->second;
} else
{
throw config_error("Unable to find any fontset named '" + *fontset_name_ + "'");
}
}
if (!face_name.empty() && !fontset.get_name().empty())
{
throw config_error(std::string("Can't have both face-name and fontset-name"));
}
if (face_name.empty() && fontset.get_name().empty())
{
throw config_error(std::string("Must have face-name or fontset-name"));
}
}
void char_properties::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const &dfl) const
{
const std::string & fontset_name = fontset.get_name();
const std::string & dfl_fontset_name = dfl.fontset.get_name();
if (fontset_name != dfl_fontset_name || explicit_defaults)
{
set_attr(node, "fontset-name", fontset_name);
}
if (face_name != dfl.face_name || explicit_defaults)
{
set_attr(node, "face-name", face_name);
}
if (text_size != dfl.text_size || explicit_defaults)
{
set_attr(node, "size", text_size);
}
if (fill != dfl.fill || explicit_defaults)
{
set_attr(node, "fill", fill);
}
if (halo_radius != dfl.halo_radius || explicit_defaults)
{
set_attr(node, "halo-radius", halo_radius);
}
if (halo_fill != dfl.halo_fill || explicit_defaults)
{
set_attr(node, "halo-fill", halo_fill);
}
if (wrap_before != dfl.wrap_before || explicit_defaults)
{
set_attr(node, "wrap-before", wrap_before);
}
if (wrap_char != dfl.wrap_char || explicit_defaults)
{
set_attr(node, "wrap-character", std::string(1, wrap_char));
}
if (text_transform != dfl.text_transform || explicit_defaults)
{
set_attr(node, "text-transform", text_transform);
}
if (line_spacing != dfl.line_spacing || explicit_defaults)
{
set_attr(node, "line-spacing", line_spacing);
}
if (character_spacing != dfl.character_spacing || explicit_defaults)
{
set_attr(node, "character-spacing", character_spacing);
}
// for shield_symbolizer this is later overridden
if (text_opacity != dfl.text_opacity || explicit_defaults)
{
set_attr(node, "opacity", text_opacity);
}
}
/************************************************************************/
text_placements::text_placements() : properties()
{
}
std::set<expression_ptr> text_placements::get_all_expressions()
{
std::set<expression_ptr> result, tmp;
tmp = properties.processor.get_all_expressions();
result.insert(tmp.begin(), tmp.end());
result.insert(properties.orientation);
return result;
}
/************************************************************************/
text_placement_info::text_placement_info(text_placements const* parent):
displacement(parent->displacement_),
text_size(parent->text_size_), halign(parent->halign_), jalign(parent->jalign_),
valign(parent->valign_)
properties(parent->properties),
scale_factor(1),
has_dimensions(false),
collect_extents(false)
{
}
@ -55,73 +344,81 @@ bool text_placement_info_dummy::next()
return true;
}
bool text_placement_info_dummy::next_position_only()
{
if (position_state) return false;
position_state++;
return true;
}
text_placement_info_ptr text_placements_dummy::get_placement_info() const
{
return text_placement_info_ptr(new text_placement_info_dummy(this));
}
void text_placement_info::init(double scale_factor_,
unsigned w, unsigned h, bool has_dimensions_)
{
scale_factor = scale_factor_;
dimensions = std::make_pair(w, h);
has_dimensions = has_dimensions_;
}
/************************************************************************/
bool text_placement_info_simple::next()
{
position_state = 0;
if (state == 0) {
text_size = parent_->text_size_;
} else {
if (state > parent_->text_sizes_.size()) return false;
text_size = parent_->text_sizes_[state-1];
while (1) {
if (state == 0) {
properties.processor.defaults.text_size = parent_->properties.processor.defaults.text_size;
} else {
if (state > parent_->text_sizes_.size()) return false;
properties.processor.defaults.text_size = parent_->text_sizes_[state-1];
}
if (!next_position_only()) {
state++;
position_state = 0;
} else {
break;
}
}
state++;
return true;
}
bool text_placement_info_simple::next_position_only()
{
const position &pdisp = parent_->properties.displacement;
position &displacement = properties.displacement;
if (position_state >= parent_->direction_.size()) return false;
directions_t dir = parent_->direction_[position_state];
switch (dir) {
case EXACT_POSITION:
displacement = parent_->displacement_;
displacement = pdisp;
break;
case NORTH:
displacement = boost::make_tuple(0, -abs(parent_->displacement_.get<1>()));
displacement = boost::make_tuple(0, -abs(pdisp.get<1>()));
break;
case EAST:
displacement = boost::make_tuple(abs(parent_->displacement_.get<0>()), 0);
displacement = boost::make_tuple(abs(pdisp.get<0>()), 0);
break;
case SOUTH:
displacement = boost::make_tuple(0, abs(parent_->displacement_.get<1>()));
displacement = boost::make_tuple(0, abs(pdisp.get<1>()));
break;
case WEST:
displacement = boost::make_tuple(-abs(parent_->displacement_.get<0>()), 0);
displacement = boost::make_tuple(-abs(pdisp.get<0>()), 0);
break;
case NORTHEAST:
displacement = boost::make_tuple(
abs(parent_->displacement_.get<0>()),
-abs(parent_->displacement_.get<1>()));
abs(pdisp.get<0>()),
-abs(pdisp.get<1>()));
break;
case SOUTHEAST:
displacement = boost::make_tuple(
abs(parent_->displacement_.get<0>()),
abs(parent_->displacement_.get<1>()));
abs(pdisp.get<0>()),
abs(pdisp.get<1>()));
break;
case NORTHWEST:
displacement = boost::make_tuple(
-abs(parent_->displacement_.get<0>()),
-abs(parent_->displacement_.get<1>()));
-abs(pdisp.get<0>()),
-abs(pdisp.get<1>()));
break;
case SOUTHWEST:
displacement = boost::make_tuple(
-abs(parent_->displacement_.get<0>()),
abs(parent_->displacement_.get<1>()));
-abs(pdisp.get<0>()),
abs(pdisp.get<1>()));
break;
default:
std::cerr << "WARNING: Unknown placement\n";
@ -130,13 +427,12 @@ bool text_placement_info_simple::next_position_only()
return true;
}
text_placement_info_ptr text_placements_simple::get_placement_info() const
{
return text_placement_info_ptr(new text_placement_info_simple(this));
}
/** Positiion string: [POS][SIZE]
/** Position string: [POS][SIZE]
* [POS] is any combination of
* N, E, S, W, NE, SE, NW, SW, X (exact position) (separated by commas)
* [SIZE] is a list of font sizes, separated by commas. The first font size
@ -196,4 +492,75 @@ text_placements_simple::text_placements_simple(std::string positions)
{
set_positions(positions);
}
std::string text_placements_simple::get_positions()
{
return positions_; //TODO: Build string from data in direction_ and text_sizes_
}
/***************************************************************************/
bool text_placement_info_list::next()
{
if (state == 0) {
properties = parent_->properties;
} else {
if (state-1 >= parent_->list_.size()) return false;
properties = parent_->list_[state-1];
}
state++;
return true;
}
text_symbolizer_properties & text_placements_list::add()
{
if (list_.size()) {
text_symbolizer_properties &last = list_.back();
list_.push_back(last); //Preinitialize with old values
} else {
list_.push_back(properties);
}
return list_.back();
}
text_symbolizer_properties & text_placements_list::get(unsigned i)
{
return list_[i];
}
/***************************************************************************/
text_placement_info_ptr text_placements_list::get_placement_info() const
{
return text_placement_info_ptr(new text_placement_info_list(this));
}
text_placements_list::text_placements_list() : text_placements(), list_(0)
{
}
std::set<expression_ptr> text_placements_list::get_all_expressions()
{
std::set<expression_ptr> result, tmp;
tmp = properties.processor.get_all_expressions();
result.insert(tmp.begin(), tmp.end());
result.insert(properties.orientation);
std::vector<text_symbolizer_properties>::const_iterator it;
for (it=list_.begin(); it != list_.end(); it++)
{
tmp = it->processor.get_all_expressions();
result.insert(tmp.begin(), tmp.end());
result.insert(it->orientation);
}
return result;
}
unsigned text_placements_list::size() const
{
return list_.size();
}
} //namespace

447
src/text_processing.cpp Normal file
View File

@ -0,0 +1,447 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#include <mapnik/text_processing.hpp>
#include <mapnik/text_placements.hpp>
#include <mapnik/color.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/ptree_helpers.hpp>
#include <mapnik/expression_string.hpp>
#include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>
#include <stack>
namespace mapnik {
using boost::property_tree::ptree;
using boost::optional;
class abstract_token
{
public:
virtual ~abstract_token() {}
virtual ptree *to_xml(ptree *node) = 0;
};
class abstract_formating_token : public abstract_token
{
public:
virtual void apply(char_properties &p, Feature const& feature) = 0;
};
class abstract_text_token : public abstract_token
{
public:
virtual UnicodeString to_string(Feature const& feature) = 0;
};
class end_format_token : public abstract_token
{
public:
end_format_token() {}
ptree *to_xml(ptree *node);
};
class expression_token: public abstract_text_token
{
public:
expression_token(expression_ptr text);
UnicodeString to_string(Feature const& feature);
ptree *to_xml(ptree *node);
void set_expression(expression_ptr text);
expression_ptr get_expression();
private:
expression_ptr text_;
};
class fixed_formating_token : public abstract_formating_token
{
public:
fixed_formating_token();
virtual void apply(char_properties &p, Feature const& feature);
ptree* to_xml(ptree *node);
void from_xml(ptree const& node);
void set_face_name(optional<std::string> face_name);
void set_text_size(optional<unsigned> text_size);
void set_character_spacing(optional<unsigned> character_spacing);
void set_line_spacing(optional<unsigned> line_spacing);
void set_text_opacity(optional<double> opacity);
void set_wrap_before(optional<boolean> wrap_before);
void set_wrap_char(optional<unsigned> wrap_char);
void set_text_transform(optional<text_transform_e> text_trans);
void set_fill(optional<color> fill);
void set_halo_fill(optional<color> halo_fill);
void set_halo_radius(optional<double> radius);
private:
boost::optional<std::string> face_name_;
// font_set fontset;
boost::optional<unsigned> text_size_;
boost::optional<unsigned> character_spacing_;
boost::optional<unsigned> line_spacing_;
boost::optional<double> text_opacity_;
boost::optional<boolean> wrap_before_;
boost::optional<unsigned> wrap_char_;
boost::optional<text_transform_e> text_transform_;
boost::optional<color> fill_;
boost::optional<color> halo_fill_;
boost::optional<double> halo_radius_;
};
/************************************************************/
expression_token::expression_token(expression_ptr text):
text_(text)
{
}
void expression_token::set_expression(expression_ptr text)
{
text_ = text;
}
expression_ptr expression_token::get_expression()
{
return text_;
}
UnicodeString expression_token::to_string(const Feature &feature)
{
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature), *text_);
return result.to_unicode();
}
ptree *expression_token::to_xml(ptree *node)
{
ptree &new_node = node->push_back(ptree::value_type(
"<xmltext>", ptree()))->second;
new_node.put_value(to_expression_string(*text_));
return &new_node;
}
/************************************************************/
fixed_formating_token::fixed_formating_token():
fill_()
{
}
void fixed_formating_token::apply(char_properties &p, const Feature &feature)
{
if (face_name_) p.face_name = *face_name_;
if (text_size_) p.text_size = *text_size_;
if (character_spacing_) p.character_spacing = *character_spacing_;
if (line_spacing_) p.line_spacing = *line_spacing_;
if (text_opacity_) p.text_opacity = *text_opacity_;
if (wrap_before_) p.wrap_before = *wrap_before_;
if (wrap_char_) p.wrap_char = *wrap_char_;
if (text_transform_) p.text_transform = *text_transform_;
if (fill_) p.fill = *fill_;
if (halo_fill_) p.halo_fill = *halo_fill_;
if (halo_radius_) p.halo_radius = *halo_radius_;
}
ptree *fixed_formating_token::to_xml(ptree *node)
{
ptree &new_node = node->push_back(ptree::value_type("Format", ptree()))->second;
if (face_name_) set_attr(new_node, "face-name", face_name_);
if (text_size_) set_attr(new_node, "size", text_size_);
if (character_spacing_) set_attr(new_node, "character-spacing", character_spacing_);
if (line_spacing_) set_attr(new_node, "line-spacing", line_spacing_);
if (text_opacity_) set_attr(new_node, "opacity", text_opacity_);
if (wrap_before_) set_attr(new_node, "wrap-before", wrap_before_);
if (wrap_char_) set_attr(new_node, "wrap-character", wrap_char_);
if (text_transform_) set_attr(new_node, "text-transform", text_transform_);
if (fill_) set_attr(new_node, "fill", fill_);
if (halo_fill_) set_attr(new_node, "halo-fill", halo_fill_);
if (halo_radius_) set_attr(new_node, "halo-radius", halo_radius_);
return &new_node;
}
void fixed_formating_token::from_xml(ptree const& node)
{
set_face_name(get_opt_attr<std::string>(node, "face-name"));
/*TODO: Fontset is problematic. We don't have the fontsets pointer here... */
set_text_size(get_opt_attr<unsigned>(node, "size"));
set_character_spacing(get_opt_attr<unsigned>(node, "character-spacing"));
set_line_spacing(get_opt_attr<unsigned>(node, "line-spacing"));
set_text_opacity(get_opt_attr<double>(node, "opactity"));
set_wrap_before(get_opt_attr<boolean>(node, "wrap-before"));
set_wrap_char(get_opt_attr<unsigned>(node, "wrap-character"));
set_text_transform(get_opt_attr<text_transform_e>(node, "text-transform"));
set_fill(get_opt_attr<color>(node, "fill"));
set_halo_fill(get_opt_attr<color>(node, "halo-fill"));
set_halo_radius(get_opt_attr<double>(node, "halo-radius"));
}
void fixed_formating_token::set_face_name(optional<std::string> face_name)
{
face_name_ = face_name;
}
void fixed_formating_token::set_text_size(optional<unsigned> text_size)
{
text_size_ = text_size;
}
void fixed_formating_token::set_character_spacing(optional<unsigned> character_spacing)
{
character_spacing_ = character_spacing;
}
void fixed_formating_token::set_line_spacing(optional<unsigned> line_spacing)
{
line_spacing_ = line_spacing;
}
void fixed_formating_token::set_text_opacity(optional<double> text_opacity)
{
text_opacity_ = text_opacity;
}
void fixed_formating_token::set_wrap_before(optional<boolean> wrap_before)
{
wrap_before_ = wrap_before;
}
void fixed_formating_token::set_wrap_char(optional<unsigned> wrap_char)
{
wrap_char_ = wrap_char;
}
void fixed_formating_token::set_text_transform(optional<text_transform_e> text_transform)
{
text_transform_ = text_transform;
}
void fixed_formating_token::set_fill(optional<color> c)
{
fill_ = c;
}
void fixed_formating_token::set_halo_fill(optional<color> c)
{
halo_fill_ = c;
}
void fixed_formating_token::set_halo_radius(optional<double> radius)
{
halo_radius_ = radius;
}
/************************************************************/
ptree *end_format_token::to_xml(ptree *node)
{
return 0;
}
/************************************************************/
text_processor::text_processor():
list_(), clear_on_write(false)
{
}
void text_processor::push_back(abstract_token *token)
{
if (clear_on_write) list_.clear();
clear_on_write = false;
list_.push_back(token);
}
void text_processor::from_xml(const boost::property_tree::ptree &pt, std::map<std::string,font_set> const &fontsets)
{
clear_on_write = true;
defaults.set_values_from_xml(pt, fontsets);
from_xml_recursive(pt, fontsets);
}
void text_processor::from_xml_recursive(const boost::property_tree::ptree &pt, std::map<std::string,font_set> const &fontsets)
{
ptree::const_iterator itr = pt.begin();
ptree::const_iterator end = pt.end();
for (; itr != end; ++itr) {
if (itr->first == "<xmltext>") {
std::string data = itr->second.data();
boost::trim(data);
if (data.empty()) continue;
expression_token *token = new expression_token(parse_expression(data, "utf8"));
push_back(token);
} else if (itr->first == "Format") {
fixed_formating_token *token = new fixed_formating_token();
token->from_xml(itr->second);
push_back(token);
from_xml_recursive(itr->second, fontsets); /* Parse children, making a list out of a tree. */
push_back(new end_format_token());
} else if (itr->first != "<xmlcomment>" && itr->first != "<xmlattr>" && itr->first != "Placement") {
std::cerr << "Unknown item" << itr->first;
}
}
}
void text_processor::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_processor const& dfl) const
{
defaults.to_xml(node, explicit_defaults, dfl.defaults);
std::list<abstract_token *>::const_iterator itr = list_.begin();
std::list<abstract_token *>::const_iterator end = list_.end();
std::stack<ptree *> nodes;
ptree *current_node = &node;
for (; itr != end; ++itr) {
abstract_token *token = *itr;
ptree *new_node = token->to_xml(current_node);
if (dynamic_cast<abstract_formating_token *>(token)) {
nodes.push(current_node);
current_node = new_node;
} else if (dynamic_cast<end_format_token *>(token)) {
current_node = nodes.top();
nodes.pop();
}
}
}
void text_processor::process(processed_text &output, Feature const& feature)
{
std::list<abstract_token *>::const_iterator itr = list_.begin();
std::list<abstract_token *>::const_iterator end = list_.end();
std::stack<char_properties> formats;
formats.push(defaults);
for (; itr != end; ++itr) {
abstract_text_token *text = dynamic_cast<abstract_text_token *>(*itr);
abstract_formating_token *format = dynamic_cast<abstract_formating_token *>(*itr);;
end_format_token *end = dynamic_cast<end_format_token *>(*itr);;
if (text) {
UnicodeString text_str = text->to_string(feature);
char_properties const& p = formats.top();
/* TODO: Make a class out of text_transform which does the work! */
if (p.text_transform == UPPERCASE)
{
text_str = text_str.toUpper();
}
else if (p.text_transform == LOWERCASE)
{
text_str = text_str.toLower();
}
else if (p.text_transform == CAPITALIZE)
{
text_str = text_str.toTitle(NULL);
}
if (text_str.length() > 0) {
output.push_back(processed_expression(p, text_str));
} else {
#ifdef MAPNIK_DEBUG
std::cerr << "Warning: Empty expression.\n";
#endif
}
} else if (format) {
char_properties next_properties = formats.top();
format->apply(next_properties, feature);
formats.push(next_properties);
} else if (end) {
/* Always keep at least the defaults_ on stack. */
if (formats.size() > 1) {
formats.pop();
} else {
std::cerr << "Warning: Internal mapnik error. More elements popped than pushed in text_processor::process()\n";
output.clear();
return;
}
}
}
if (formats.size() != 1) {
std::cerr << "Warning: Internal mapnik error. Less elements popped than pushed in text_processor::process()\n";
}
}
std::set<expression_ptr> text_processor::get_all_expressions() const
{
std::set<expression_ptr> result;
std::list<abstract_token *>::const_iterator itr = list_.begin();
std::list<abstract_token *>::const_iterator end = list_.end();
for (; itr != end; ++itr) {
expression_token *text = dynamic_cast<expression_token *>(*itr);
if (text) result.insert(text->get_expression());
}
return result;
}
void text_processor::set_old_style_expression(expression_ptr expr)
{
list_.push_back(new expression_token(expr));
}
/************************************************************/
void processed_text::push_back(processed_expression const& exp)
{
expr_list_.push_back(exp);
}
processed_text::expression_list::const_iterator processed_text::begin()
{
return expr_list_.begin();
}
processed_text::expression_list::const_iterator processed_text::end()
{
return expr_list_.end();
}
processed_text::processed_text(face_manager<freetype_engine> & font_manager, double scale_factor)
: font_manager_(font_manager), scale_factor_(scale_factor)
{
}
void processed_text::clear()
{
info_.clear();
expr_list_.clear();
}
string_info &processed_text::get_string_info()
{
//info_.clear(); TODO: if this function is called twice invalid results are returned, so clear string_info first
expression_list::iterator itr = expr_list_.begin();
expression_list::iterator end = expr_list_.end();
for (; itr != end; ++itr)
{
char_properties const &p = itr->p;
face_set_ptr faces = font_manager_.get_face_set(p.face_name, p.fontset);
if (faces->size() <= 0)
{
throw config_error("Unable to find specified font face '" + p.face_name + "'");
}
faces->set_character_sizes(p.text_size * scale_factor_);
faces->get_string_info(info_, itr->str, &(itr->p));
info_.add_text(itr->str);
}
return info_;
}
} /* namespace */

View File

@ -22,15 +22,11 @@
//$Id$
#include <string>
//mapnik
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/text_placements.hpp>
#include <mapnik/text_placements_simple.hpp>
// boost
#include <boost/scoped_ptr.hpp>
#include <mapnik/text_processing.hpp>
namespace mapnik
{
@ -90,471 +86,363 @@ static const char * text_transform_strings[] = {
IMPLEMENT_ENUM( text_transform_e, text_transform_strings )
static const char * placement_type_strings[] = {
"dummy",
"simple",
""
};
IMPLEMENT_ENUM( placement_type_e, placement_type_strings )
text_symbolizer::text_symbolizer(text_placements_ptr placements)
: symbolizer_base(),
placement_options_(placements)
{
}
text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_name,
float size, color const& fill,
text_placements_ptr placements)
: symbolizer_base(),
name_(name),
face_name_(face_name),
//fontset_(default_fontset),
text_ratio_(0),
wrap_width_(0),
wrap_char_(' '),
text_transform_(NONE),
line_spacing_(0),
character_spacing_(0),
label_spacing_(0),
label_position_tolerance_(0),
force_odd_labels_(false),
max_char_angle_delta_(22.5 * M_PI/180.0),
fill_(fill),
halo_fill_(color(255,255,255)),
halo_radius_(0.0),
label_p_(POINT_PLACEMENT),
anchor_(0.0,0.5),
avoid_edges_(false),
minimum_distance_(0.0),
minimum_padding_(0.0),
minimum_path_length_(0.0),
overlap_(false),
text_opacity_(1.0),
wrap_before_(false),
placement_options_(placements)
{
set_name(name);
set_face_name(face_name);
set_text_size(size);
set_fill(fill);
}
text_symbolizer::text_symbolizer(expression_ptr name, float size, color const& fill,
text_placements_ptr placements)
: symbolizer_base(),
name_(name),
//face_name_(""),
//fontset_(default_fontset),
text_ratio_(0),
wrap_width_(0),
wrap_char_(' '),
text_transform_(NONE),
line_spacing_(0),
character_spacing_(0),
label_spacing_(0),
label_position_tolerance_(0),
force_odd_labels_(false),
max_char_angle_delta_(22.5 * M_PI/180.0),
fill_(fill),
halo_fill_(color(255,255,255)),
halo_radius_(0.0),
label_p_(POINT_PLACEMENT),
anchor_(0.0,0.5),
avoid_edges_(false),
minimum_distance_(0.0),
minimum_padding_(0.0),
minimum_path_length_(0.0),
overlap_(false),
text_opacity_(1.0),
wrap_before_(false),
placement_options_(placements)
{
set_name(name);
set_text_size(size);
set_fill(fill);
}
text_symbolizer::text_symbolizer(text_symbolizer const& rhs)
: symbolizer_base(rhs),
name_(rhs.name_),
orientation_(rhs.orientation_),
face_name_(rhs.face_name_),
fontset_(rhs.fontset_),
text_ratio_(rhs.text_ratio_),
wrap_width_(rhs.wrap_width_),
wrap_char_(rhs.wrap_char_),
text_transform_(rhs.text_transform_),
line_spacing_(rhs.line_spacing_),
character_spacing_(rhs.character_spacing_),
label_spacing_(rhs.label_spacing_),
label_position_tolerance_(rhs.label_position_tolerance_),
force_odd_labels_(rhs.force_odd_labels_),
max_char_angle_delta_(rhs.max_char_angle_delta_),
fill_(rhs.fill_),
halo_fill_(rhs.halo_fill_),
halo_radius_(rhs.halo_radius_),
label_p_(rhs.label_p_),
anchor_(rhs.anchor_),
avoid_edges_(rhs.avoid_edges_),
minimum_distance_(rhs.minimum_distance_),
minimum_padding_(rhs.minimum_padding_),
minimum_path_length_(rhs.minimum_path_length_),
overlap_(rhs.overlap_),
text_opacity_(rhs.text_opacity_),
wrap_before_(rhs.wrap_before_),
placement_options_(rhs.placement_options_) /*TODO: Copy options! */ {}
placement_options_(rhs.placement_options_) /*TODO: Copy options! */
{
}
text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other)
{
if (this == &other)
return *this;
name_ = other.name_;
orientation_ = other.orientation_;
face_name_ = other.face_name_;
fontset_ = other.fontset_;
text_ratio_ = other.text_ratio_;
wrap_width_ = other.wrap_width_;
wrap_char_ = other.wrap_char_;
text_transform_ = other.text_transform_;
line_spacing_ = other.line_spacing_;
character_spacing_ = other.character_spacing_;
label_spacing_ = other.label_spacing_;
label_position_tolerance_ = other.label_position_tolerance_;
force_odd_labels_ = other.force_odd_labels_;
max_char_angle_delta_ = other.max_char_angle_delta_;
fill_ = other.fill_;
halo_fill_ = other.halo_fill_;
halo_radius_ = other.halo_radius_;
label_p_ = other.label_p_;
anchor_ = other.anchor_;
avoid_edges_ = other.avoid_edges_;
minimum_distance_ = other.minimum_distance_;
minimum_padding_ = other.minimum_padding_;
minimum_path_length_ = other.minimum_path_length_;
overlap_ = other.overlap_;
text_opacity_ = other.text_opacity_;
wrap_before_ = other.wrap_before_;
placement_options_ = other.placement_options_; /*TODO?*/
std::cout << "TODO: Metawriter (text_symbolizer::operator=)\n";
placement_options_ = other.placement_options_; /*TODO: Copy options? */
std::clog << "TODO: Metawriter (text_symbolizer::operator=)\n";
return *this;
}
expression_ptr text_symbolizer::get_name() const
{
return name_;
return expression_ptr();
}
void text_symbolizer::set_name(expression_ptr name)
{
name_ = name;
placement_options_->properties.processor.set_old_style_expression(name);
}
expression_ptr text_symbolizer::get_orientation() const
{
return orientation_;
return placement_options_->properties.orientation;
}
void text_symbolizer::set_orientation(expression_ptr orientation)
{
orientation_ = orientation;
placement_options_->properties.orientation = orientation;
}
std::string const& text_symbolizer::get_face_name() const
{
return face_name_;
return placement_options_->properties.processor.defaults.face_name;
}
void text_symbolizer::set_face_name(std::string face_name)
{
face_name_ = face_name;
placement_options_->properties.processor.defaults.face_name = face_name;
}
void text_symbolizer::set_fontset(font_set const& fontset)
{
fontset_ = fontset;
placement_options_->properties.processor.defaults.fontset = fontset;
}
font_set const& text_symbolizer::get_fontset() const
{
return fontset_;
return placement_options_->properties.processor.defaults.fontset;
}
unsigned text_symbolizer::get_text_ratio() const
{
return text_ratio_;
return placement_options_->properties.text_ratio;
}
void text_symbolizer::set_text_ratio(unsigned ratio)
{
text_ratio_ = ratio;
placement_options_->properties.text_ratio = ratio;
}
unsigned text_symbolizer::get_wrap_width() const
{
return wrap_width_;
return placement_options_->properties.wrap_width;
}
void text_symbolizer::set_wrap_width(unsigned width)
{
wrap_width_ = width;
placement_options_->properties.wrap_width = width;
}
bool text_symbolizer::get_wrap_before() const
{
return wrap_before_;
return placement_options_->properties.processor.defaults.wrap_before;
}
void text_symbolizer::set_wrap_before(bool wrap_before)
{
wrap_before_ = wrap_before;
placement_options_->properties.processor.defaults.wrap_before = wrap_before;
}
unsigned char text_symbolizer::get_wrap_char() const
{
return wrap_char_;
return placement_options_->properties.processor.defaults.wrap_char;
}
std::string text_symbolizer::get_wrap_char_string() const
{
return std::string(1, wrap_char_);
return std::string(1, placement_options_->properties.processor.defaults.wrap_char);
}
void text_symbolizer::set_wrap_char(unsigned char character)
{
wrap_char_ = character;
placement_options_->properties.processor.defaults.wrap_char = character;
}
void text_symbolizer::set_wrap_char_from_string(std::string const& character)
{
wrap_char_ = (character)[0];
placement_options_->properties.processor.defaults.wrap_char = (character)[0];
}
text_transform_e text_symbolizer::get_text_transform() const
{
return text_transform_;
return placement_options_->properties.processor.defaults.text_transform;
}
void text_symbolizer::set_text_transform(text_transform_e convert)
{
text_transform_ = convert;
placement_options_->properties.processor.defaults.text_transform = convert;
}
unsigned text_symbolizer::get_line_spacing() const
{
return line_spacing_;
return placement_options_->properties.processor.defaults.line_spacing;
}
void text_symbolizer::set_line_spacing(unsigned spacing)
{
line_spacing_ = spacing;
placement_options_->properties.processor.defaults.line_spacing = spacing;
}
unsigned text_symbolizer::get_character_spacing() const
{
return character_spacing_;
return placement_options_->properties.processor.defaults.character_spacing;
}
void text_symbolizer::set_character_spacing(unsigned spacing)
{
character_spacing_ = spacing;
placement_options_->properties.processor.defaults.character_spacing = spacing;
}
unsigned text_symbolizer::get_label_spacing() const
{
return label_spacing_;
return placement_options_->properties.label_spacing;
}
void text_symbolizer::set_label_spacing(unsigned spacing)
{
label_spacing_ = spacing;
placement_options_->properties.label_spacing = spacing;
}
unsigned text_symbolizer::get_label_position_tolerance() const
{
return label_position_tolerance_;
return placement_options_->properties.label_position_tolerance;
}
void text_symbolizer::set_label_position_tolerance(unsigned tolerance)
{
label_position_tolerance_ = tolerance;
placement_options_->properties.label_position_tolerance = tolerance;
}
bool text_symbolizer::get_force_odd_labels() const
{
return force_odd_labels_;
return placement_options_->properties.force_odd_labels;
}
void text_symbolizer::set_force_odd_labels(bool force)
{
force_odd_labels_ = force;
placement_options_->properties.force_odd_labels = force;
}
double text_symbolizer::get_max_char_angle_delta() const
{
return max_char_angle_delta_;
return placement_options_->properties.max_char_angle_delta;
}
void text_symbolizer::set_max_char_angle_delta(double angle)
{
max_char_angle_delta_ = angle;
placement_options_->properties.max_char_angle_delta = angle;
}
void text_symbolizer::set_text_size(float size)
{
placement_options_->set_default_text_size(size);
placement_options_->properties.processor.defaults.text_size = size;
}
float text_symbolizer::get_text_size() const
{
return placement_options_->get_default_text_size();
return placement_options_->properties.processor.defaults.text_size;
}
void text_symbolizer::set_fill(color const& fill)
{
fill_ = fill;
placement_options_->properties.processor.defaults.fill = fill;
}
color const& text_symbolizer::get_fill() const
{
return fill_;
return placement_options_->properties.processor.defaults.fill;
}
void text_symbolizer::set_halo_fill(color const& fill)
{
halo_fill_ = fill;
placement_options_->properties.processor.defaults.halo_fill = fill;
}
color const& text_symbolizer::get_halo_fill() const
{
return halo_fill_;
return placement_options_->properties.processor.defaults.halo_fill;
}
void text_symbolizer::set_halo_radius(double radius)
{
halo_radius_ = radius;
placement_options_->properties.processor.defaults.halo_radius = radius;
}
double text_symbolizer::get_halo_radius() const
{
return halo_radius_;
return placement_options_->properties.processor.defaults.halo_radius;
}
void text_symbolizer::set_label_placement(label_placement_e label_p)
{
label_p_ = label_p;
placement_options_->properties.label_placement = label_p;
}
label_placement_e text_symbolizer::get_label_placement() const
{
return label_p_;
return placement_options_->properties.label_placement;
}
void text_symbolizer::set_anchor(double x, double y)
void text_symbolizer::set_displacement(double x, double y)
{
anchor_ = boost::make_tuple(x,y);
}
position const& text_symbolizer::get_anchor() const
{
return anchor_;
}
void text_symbolizer::set_displacement(double x, double y)
{
placement_options_->set_default_displacement(boost::make_tuple(x,y));
placement_options_->properties.displacement = boost::make_tuple(x,y);
}
void text_symbolizer::set_displacement(position const& p)
{
placement_options_->set_default_displacement(p);
placement_options_->properties.displacement = p;
}
position const& text_symbolizer::get_displacement() const
{
return placement_options_->get_default_displacement();
return placement_options_->properties.displacement;
}
bool text_symbolizer::get_avoid_edges() const
{
return avoid_edges_;
return placement_options_->properties.avoid_edges;
}
void text_symbolizer::set_avoid_edges(bool avoid)
{
avoid_edges_ = avoid;
placement_options_->properties.avoid_edges = avoid;
}
double text_symbolizer::get_minimum_distance() const
{
return minimum_distance_;
return placement_options_->properties.minimum_distance;
}
void text_symbolizer::set_minimum_distance(double distance)
{
minimum_distance_ = distance;
placement_options_->properties.minimum_distance = distance;
}
double text_symbolizer::get_minimum_padding() const
{
return minimum_padding_;
return placement_options_->properties.minimum_padding;
}
void text_symbolizer::set_minimum_padding(double distance)
{
minimum_padding_ = distance;
placement_options_->properties.minimum_padding = distance;
}
double text_symbolizer::get_minimum_path_length() const
{
return minimum_path_length_;
return placement_options_->properties.minimum_path_length;
}
void text_symbolizer::set_minimum_path_length(double size)
{
minimum_path_length_ = size;
placement_options_->properties.minimum_path_length = size;
}
void text_symbolizer::set_allow_overlap(bool overlap)
{
overlap_ = overlap;
placement_options_->properties.allow_overlap = overlap;
}
bool text_symbolizer::get_allow_overlap() const
{
return overlap_;
return placement_options_->properties.allow_overlap;
}
void text_symbolizer::set_text_opacity(double text_opacity)
{
text_opacity_ = text_opacity;
placement_options_->properties.processor.defaults.text_opacity = text_opacity;
}
double text_symbolizer::get_text_opacity() const
{
return text_opacity_;
return placement_options_->properties.processor.defaults.text_opacity;
}
void text_symbolizer::set_vertical_alignment(vertical_alignment_e valign)
{
placement_options_->set_default_valign(valign);
placement_options_->properties.valign = valign;
}
vertical_alignment_e text_symbolizer::get_vertical_alignment() const
{
return placement_options_->get_default_valign();
return placement_options_->properties.valign;
}
void text_symbolizer::set_horizontal_alignment(horizontal_alignment_e halign)
{
placement_options_->set_default_halign(halign);
placement_options_->properties.halign = halign;
}
horizontal_alignment_e text_symbolizer::get_horizontal_alignment() const
{
return placement_options_->get_default_halign();
return placement_options_->properties.halign;
}
void text_symbolizer::set_justify_alignment(justify_alignment_e jalign)
{
placement_options_->set_default_jalign(jalign);
placement_options_->properties.jalign = jalign;
}
justify_alignment_e text_symbolizer::get_justify_alignment() const
{
return placement_options_->get_default_jalign();
return placement_options_->properties.jalign;
}
text_placements_ptr text_symbolizer::get_placement_options() const
@ -567,47 +455,5 @@ void text_symbolizer::set_placement_options(text_placements_ptr placement_option
placement_options_ = placement_options;
}
void text_symbolizer::set_placement_options(placement_type_e arg, std::string const& placements)
{
text_placements_ptr placement_finder;
switch (arg)
{
case T_SIMPLE:
placement_finder = text_placements_ptr(
new text_placements_simple(placements));
break;
case T_DUMMY:
placement_finder = text_placements_ptr(new text_placements_dummy());
break;
default:
throw config_error(std::string("Unknown placement type"));
break;
}
this->set_placement_options(placement_finder);
}
placement_type_e text_symbolizer::get_placement_type() const
{
text_placements_ptr placement_finder = this->get_placement_options();
if (dynamic_cast<text_placements_simple *>(placement_finder.get()) != NULL)
{
return T_SIMPLE;
}
return T_DUMMY;
}
std::string text_symbolizer::get_placements() const
{
text_placements_ptr placement_finder = this->get_placement_options();
text_placements_simple *placements_simple = dynamic_cast<text_placements_simple *>(placement_finder.get());
if (placements_simple != NULL)
{
return placements_simple->get_positions();
}
return "";
}
}

View File

@ -14,4 +14,4 @@
<PointSymbolizer file="../data/images/also.png"/>
</Rule>
</Style>
</Map>
</Map>

View File

@ -1,8 +0,0 @@
<Map minimum-version="0.7.2">
<Style name="arrows">
<Rule>
<GlyphSymbolizer size="10" char="'&#237;'" value="[value]" angle="[azimuth]+90" face-name="DejaVu Sans Condensed" allow-overlap="1" avoid-edges="0" halo-fill="rgba(0%,0%,0%,.1)" halo-radius="1" angle-mode="azimuth">
</GlyphSymbolizer>
</Rule>
</Style>
</Map>

View File

@ -33,4 +33,4 @@
</Datasource>
</Layer>
</Map>
</Map>

View File

@ -22,4 +22,4 @@
<Parameter name="type">shape</Parameter>
</Datasource>
</Layer>
</Map>
</Map>

View File

@ -1,110 +0,0 @@
#encoding: utf8
#!/usr/bin/env python
from nose.tools import *
from utilities import execution_path, save_data, contains_word
import os, mapnik
def test_renders_with_agg():
sym = mapnik.GlyphSymbolizer("DejaVu Sans Condensed",
mapnik.Expression("'í'"))
sym.allow_overlap = True
sym.angle = mapnik.Expression("[azimuth]+90") #+90 so the top of the glyph points upwards
sym.size = mapnik.Expression("[value]")
sym.color = mapnik.Expression("'#ff0000'")
_map = create_map_and_append_symbolyzer(sym)
if _map:
im = mapnik.Image(_map.width,_map.height)
mapnik.render(_map, im)
save_data('agg_glyph_symbolizer.png', im.tostring('png'))
assert contains_word('\xff\x00\x00\xff', im.tostring())
def test_renders_with_cairo():
if not mapnik.has_pycairo():
return
sym = mapnik.GlyphSymbolizer("DejaVu Sans Condensed",
mapnik.Expression("'í'"))
sym.allow_overlap = True
sym.angle = mapnik.Expression("[azimuth]+90") #+90 so the top of the glyph points upwards
sym.size = mapnik.Expression("[value]")
sym.color = mapnik.Expression("'#ff0000'")
_map = create_map_and_append_symbolyzer(sym)
if _map:
from cStringIO import StringIO
import cairo
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 256, 256)
mapnik.render(_map, surface)
im = mapnik.Image.from_cairo(surface)
save_data('cairo_glyph_symbolizer.png', im.tostring('png'))
assert contains_word('\xff\x00\x00\xff', im.tostring())
def test_load_save_load_map():
map = mapnik.Map(256,256)
in_map = "../data/good_maps/glyph_symbolizer.xml"
try:
mapnik.load_map(map, in_map)
style = map.find_style('arrows')
sym = style.rules[0].symbols[0]
assert isinstance(sym, mapnik.GlyphSymbolizer)
assert sym.angle_mode == mapnik.angle_mode.AZIMUTH
out_map = mapnik.save_map_to_string(map).decode('utf8')
map = mapnik.Map(256,256)
mapnik.load_map_from_string(map, out_map.encode('utf8'))
assert 'GlyphSymbolizer' in out_map
# make sure non-ascii characters are well supported since most interesting
# glyphs for symbology are usually in that range
assert u'í' in out_map, out_map
except RuntimeError, e:
# only test datasources that we have installed
if not 'Could not create datasource' in str(e):
raise RuntimeError(e)
#
# Utilities and setup code
#
def setup():
# All of the paths used are relative, if we run the tests
# from another directory we need to chdir()
os.chdir(execution_path('.'))
def create_map_and_append_symbolyzer(sym):
srs = '+init=epsg:32630'
lyr = mapnik.Layer('arrows')
try:
lyr.datasource = mapnik.Shapefile(
file = '../data/shp/arrows.shp',
)
lyr.srs = srs
_map = mapnik.Map(256,256, srs)
style = mapnik.Style()
rule = mapnik.Rule()
rule.symbols.append(sym)
# put a test symbolizer to see what is the azimuth being read
ts = mapnik.TextSymbolizer(mapnik.Expression('[azimuth]'),
"DejaVu Sans Book",
10,
mapnik.Color("black"))
ts.allow_overlap = True
rule.symbols.append(ts)
style.rules.append(rule)
_map.append_style('foo', style)
lyr.styles.append('foo')
_map.layers.append(lyr)
_map.zoom_to_box(mapnik.Box2d(0,0,8,8))
return _map
except RuntimeError, e:
# only test datasources that we have installed
if not 'Could not create datasource' in str(e):
raise RuntimeError(e)
if __name__ == "__main__":
setup()
[eval(run)() for run in dir() if 'test_' in run]

View File

@ -29,7 +29,7 @@ def test_shieldsymbolizer_init():
eq_(s.allow_overlap, False)
eq_(s.avoid_edges, False)
eq_(s.character_spacing,0)
eq_(str(s.name), str(mapnik.Expression('[Field Name]')))
#eq_(str(s.name), str(mapnik2.Expression('[Field Name]'))) name field is no longer supported
eq_(s.face_name, 'DejaVu Sans Bold')
eq_(s.allow_overlap, False)
eq_(s.fill, mapnik.Color('#000000'))
@ -198,7 +198,7 @@ def test_linesymbolizer_init():
def test_textsymbolizer_init():
ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black'))
eq_(str(ts.name), str(mapnik.Expression('[Field_Name]')))
# eq_(str(ts.name), str(mapnik2.Expression('[Field_Name]'))) name field is no longer supported
eq_(ts.face_name, 'Font Name')
eq_(ts.text_size, 8)
eq_(ts.fill, mapnik.Color('black'))

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map>
<Map background-color="white" srs="+proj=latlong +datum=WGS84">
<Layer name="layer" srs="+proj=latlong +datum=WGS84">
<StyleName>My Style</StyleName>
<Datasource>
<Parameter name="type">shape</Parameter>
<Parameter name="file">points.shp</Parameter>
</Datasource>
</Layer>
<Style name="My Style">
<Rule>
<PointSymbolizer/>
<TextSymbolizer placement-type="simple" placements="N,S" face-name="DejaVu Sans Book" size="16" placement="point" dx="0" dy="5">
[name]+'&#10;'<Format face-name="DejaVu Sans Oblique" size="9">'('+[name]+')'</Format></TextSymbolizer>
</Rule>
</Style>
</Map>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map>
<Map background-color="white" srs="+proj=latlong +datum=WGS84">
<Layer name="layer" srs="+proj=latlong +datum=WGS84">
<StyleName>My Style</StyleName>
<Datasource>
<!--
<Parameter name="type">osm</Parameter>
<Parameter name="file">points.osm</Parameter>-->
<Parameter name="type">shape</Parameter>
<Parameter name="file">points.shp</Parameter>
</Datasource>
</Layer>
<Style name="My Style">
<Rule>
<PointSymbolizer/>
<TextSymbolizer face-name="DejaVu Sans Book" size="16" placement="point" dy="8" fill="blue" placement-type="list">[name]
<Placement size="10" dy="-8" fill="red"/>
<Placement fill="green">'S'+[nr]</Placement>
<Placement fill="orange" dy="8">[nr]</Placement>
</TextSymbolizer>
</Rule>
</Style>
</Map>

Binary file not shown.

View File

@ -0,0 +1,43 @@
<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6' generator='JOSM'>
<node id='-1' lat='0' lon='0'>
<tag k='name' v='Test one' />
<tag k='nr' v='1' />
</node>
<node id='-2' lat='0' lon='0.1'>
<tag k='name' v='Test two' />
<tag k='nr' v='2' />
</node>
<node id='-3' lat='0' lon='0.2'>
<tag k='name' v='Test three' />
<tag k='nr' v='3' />
</node>
<node id='-4' lat='0' lon='0.3'>
<tag k='name' v='Test four' />
<tag k='nr' v='4' />
</node>
<node id='-5' lat='0' lon='0.4'>
<tag k='name' v='Test five' />
<tag k='nr' v='5' />
</node>
<node id='-6' lat='0' lon='0.5'>
<tag k='name' v='Test six' />
<tag k='nr' v='6' />
</node>
<node id='-7' lat='0' lon='0.6'>
<tag k='name' v='Test seven' />
<tag k='nr' v='7' />
</node>
<node id='-8' lat='0' lon='0.7'>
<tag k='name' v='Test eight' />
<tag k='nr' v='8' />
</node>
<node id='-9' lat='0' lon='0.8'>
<tag k='name' v='Test nine' />
<tag k='nr' v='9' />
</node>
<node id='-10' lat='0' lon='0.9'>
<tag k='name' v='Test ten' />
<tag k='nr' v='10' />
</node>
</osm>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Some files were not shown because too many files have changed in this diff Show More