+ applied patch from Jochen Topf :
* text_convert="none|toupper|tolower" Convert all text to upper/lower case before rendering. "none" doesn't do anything with the text and is the default. Works for labels along lines or at points. * line_spacing="<number>" Add this many pixels space between two lines in text labels that have been broken into several lines. Default is 0. Doesn't do anything for labels along lines. * character_spacing="<number>" Add this many pixels space between two characters in a text. Default is 0. Currently only works for text labels on point geometries. This should also be implemented for labels along lines, but I'll leave that for another day. * wrap_character="<character>" Instead of breaking text into lines on spaces, use this character. This is useful, when you want to make sure that labels are broken at the right spot. Note that you'll probably want to make wrap_width small so that your lines are actually broken, otherwise you'll see the wrap_character in the output. Default is ' ' (space). Doesn't do anything for labels along lines.
This commit is contained in:
parent
8676d23081
commit
85ecc33d7f
8 changed files with 169 additions and 12 deletions
|
@ -58,6 +58,7 @@ namespace mapnik
|
||||||
boost::ptr_vector<placement_element> placements;
|
boost::ptr_vector<placement_element> placements;
|
||||||
|
|
||||||
int wrap_width;
|
int wrap_width;
|
||||||
|
unsigned char wrap_char;
|
||||||
int text_ratio;
|
int text_ratio;
|
||||||
|
|
||||||
int label_spacing; // distance between repeated labels on a single geometry
|
int label_spacing; // distance between repeated labels on a single geometry
|
||||||
|
@ -81,7 +82,7 @@ namespace mapnik
|
||||||
placement_finder(DetectorT & detector);
|
placement_finder(DetectorT & detector);
|
||||||
|
|
||||||
//Try place a single label at the given point
|
//Try place a single label at the given point
|
||||||
void find_point_placement(placement & p, double pos_x, double pos_y, vertical_alignment_e = MIDDLE);
|
void find_point_placement(placement & p, double pos_x, double pos_y, vertical_alignment_e = MIDDLE, unsigned line_spacing=0, unsigned character_spacing=0);
|
||||||
|
|
||||||
//Iterate over the given path, placing point labels with respect to label_spacing
|
//Iterate over the given path, placing point labels with respect to label_spacing
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -55,6 +55,16 @@ namespace mapnik
|
||||||
|
|
||||||
DEFINE_ENUM( vertical_alignment_e, vertical_alignment );
|
DEFINE_ENUM( vertical_alignment_e, vertical_alignment );
|
||||||
|
|
||||||
|
enum text_convert
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
TOUPPER,
|
||||||
|
TOLOWER,
|
||||||
|
text_convert_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_ENUM( text_convert_e, text_convert );
|
||||||
|
|
||||||
typedef boost::tuple<double,double> position;
|
typedef boost::tuple<double,double> position;
|
||||||
|
|
||||||
struct MAPNIK_DECL text_symbolizer
|
struct MAPNIK_DECL text_symbolizer
|
||||||
|
@ -70,6 +80,14 @@ namespace mapnik
|
||||||
void set_text_ratio(unsigned ratio);
|
void set_text_ratio(unsigned ratio);
|
||||||
unsigned get_wrap_width() const; // width to wrap text at, or trigger ratio
|
unsigned get_wrap_width() const; // width to wrap text at, or trigger ratio
|
||||||
void set_wrap_width(unsigned ratio);
|
void set_wrap_width(unsigned ratio);
|
||||||
|
unsigned char get_wrap_char() const; // character used to wrap lines
|
||||||
|
void set_wrap_char(unsigned char character);
|
||||||
|
text_convert_e get_text_convert() const; // text conversion on strings before display
|
||||||
|
void set_text_convert(text_convert_e convert);
|
||||||
|
unsigned get_line_spacing() const; // spacing between lines of text
|
||||||
|
void set_line_spacing(unsigned spacing);
|
||||||
|
unsigned get_character_spacing() const; // 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; // spacing between repeated labels on lines
|
||||||
void set_label_spacing(unsigned spacing);
|
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; //distance the label can be moved on the line to fit, if 0 the default is used
|
||||||
|
@ -111,6 +129,10 @@ namespace mapnik
|
||||||
unsigned size_;
|
unsigned size_;
|
||||||
unsigned text_ratio_;
|
unsigned text_ratio_;
|
||||||
unsigned wrap_width_;
|
unsigned wrap_width_;
|
||||||
|
unsigned char wrap_char_;
|
||||||
|
text_convert_e text_convert_;
|
||||||
|
unsigned line_spacing_;
|
||||||
|
unsigned character_spacing_;
|
||||||
unsigned label_spacing_;
|
unsigned label_spacing_;
|
||||||
unsigned label_position_tolerance_;
|
unsigned label_position_tolerance_;
|
||||||
bool force_odd_labels_;
|
bool force_odd_labels_;
|
||||||
|
|
|
@ -764,6 +764,15 @@ namespace mapnik
|
||||||
typedef coord_transform2<CoordTransform,geometry2d> path_type;
|
typedef coord_transform2<CoordTransform,geometry2d> path_type;
|
||||||
|
|
||||||
UnicodeString text = feature[sym.get_name()].to_unicode();
|
UnicodeString text = feature[sym.get_name()].to_unicode();
|
||||||
|
if ( sym.get_text_convert() == TOUPPER)
|
||||||
|
{
|
||||||
|
text = text.toUpper();
|
||||||
|
}
|
||||||
|
else if ( sym.get_text_convert() == TOLOWER)
|
||||||
|
{
|
||||||
|
text = text.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
if ( text.length() > 0 )
|
if ( text.length() > 0 )
|
||||||
{
|
{
|
||||||
color const& fill = sym.get_fill();
|
color const& fill = sym.get_fill();
|
||||||
|
@ -807,7 +816,7 @@ namespace mapnik
|
||||||
geom.label_position(&label_x, &label_y);
|
geom.label_position(&label_x, &label_y);
|
||||||
prj_trans.backward(label_x,label_y, z);
|
prj_trans.backward(label_x,label_y, z);
|
||||||
t_.forward(&label_x,&label_y);
|
t_.forward(&label_x,&label_y);
|
||||||
finder.find_point_placement(text_placement,label_x,label_y,sym.get_vertical_alignment());
|
finder.find_point_placement(text_placement,label_x,label_y,sym.get_vertical_alignment(),sym.get_line_spacing(),sym.get_character_spacing());
|
||||||
finder.update_detector(text_placement);
|
finder.update_detector(text_placement);
|
||||||
}
|
}
|
||||||
else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
|
else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
|
||||||
|
|
|
@ -1006,6 +1006,14 @@ namespace mapnik
|
||||||
typedef coord_transform2<CoordTransform,geometry2d> path_type;
|
typedef coord_transform2<CoordTransform,geometry2d> path_type;
|
||||||
|
|
||||||
UnicodeString text = feature[sym.get_name()].to_unicode();
|
UnicodeString text = feature[sym.get_name()].to_unicode();
|
||||||
|
if ( sym.get_text_convert() == TOUPPER)
|
||||||
|
{
|
||||||
|
text = text.toUpper();
|
||||||
|
}
|
||||||
|
else if ( sym.get_text_convert() == TOLOWER)
|
||||||
|
{
|
||||||
|
text = text.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
if (text.length() > 0)
|
if (text.length() > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -885,6 +885,33 @@ namespace mapnik
|
||||||
text_symbol.set_wrap_width(*wrap_width);
|
text_symbol.set_wrap_width(*wrap_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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_convert_e tconvert =
|
||||||
|
get_attr<text_convert_e>(sym, "text_convert", NONE);
|
||||||
|
text_symbol.set_text_convert(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// spacing between repeated labels on lines
|
||||||
optional<unsigned> spacing = get_opt_attr<unsigned>(sym, "spacing");
|
optional<unsigned> spacing = get_opt_attr<unsigned>(sym, "spacing");
|
||||||
if (spacing)
|
if (spacing)
|
||||||
|
|
|
@ -75,6 +75,7 @@ namespace mapnik
|
||||||
displacement_(sym.get_displacement()),
|
displacement_(sym.get_displacement()),
|
||||||
label_placement(sym.get_label_placement()),
|
label_placement(sym.get_label_placement()),
|
||||||
wrap_width(sym.get_wrap_width()),
|
wrap_width(sym.get_wrap_width()),
|
||||||
|
wrap_char(sym.get_wrap_char()),
|
||||||
text_ratio(sym.get_text_ratio()),
|
text_ratio(sym.get_text_ratio()),
|
||||||
label_spacing(sym.get_label_spacing()),
|
label_spacing(sym.get_label_spacing()),
|
||||||
label_position_tolerance(sym.get_label_position_tolerance()),
|
label_position_tolerance(sym.get_label_position_tolerance()),
|
||||||
|
@ -214,7 +215,9 @@ namespace mapnik
|
||||||
void placement_finder<DetectorT>::find_point_placement(placement & p,
|
void placement_finder<DetectorT>::find_point_placement(placement & p,
|
||||||
double label_x,
|
double label_x,
|
||||||
double label_y,
|
double label_y,
|
||||||
vertical_alignment_e valign)
|
vertical_alignment_e valign,
|
||||||
|
unsigned line_spacing,
|
||||||
|
unsigned character_spacing)
|
||||||
{
|
{
|
||||||
double x, y;
|
double x, y;
|
||||||
std::auto_ptr<placement_element> current_placement(new placement_element);
|
std::auto_ptr<placement_element> current_placement(new placement_element);
|
||||||
|
@ -239,7 +242,7 @@ namespace mapnik
|
||||||
std::vector<double> line_heights;
|
std::vector<double> line_heights;
|
||||||
if (wrap_at < string_width && p.info.num_characters() > 0)
|
if (wrap_at < string_width && p.info.num_characters() > 0)
|
||||||
{
|
{
|
||||||
int last_space = 0;
|
int last_wrap_char = 0;
|
||||||
string_width = 0;
|
string_width = 0;
|
||||||
string_height = 0;
|
string_height = 0;
|
||||||
double line_width = 0;
|
double line_width = 0;
|
||||||
|
@ -251,13 +254,15 @@ namespace mapnik
|
||||||
character_info ci;
|
character_info ci;
|
||||||
ci = p.info.at(ii);
|
ci = p.info.at(ii);
|
||||||
|
|
||||||
unsigned c = ci.character;
|
unsigned cwidth = ci.width + character_spacing;
|
||||||
word_width += ci.width;
|
|
||||||
word_height = word_height > ci.height ? word_height : ci.height;
|
|
||||||
|
|
||||||
if (c == ' ')
|
unsigned c = ci.character;
|
||||||
|
word_width += cwidth;
|
||||||
|
word_height = word_height > (ci.height + line_spacing) ? word_height : (ci.height + line_spacing);
|
||||||
|
|
||||||
|
if (c == p.wrap_char)
|
||||||
{
|
{
|
||||||
last_space = ii;
|
last_wrap_char = ii;
|
||||||
line_width += word_width;
|
line_width += word_width;
|
||||||
line_height = line_height > word_height ? line_height : word_height;
|
line_height = line_height > word_height ? line_height : word_height;
|
||||||
word_width = 0;
|
word_width = 0;
|
||||||
|
@ -266,13 +271,13 @@ namespace mapnik
|
||||||
if (line_width > 0 && line_width > wrap_at)
|
if (line_width > 0 && line_width > wrap_at)
|
||||||
{
|
{
|
||||||
// Remove width of breaking space character since it is not rendered
|
// Remove width of breaking space character since it is not rendered
|
||||||
line_width -= ci.width;
|
line_width -= cwidth;
|
||||||
string_width = string_width > line_width ? string_width : line_width;
|
string_width = string_width > line_width ? string_width : line_width;
|
||||||
string_height += line_height;
|
string_height += line_height;
|
||||||
line_breaks.push_back(last_space);
|
line_breaks.push_back(last_wrap_char);
|
||||||
line_widths.push_back(line_width);
|
line_widths.push_back(line_width);
|
||||||
line_heights.push_back(line_height);
|
line_heights.push_back(line_height);
|
||||||
ii = last_space;
|
ii = last_wrap_char;
|
||||||
line_width = 0;
|
line_width = 0;
|
||||||
line_height = 0;
|
line_height = 0;
|
||||||
word_width = 0;
|
word_width = 0;
|
||||||
|
@ -326,6 +331,8 @@ namespace mapnik
|
||||||
character_info ci;
|
character_info ci;
|
||||||
ci = p.info.at(i);
|
ci = p.info.at(i);
|
||||||
|
|
||||||
|
unsigned cwidth = ci.width + character_spacing;
|
||||||
|
|
||||||
unsigned c = ci.character;
|
unsigned c = ci.character;
|
||||||
if (i == index_to_wrap_at)
|
if (i == index_to_wrap_at)
|
||||||
{
|
{
|
||||||
|
@ -367,7 +374,7 @@ p.minimum_distance)))
|
||||||
|
|
||||||
p.envelopes.push(e);
|
p.envelopes.push(e);
|
||||||
}
|
}
|
||||||
x += ci.width;
|
x += cwidth;
|
||||||
}
|
}
|
||||||
p.placements.push_back(current_placement.release());
|
p.placements.push_back(current_placement.release());
|
||||||
//update_detector(p);
|
//update_detector(p);
|
||||||
|
|
|
@ -275,6 +275,22 @@ namespace mapnik
|
||||||
{
|
{
|
||||||
set_attr( node, "wrap_width", sym.get_wrap_width() );
|
set_attr( node, "wrap_width", sym.get_wrap_width() );
|
||||||
}
|
}
|
||||||
|
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_convert() != dfl.get_text_convert() || explicit_defaults_ )
|
||||||
|
{
|
||||||
|
set_attr( node, "text_convert", sym.get_text_convert() );
|
||||||
|
}
|
||||||
|
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_spacing() != dfl.get_label_spacing() || explicit_defaults_ )
|
if (sym.get_label_spacing() != dfl.get_label_spacing() || explicit_defaults_ )
|
||||||
{
|
{
|
||||||
set_attr( node, "spacing", sym.get_label_spacing() );
|
set_attr( node, "spacing", sym.get_label_spacing() );
|
||||||
|
|
|
@ -48,6 +48,17 @@ static const char * vertical_alignment_strings[] = {
|
||||||
|
|
||||||
IMPLEMENT_ENUM( mapnik::vertical_alignment_e, vertical_alignment_strings );
|
IMPLEMENT_ENUM( mapnik::vertical_alignment_e, vertical_alignment_strings );
|
||||||
|
|
||||||
|
static const char * text_convert_strings[] = {
|
||||||
|
"none",
|
||||||
|
"toupper",
|
||||||
|
"tolower",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
IMPLEMENT_ENUM( mapnik::text_convert_e, text_convert_strings );
|
||||||
|
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
text_symbolizer::text_symbolizer(std::string const& name, std::string const& face_name, unsigned size, color const& fill)
|
text_symbolizer::text_symbolizer(std::string const& name, std::string const& face_name, unsigned size, color const& fill)
|
||||||
|
@ -57,6 +68,10 @@ namespace mapnik
|
||||||
size_(size),
|
size_(size),
|
||||||
text_ratio_(0),
|
text_ratio_(0),
|
||||||
wrap_width_(0),
|
wrap_width_(0),
|
||||||
|
wrap_char_(' '),
|
||||||
|
text_convert_(NONE),
|
||||||
|
line_spacing_(0),
|
||||||
|
character_spacing_(0),
|
||||||
label_spacing_(0),
|
label_spacing_(0),
|
||||||
label_position_tolerance_(0),
|
label_position_tolerance_(0),
|
||||||
force_odd_labels_(false),
|
force_odd_labels_(false),
|
||||||
|
@ -78,6 +93,10 @@ namespace mapnik
|
||||||
size_(size),
|
size_(size),
|
||||||
text_ratio_(0),
|
text_ratio_(0),
|
||||||
wrap_width_(0),
|
wrap_width_(0),
|
||||||
|
wrap_char_(' '),
|
||||||
|
text_convert_(NONE),
|
||||||
|
line_spacing_(0),
|
||||||
|
character_spacing_(0),
|
||||||
label_spacing_(0),
|
label_spacing_(0),
|
||||||
label_position_tolerance_(0),
|
label_position_tolerance_(0),
|
||||||
force_odd_labels_(false),
|
force_odd_labels_(false),
|
||||||
|
@ -99,6 +118,10 @@ namespace mapnik
|
||||||
size_(rhs.size_),
|
size_(rhs.size_),
|
||||||
text_ratio_(rhs.text_ratio_),
|
text_ratio_(rhs.text_ratio_),
|
||||||
wrap_width_(rhs.wrap_width_),
|
wrap_width_(rhs.wrap_width_),
|
||||||
|
wrap_char_(rhs.wrap_char_),
|
||||||
|
text_convert_(rhs.text_convert_),
|
||||||
|
line_spacing_(rhs.line_spacing_),
|
||||||
|
character_spacing_(rhs.character_spacing_),
|
||||||
label_spacing_(rhs.label_spacing_),
|
label_spacing_(rhs.label_spacing_),
|
||||||
label_position_tolerance_(rhs.label_position_tolerance_),
|
label_position_tolerance_(rhs.label_position_tolerance_),
|
||||||
force_odd_labels_(rhs.force_odd_labels_),
|
force_odd_labels_(rhs.force_odd_labels_),
|
||||||
|
@ -124,6 +147,10 @@ namespace mapnik
|
||||||
size_ = other.size_;
|
size_ = other.size_;
|
||||||
text_ratio_ = other.text_ratio_;
|
text_ratio_ = other.text_ratio_;
|
||||||
wrap_width_ = other.wrap_width_;
|
wrap_width_ = other.wrap_width_;
|
||||||
|
wrap_char_ = other.wrap_char_;
|
||||||
|
text_convert_ = other.text_convert_;
|
||||||
|
line_spacing_ = other.line_spacing_;
|
||||||
|
character_spacing_ = other.character_spacing_;
|
||||||
label_spacing_ = other.label_spacing_;
|
label_spacing_ = other.label_spacing_;
|
||||||
label_position_tolerance_ = other.label_position_tolerance_;
|
label_position_tolerance_ = other.label_position_tolerance_;
|
||||||
force_odd_labels_ = other.force_odd_labels_;
|
force_odd_labels_ = other.force_odd_labels_;
|
||||||
|
@ -191,6 +218,46 @@ namespace mapnik
|
||||||
wrap_width_ = width;
|
wrap_width_ = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char text_symbolizer::get_wrap_char() const
|
||||||
|
{
|
||||||
|
return wrap_char_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_symbolizer::set_wrap_char(unsigned char character)
|
||||||
|
{
|
||||||
|
wrap_char_ = character;
|
||||||
|
}
|
||||||
|
|
||||||
|
text_convert_e text_symbolizer::get_text_convert() const
|
||||||
|
{
|
||||||
|
return text_convert_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_symbolizer::set_text_convert(text_convert_e convert)
|
||||||
|
{
|
||||||
|
text_convert_ = convert;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned text_symbolizer::get_line_spacing() const
|
||||||
|
{
|
||||||
|
return line_spacing_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_symbolizer::set_line_spacing(unsigned spacing)
|
||||||
|
{
|
||||||
|
line_spacing_ = spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned text_symbolizer::get_character_spacing() const
|
||||||
|
{
|
||||||
|
return character_spacing_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_symbolizer::set_character_spacing(unsigned spacing)
|
||||||
|
{
|
||||||
|
character_spacing_ = spacing;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned text_symbolizer::get_label_spacing() const
|
unsigned text_symbolizer::get_label_spacing() const
|
||||||
{
|
{
|
||||||
return label_spacing_;
|
return label_spacing_;
|
||||||
|
|
Loading…
Reference in a new issue