Add support for multiple placements.

NOTE: In some places indention is not updated to keep this patch readable. This will be fixed in the next revision.
This commit is contained in:
Hermann Kraus 2011-02-28 13:17:46 +00:00
parent 8ca85c95c6
commit 4af0696b9d
13 changed files with 507 additions and 86 deletions

View file

@ -168,9 +168,10 @@ void export_text_symbolizer()
.value("INTERIOR_PLACEMENT",INTERIOR_PLACEMENT)
;
enumeration_<vertical_alignment_e>("vertical_alignment")
.value("TOP",TOP)
.value("MIDDLE",MIDDLE)
.value("BOTTOM",BOTTOM)
.value("TOP",V_TOP)
.value("MIDDLE",V_MIDDLE)
.value("BOTTOM",V_BOTTOM)
.value("AUTO",V_AUTO)
;
enumeration_<horizontal_alignment_e>("horizontal_alignment")

View file

@ -32,6 +32,7 @@
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/text_placements.hpp>
#include <queue>
@ -41,9 +42,9 @@ 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_, shield_symbolizer const& sym, text_placement_info_ptr placement_option, 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_, text_symbolizer const& sym, text_placement_info_ptr placement_option, double scale_factor);
~placement();
@ -87,7 +88,7 @@ public:
placement_finder(DetectorT & detector, box2d<double> const& extent);
//Try place a single label at the given point
void find_point_placement(placement & p, double pos_x, double pos_y, double angle=0.0, vertical_alignment_e = MIDDLE, unsigned line_spacing=0, unsigned character_spacing=0, horizontal_alignment_e = H_MIDDLE, justify_alignment_e = J_MIDDLE);
void find_point_placement(placement & p, double pos_x, double pos_y, double angle=0.0, vertical_alignment_e = V_MIDDLE, unsigned line_spacing=0, unsigned character_spacing=0, horizontal_alignment_e = H_MIDDLE, justify_alignment_e = J_MIDDLE);
//Iterate over the given path, placing point labels with respect to label_spacing
template <typename T>

View file

@ -0,0 +1,95 @@
/*****************************************************************************
*
* 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_HPP
#define TEXT_PLACEMENTS_HPP
//stl
#include <vector>
#include <string>
//boost
#include <boost/tuple/tuple.hpp>
#include <boost/shared_ptr.hpp>
namespace mapnik {
typedef boost::tuple<double,double> position;
class text_placement_info
{
public:
/** 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;
/* NOTE: Values are public and non-virtual to avoid any performance problems. */
position displacement;
unsigned text_size;
};
typedef boost::shared_ptr<text_placement_info> text_placement_info_ptr;
class text_placements
{
public:
text_placements() : text_size_(10) {}
virtual text_placement_info_ptr get_placement_info() const =0;
virtual void set_default_text_size(unsigned size) { text_size_ = size; }
unsigned 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_; }
protected:
unsigned text_size_;
position displacement_;
};
typedef boost::shared_ptr<text_placements> text_placements_ptr;
class text_placements_dummy;
class text_placement_info_dummy : public text_placement_info
{
public:
text_placement_info_dummy(text_placements_dummy const* parent) : state(0), position_state(0), parent_(parent) {}
bool next();
bool next_position_only();
private:
unsigned state;
unsigned position_state;
text_placements_dummy const* parent_;
};
class text_placements_dummy: public text_placements
{
public:
text_placement_info_ptr get_placement_info() const;
friend class text_placement_info_dummy;
};
} //namespace
#endif // TEXT_PLACEMENTS_HPP

View file

@ -0,0 +1,73 @@
/*****************************************************************************
*
* 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_SIMPLE_HPP
#define TEXT_PLACEMENTS_SIMPLE_HPP
#include <mapnik/text_placements.hpp>
namespace mapnik {
class text_placements_simple;
typedef enum {
NORTH,
EAST,
SOUTH,
WEST,
NORTHEAST,
SOUTHEAST,
NORTHWEST,
SOUTHWEST,
EXACT_POSITION,
} directions_t;
/** Simple placement strategy.
* See parent class for documentation of each function. */
class text_placement_info_simple : public text_placement_info
{
public:
text_placement_info_simple(text_placements_simple const* parent) : state(0), position_state(0), parent_(parent) {}
bool next();
bool next_position_only();
private:
unsigned state;
unsigned position_state;
text_placements_simple const* parent_;
};
/** Automatically generates placement options from a user selected list of directions and text sizes. */
class text_placements_simple: public text_placements
{
public:
text_placements_simple();
text_placements_simple(std::string positions);
text_placement_info_ptr get_placement_info() const;
void set_positions(std::string positions);
private:
std::string positions_;
std::vector<directions_t> direction_;
std::vector<int> text_sizes_;
friend class text_placement_info_simple;
};
} //namespace
#endif

View file

@ -32,9 +32,9 @@
#include <mapnik/graphics.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/text_placements.hpp>
// boost
#include <boost/tuple/tuple.hpp>
#include <boost/shared_ptr.hpp>
// stl
#include <string>
@ -53,9 +53,10 @@ DEFINE_ENUM( label_placement_e, label_placement_enum );
enum vertical_alignment
{
TOP = 0,
MIDDLE,
BOTTOM,
V_TOP = 0,
V_MIDDLE,
V_BOTTOM,
V_AUTO,
vertical_alignment_MAX
};
@ -66,6 +67,7 @@ enum horizontal_alignment
H_LEFT = 0,
H_MIDDLE,
H_RIGHT,
H_AUTO,
horizontal_alignment_MAX
};
@ -92,13 +94,14 @@ enum text_transform
DEFINE_ENUM( text_transform_e, text_transform );
typedef boost::tuple<double,double> position;
struct MAPNIK_DECL text_symbolizer : public symbolizer_base
{
text_symbolizer(expression_ptr name, std::string const& face_name,
unsigned size, color const& fill);
text_symbolizer(expression_ptr name, unsigned size, color const& fill);
unsigned size, color const& fill,
text_placements_ptr placements = text_placements_ptr(new text_placements_dummy()));
text_symbolizer(expression_ptr name, unsigned size, color const& fill,
text_placements_ptr placements = text_placements_ptr(new text_placements_dummy()));
text_symbolizer(text_symbolizer const& rhs);
text_symbolizer& operator=(text_symbolizer const& rhs);
expression_ptr get_name() const;
@ -165,13 +168,14 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base
horizontal_alignment_e get_horizontal_alignment() const;
void set_justify_alignment(justify_alignment_e valign);
justify_alignment_e get_justify_alignment() const;
text_placements_ptr get_placement_options() const;
void set_placement_options(text_placements_ptr placement_options);
private:
expression_ptr name_;
expression_ptr orientation_;
std::string face_name_;
font_set fontset_;
unsigned size_;
unsigned text_ratio_;
unsigned wrap_width_;
unsigned char wrap_char_;
@ -188,7 +192,6 @@ private:
label_placement_e label_p_;
vertical_alignment_e valign_;
position anchor_;
position displacement_;
bool avoid_edges_;
double minimum_distance_;
double minimum_padding_;
@ -197,6 +200,7 @@ private:
bool wrap_before_;
horizontal_alignment_e halign_;
justify_alignment_e jalign_;
text_placements_ptr placement_options_;
};
}

View file

@ -138,6 +138,7 @@ source = Split(
glyph_symbolizer.cpp
markers_symbolizer.cpp
metawriter.cpp
text_placements.cpp
wkt/wkt_factory.cpp
"""
)
@ -302,4 +303,4 @@ if 'uninstall' not in COMMAND_LINE_TARGETS:
env['create_uninstall_target'](env, inc_target)
env['create_uninstall_target'](env, svg_inc_target)
env['create_uninstall_target'](env, wkt_inc_target)
env['create_uninstall_target'](env, wkt_inc_target)

View file

@ -42,6 +42,11 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
{
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() )
text = UnicodeString( " " ); // TODO: fix->use 'space' as the text to render
@ -129,7 +134,7 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
{
// for every vertex, try and place a shield/text
geom.rewind(0);
placement text_placement(info, sym, scale_factor_, w, h, false);
placement text_placement(info, sym, placement_options, scale_factor_, w, h, false);
text_placement.avoid_edges = sym.get_avoid_edges();
text_placement.allow_overlap = sym.get_allow_overlap();
position const& pos = sym.get_displacement();
@ -204,7 +209,7 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
else if (geom.num_points() > 1 && how_placed == LINE_PLACEMENT)
{
placement text_placement(info, sym, scale_factor_, w, h, true);
placement text_placement(info, sym, placement_options, scale_factor_, w, h, true);
text_placement.avoid_edges = sym.get_avoid_edges();
finder.find_point_placements<path_type>(text_placement,path);

View file

@ -24,14 +24,6 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
/*
#include <mapnik/unicode.hpp>
#include <mapnik/placement_finder.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/text_path.hpp>
*/
namespace mapnik {
@ -42,6 +34,9 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
bool placement_found = false;
text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info();
while (!placement_found && placement_options->next()) {
expression_ptr name_expr = sym.get_name();
if (!name_expr) return;
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*name_expr);
@ -79,7 +74,7 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
if (faces->size() > 0 && strk)
{
text_renderer<T> ren(pixmap_, faces, *strk);
ren.set_pixel_size(sym.get_text_size() * scale_factor_);
ren.set_pixel_size(placement_options->text_size * scale_factor_);
ren.set_fill(fill);
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
@ -95,9 +90,9 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
for (unsigned i=0;i<num_geom;++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 0) // don't bother with empty geometries
{
placement text_placement(info,sym,scale_factor_);
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, placement_options, scale_factor_);
text_placement.avoid_edges = sym.get_avoid_edges();
if (sym.get_label_placement() == POINT_PLACEMENT ||
sym.get_label_placement() == INTERIOR_PLACEMENT)
@ -132,12 +127,14 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
finder.find_line_placements<path_type>(text_placement,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;
box2d<double> dim = ren.prepare_glyphs(&text_placement.placements[ii]);
ren.prepare_glyphs(&text_placement.placements[ii]);
ren.render(x,y);
}
@ -151,6 +148,7 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'");
}
}
}
}
template void agg_renderer<image_32>::process(text_symbolizer const&,

View file

@ -1074,6 +1074,10 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
{
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() )
text = UnicodeString( " " ); // TODO: fix->use 'space' as the text to render
@ -1133,7 +1137,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
placement_finder<label_collision_detector4> finder(detector_);
faces->set_pixel_sizes(sym.get_text_size());
faces->set_pixel_sizes(placement_options->text_size);
faces->get_string_info(info);
int w = (*marker)->width();
@ -1153,7 +1157,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
{
// for every vertex, try and place a shield/text
geom.rewind(0);
placement text_placement(info, sym, 1.0, w, h, false);
placement text_placement(info, sym, placement_options, 1.0, w, h, false);
text_placement.avoid_edges = sym.get_avoid_edges();
text_placement.allow_overlap = sym.get_allow_overlap();
position const& pos = sym.get_displacement();
@ -1216,7 +1220,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
context.add_text(text_placement.placements[ii],
face_manager_,
faces,
sym.get_text_size(),
placement_options->text_size,
sym.get_fill(),
sym.get_halo_radius(),
sym.get_halo_fill()
@ -1234,7 +1238,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
}
else if (geom.num_points() > 1 && how_placed == LINE_PLACEMENT)
{
placement text_placement(info, sym, 1.0, w, h, true);
placement text_placement(info, sym, placement_options, 1.0, w, h, true);
text_placement.avoid_edges = sym.get_avoid_edges();
finder.find_point_placements<path_type>(text_placement, path);
@ -1254,7 +1258,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
context.add_text(text_placement.placements[ii],
face_manager_,
faces,
sym.get_text_size(),
placement_options->text_size,
sym.get_fill(),
sym.get_halo_radius(),
sym.get_halo_fill()
@ -1514,6 +1518,11 @@ void cairo_renderer_base::process(text_symbolizer const& sym,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
bool placement_found = false;
text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info();
while (!placement_found && placement_options->next()) {
expression_ptr name_expr = sym.get_name();
if (!name_expr) return;
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature),*name_expr);
@ -1532,8 +1541,7 @@ void cairo_renderer_base::process(text_symbolizer const& sym,
text = text.toTitle(NULL);
}
if (text.length() > 0)
{
if (text.length() <= 0) continue;
face_set_ptr faces;
if (sym.get_fontset().size() > 0)
@ -1550,7 +1558,7 @@ void cairo_renderer_base::process(text_symbolizer const& sym,
cairo_context context(context_);
string_info info(text);
faces->set_pixel_sizes(sym.get_text_size());
faces->set_pixel_sizes(placement_options->text_size);
faces->get_string_info(info);
placement_finder<label_collision_detector4> finder(detector_);
@ -1559,10 +1567,11 @@ void cairo_renderer_base::process(text_symbolizer const& sym,
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 0) // don't bother with empty geometries
if (geom.num_points() == 0) continue;// don't bother with empty geometries
while (!placement_found && placement_options->next_position_only())
{
path_type path(t_, geom, prj_trans);
placement text_placement(info, sym, 1.0);
placement text_placement(info, sym, placement_options, 1.0);
if (sym.get_label_placement() == POINT_PLACEMENT ||
sym.get_label_placement() == INTERIOR_PLACEMENT)
@ -1594,13 +1603,15 @@ void cairo_renderer_base::process(text_symbolizer const& sym,
{
finder.find_line_placements<path_type>(text_placement, 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,
sym.get_text_size(),
placement_options->text_size,
sym.get_fill(),
sym.get_halo_radius(),
sym.get_halo_fill()

View file

@ -45,6 +45,8 @@
#include <mapnik/metawriter_json.hpp>
#include <mapnik/text_placements_simple.hpp>
// boost
#include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>
@ -1154,11 +1156,25 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
<< "spacing,minimum-distance,minimum-padding,"
<< "avoid-edges,allow-overlap,opacity,max-char-angle-delta,"
<< "horizontal-alignment,justify-alignment,"
<< "meta-writer,meta-output";
<< "meta-writer,meta-output,"
<< "placements,placement-type";
ensure_attrs(sym, "TextSymbolizer", s.str());
try
{
text_placements_ptr placement_finder;
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")));
}
}
if (!placement_finder) {
placement_finder = text_placements_ptr(new text_placements_dummy());
}
std::string name = get_attr<string>(sym, "name");
optional<std::string> face_name =
@ -1171,7 +1187,7 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
color c = get_attr(sym, "fill", color(0,0,0));
text_symbolizer text_symbol = text_symbolizer(parse_expression(name, "utf8"), size, c);
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)
@ -1217,15 +1233,7 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
text_symbol.set_label_placement( placement );
// vertical alignment
vertical_alignment_e default_vertical_alignment = MIDDLE;
if (dy > 0.0 )
{
default_vertical_alignment = BOTTOM;
}
else if( dy < 0.0 )
{
default_vertical_alignment = TOP;
}
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);
@ -1353,7 +1361,7 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
}
// horizontal alignment
horizontal_alignment_e halign = get_attr<horizontal_alignment_e>(sym, "horizontal-alignment", H_MIDDLE);
horizontal_alignment_e halign = get_attr<horizontal_alignment_e>(sym, "horizontal-alignment", H_AUTO);
text_symbol.set_horizontal_alignment(halign);
// justify alignment
@ -1514,7 +1522,7 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
}
// vertical alignment
vertical_alignment_e valign = get_attr<vertical_alignment_e>(sym, "vertical-alignment", MIDDLE);
vertical_alignment_e valign = get_attr<vertical_alignment_e>(sym, "vertical-alignment", V_MIDDLE);
shield_symbol.set_vertical_alignment(valign);
// horizontal alignment

View file

@ -50,11 +50,12 @@ namespace mapnik
{
placement::placement(string_info & info_,
shield_symbolizer const& sym,
text_placement_info_ptr placement_options,
double scale_factor,
unsigned w, unsigned h,
bool has_dimensions_)
: info(info_),
displacement_(sym.get_displacement()),
displacement_(placement_options->displacement),
scale_factor_(scale_factor),
label_placement(sym.get_label_placement()),
wrap_width(sym.get_wrap_width()),
@ -71,13 +72,15 @@ placement::placement(string_info & info_,
has_dimensions(has_dimensions_),
allow_overlap(false),
dimensions(std::make_pair(w,h)),
text_size(sym.get_text_size()) {}
text_size(placement_options->text_size)
{}
placement::placement(string_info & info_,
text_symbolizer const& sym,
text_symbolizer const& sym,
text_placement_info_ptr placement_options,
double scale_factor)
: info(info_),
displacement_(sym.get_displacement()),
displacement_(placement_options->displacement),
scale_factor_(scale_factor),
label_placement(sym.get_label_placement()),
wrap_width(sym.get_wrap_width()),
@ -94,7 +97,7 @@ placement::placement(string_info & info_,
has_dimensions(false),
allow_overlap(sym.get_allow_overlap()),
dimensions(),
text_size(sym.get_text_size())
text_size(placement_options->text_size)
{}
@ -322,28 +325,48 @@ void placement_finder<DetectorT>::find_point_placement(placement & p,
// if needed, adjust for desired vertical alignment
current_placement->starting_y = label_y; // no adjustment, default is MIDDLE
if (valign == TOP)
vertical_alignment_e real_valign = valign;
if (real_valign == V_AUTO) {
if (p.displacement_.get<1>() > 0.0)
real_valign = V_BOTTOM;
else if (p.displacement_.get<1>() < 0.0)
real_valign = V_TOP;
else
real_valign = V_MIDDLE;
}
horizontal_alignment_e real_halign = halign;
if (real_halign == H_AUTO) {
if (p.displacement_.get<0>() > 0.0)
real_halign = H_RIGHT;
else if (p.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 (valign == BOTTOM)
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 (valign == TOP )
if (real_valign == V_TOP )
current_placement->starting_y -= (p.text_size - max_character_height); // move up by the error
else if (valign == MIDDLE)
else if (real_valign == V_MIDDLE)
current_placement->starting_y -= ((p.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 (halign == H_LEFT)
if (real_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)
else if (real_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)

183
src/text_placements.cpp Normal file
View file

@ -0,0 +1,183 @@
/*****************************************************************************
*
* 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
*
*****************************************************************************/
#include <mapnik/text_placements.hpp>
#include <mapnik/text_placements_simple.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>
namespace mapnik {
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
using boost::spirit::ascii::space;
using phoenix::push_back;
using phoenix::ref;
using qi::_1;
bool text_placement_info_dummy::next()
{
displacement = parent_->displacement_;
text_size = parent_->text_size_;
if (state) return false;
state++;
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));
}
/************************************************************************/
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];
}
std::cerr << "Trying text_size=" << text_size << "\n";
state++;
return true;
}
bool text_placement_info_simple::next_position_only()
{
if (position_state >= parent_->direction_.size()) return false;
directions_t dir = parent_->direction_[position_state];
switch (dir) {
case EXACT_POSITION:
displacement = parent_->displacement_;
break;
case NORTH:
displacement = boost::make_tuple(0, -abs(parent_->displacement_.get<1>()));
break;
case EAST:
displacement = boost::make_tuple(abs(parent_->displacement_.get<0>()), 0);
break;
case SOUTH:
displacement = boost::make_tuple(0, abs(parent_->displacement_.get<1>()));
break;
case WEST:
displacement = boost::make_tuple(-abs(parent_->displacement_.get<0>()), 0);
break;
case NORTHEAST:
displacement = boost::make_tuple(
abs(parent_->displacement_.get<0>()),
-abs(parent_->displacement_.get<1>()));
case SOUTHEAST:
displacement = boost::make_tuple(
abs(parent_->displacement_.get<0>()),
abs(parent_->displacement_.get<1>()));
case NORTHWEST:
displacement = boost::make_tuple(
-abs(parent_->displacement_.get<0>()),
-abs(parent_->displacement_.get<1>()));
case SOUTHWEST:
displacement = boost::make_tuple(
-abs(parent_->displacement_.get<0>()),
abs(parent_->displacement_.get<1>()));
break;
default:
std::cerr << "WARNING: Unknown placement\n";
}
position_state++;
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]
* [POS] is any combination of
* N, E, S, W, NE, SE, NW, SW, X (exact position) (seprated by commas)
* [SIZE] is a list of font sizes, seprated by commas. First fontsize
* is always the one given in the TextSymbolizer parameters.
* First all directions are tried, then font size is reduced by 1pt
* and all directions are tried again. The process ends when a placement is
* found or the last fontsize is tried without success.
* Example: N,S,15,10,8 (tries placement above, then below and if
* that fails it tries the additional font sizes 15, 10 and 8.
*/
void text_placements_simple::set_positions(std::string positions)
{
positions_ = positions;
struct direction_name_ : qi::symbols<char, directions_t>
{
direction_name_()
{
add
("N" , NORTH)
("E" , EAST)
("S" , SOUTH)
("W" , WEST)
("NE", NORTHEAST)
("SE", SOUTHEAST)
("NW", NORTHWEST)
("SW", SOUTHWEST)
("X" , EXACT_POSITION)
;
}
} direction_name;
std::string::iterator first = positions.begin(), last = positions.end();
qi::phrase_parse(first, last,
(direction_name[push_back(ref(direction_), _1)] % ',') >> *(',' >> qi::int_[push_back(ref(text_sizes_), _1)]),
space
);
if (first != last) {
std::cerr << "WARNING: Could not parse text_placement_simple placement string ('" << positions << "').\n";
}
if (direction_.size() == 0) {
std::cerr << "WARNING: text_placements_simple with no valid placments! ('"<< positions<<"')\n";
}
}
text_placements_simple::text_placements_simple()
{
set_positions("X");
}
text_placements_simple::text_placements_simple(std::string positions)
{
set_positions(positions);
}
} //namespace

View file

@ -45,6 +45,7 @@ static const char * vertical_alignment_strings[] = {
"top",
"middle",
"bottom",
"auto",
""
};
@ -55,6 +56,7 @@ static const char * horizontal_alignment_strings[] = {
"left",
"middle",
"right",
"auto",
""
};
@ -84,12 +86,13 @@ IMPLEMENT_ENUM( text_transform_e, text_transform_strings );
text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_name, unsigned size, color const& fill)
text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_name,
unsigned size, color const& fill,
text_placements_ptr placements)
: symbolizer_base(),
name_(name),
face_name_(face_name),
//fontset_(default_fontset),
size_(size),
text_ratio_(0),
wrap_width_(0),
wrap_char_(' '),
@ -104,9 +107,8 @@ text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_na
halo_fill_(color(255,255,255)),
halo_radius_(0),
label_p_(POINT_PLACEMENT),
valign_(MIDDLE),
valign_(V_MIDDLE),
anchor_(0.0,0.5),
displacement_(0.0,0.0),
avoid_edges_(false),
minimum_distance_(0.0),
minimum_padding_(0.0),
@ -114,14 +116,18 @@ text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_na
text_opacity_(1.0),
wrap_before_(false),
halign_(H_MIDDLE),
jalign_(J_MIDDLE) {}
jalign_(J_MIDDLE),
placement_options_(placements)
{
set_text_size(size);
}
text_symbolizer::text_symbolizer(expression_ptr name, unsigned size, color const& fill)
text_symbolizer::text_symbolizer(expression_ptr name, unsigned size, color const& fill,
text_placements_ptr placements)
: symbolizer_base(),
name_(name),
//face_name_(""),
//fontset_(default_fontset),
size_(size),
text_ratio_(0),
wrap_width_(0),
wrap_char_(' '),
@ -136,9 +142,8 @@ text_symbolizer::text_symbolizer(expression_ptr name, unsigned size, color const
halo_fill_(color(255,255,255)),
halo_radius_(0),
label_p_(POINT_PLACEMENT),
valign_(MIDDLE),
valign_(V_MIDDLE),
anchor_(0.0,0.5),
displacement_(0.0,0.0),
avoid_edges_(false),
minimum_distance_(0.0),
minimum_padding_(0.0),
@ -146,7 +151,11 @@ text_symbolizer::text_symbolizer(expression_ptr name, unsigned size, color const
text_opacity_(1.0),
wrap_before_(false),
halign_(H_MIDDLE),
jalign_(J_MIDDLE) {}
jalign_(J_MIDDLE),
placement_options_(placements)
{
set_text_size(size);
}
text_symbolizer::text_symbolizer(text_symbolizer const& rhs)
: symbolizer_base(rhs),
@ -154,7 +163,6 @@ text_symbolizer::text_symbolizer(text_symbolizer const& rhs)
orientation_(rhs.orientation_),
face_name_(rhs.face_name_),
fontset_(rhs.fontset_),
size_(rhs.size_),
text_ratio_(rhs.text_ratio_),
wrap_width_(rhs.wrap_width_),
wrap_char_(rhs.wrap_char_),
@ -171,7 +179,6 @@ text_symbolizer::text_symbolizer(text_symbolizer const& rhs)
label_p_(rhs.label_p_),
valign_(rhs.valign_),
anchor_(rhs.anchor_),
displacement_(rhs.displacement_),
avoid_edges_(rhs.avoid_edges_),
minimum_distance_(rhs.minimum_distance_),
minimum_padding_(rhs.minimum_padding_),
@ -179,7 +186,8 @@ text_symbolizer::text_symbolizer(text_symbolizer const& rhs)
text_opacity_(rhs.text_opacity_),
wrap_before_(rhs.wrap_before_),
halign_(rhs.halign_),
jalign_(rhs.jalign_) {}
jalign_(rhs.jalign_),
placement_options_(rhs.placement_options_) {}
text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other)
{
@ -189,7 +197,6 @@ text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other)
orientation_ = other.orientation_;
face_name_ = other.face_name_;
fontset_ = other.fontset_;
size_ = other.size_;
text_ratio_ = other.text_ratio_;
wrap_width_ = other.wrap_width_;
wrap_char_ = other.wrap_char_;
@ -206,7 +213,6 @@ text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other)
label_p_ = other.label_p_;
valign_ = other.valign_;
anchor_ = other.anchor_;
displacement_ = other.displacement_;
avoid_edges_ = other.avoid_edges_;
minimum_distance_ = other.minimum_distance_;
minimum_padding_ = other.minimum_padding_;
@ -215,6 +221,7 @@ text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other)
wrap_before_ = other.wrap_before_;
halign_ = other.halign_;
jalign_ = other.jalign_;
placement_options_ = other.placement_options_;
std::cout << "TODO: Metawriter (text_symbolizer::operator=)\n";
return *this;
}
@ -381,12 +388,12 @@ void text_symbolizer::set_max_char_angle_delta(double angle)
void text_symbolizer::set_text_size(unsigned size)
{
size_ = size;
placement_options_->set_default_text_size(size);
}
unsigned text_symbolizer::get_text_size() const
{
return size_;
return placement_options_->get_default_text_size();
}
void text_symbolizer::set_fill(color const& fill)
@ -451,12 +458,12 @@ position const& text_symbolizer::get_anchor() const
void text_symbolizer::set_displacement(double x, double y)
{
displacement_ = boost::make_tuple(x,y);
placement_options_->set_default_displacement(boost::make_tuple(x,y));
}
position const& text_symbolizer::get_displacement() const
{
return displacement_;
return placement_options_->get_default_displacement();
}
bool text_symbolizer::get_avoid_edges() const
@ -489,7 +496,6 @@ void text_symbolizer::set_minimum_padding(double distance)
minimum_padding_ = distance;
}
void text_symbolizer::set_allow_overlap(bool overlap)
{
overlap_ = overlap;
@ -529,4 +535,16 @@ justify_alignment_e text_symbolizer::get_justify_alignment() const
{
return jalign_;
}
text_placements_ptr text_symbolizer::get_placement_options() const
{
return placement_options_;
}
void text_symbolizer::set_placement_options(text_placements_ptr placement_options)
{
placement_options_ = placement_options;
}
}