Patch from David Eastcott :

1.  Modified Text Symbolizer

  a) corrected line fragment centering (for 2nd and subsequent lines, when line breaks occur).

  b) adjusted vertical alignment calculation so that:
      i)      middle -> has the center of the text line(s) at the point origin
      ii)     bottom -> has the text line(s) below the point origin
      iii)    top -> has the text line(s) above the point origin

  c) added new text_symbolizer attribute: 'wrap_before', value range: true/false, default == false

     allows line breaks at first wrap_char before wrap_width as an alternative to the original
     which was to create the line break at the first wrap_char after wrap_width

  d) added new text_symbolizer attribute: 'horizontal_alignment', value range: left/middle/right, default == middle
      i)      left -> has all text line(s) to left of the point origin
      ii)     middle -> has all text line(s) centered on the the point origin
      iii)    right -> has all text line(s) to the right of the point origin

      NOTE:   dx, dy position adjustments are applied after alignments and before Justify.

  e) added new text_symbolizer attribute: 'justify_alignment', value range: left/middle/right, default == middle
      i)      left -> after alignments, has all text line(s) are left justified (left to right reading)
      ii)     middle -> after alignments, has all text line(s) center justified
      iii)    right -> after alignments, has all text line(s) right justified (right to left reading)

  f) added new text_symbolizer attribute:  'opacity', value range: 0.0 thru 1.0; 1.0 == fully opaque

  g) modified positioning to compensate for both line_spacing and character_spacing, to ensure proper
     centering of the text envelope.  Also ensure that centering occurs correctly even if no wrapping
     occurs.  Line spacing is uniform and consistent and compensates for errors between text_size and
     the actual size (ci.height is inconsistent, depending on case and character); fixes issue with
     multi-line text where some lines have a slight gap and others are compressed together.


2.  Modified shield_symbolizer

  a) added the attributes:
      i)      allow_overlap
      ii)     vertical_alignment
      iii)    horizontal_alignment
      iv)     justify_alignment
      v)      wrap_width
      vi)     wrap_character
      vii)    wrap_before
      viii)   text_convert
      ix)     line_spacing
      x)      character_spacing
      xi)     opacity

  b)  added new shield_symbolizer attribute: 'unlock_image', value range: true/false, default == false
      i)  false == image and text placement behaviour same as before
      ii) true == image placement independant of text, image is always centered at geometry point, text placed per attributes,
                  dx/dy only affect text.

      Allows user to create point markers with text, but both the text and image rendering collision detection are done
      as a pair (they come and go together - solves problem if using point_symbolizer and text_symbolizers where one or the
      other are omitted due to overlaps, but not both)

  c)  extended choices for the attribute 'placement' to include vertex; effect is limited to the shield_symbolizer

      Allows an attempted placement at every vertex available, gives additional shield placement volume when using line geometry

  d)  ensured that the text placement was not updating the detector unless a shield image was actually placed.

  e)  added new shield_symbolizer attribute: 'no_text', value range: true/false, default = false

      When set true, the text for the feature is ignored ('space' subsituted) so that pure graphic symbols can be used
      and no text is rendered over top of them.
This commit is contained in:
Artem Pavlenko 2009-10-19 13:52:53 +00:00
parent df7bad25f9
commit a3a5859466
11 changed files with 1106 additions and 726 deletions

View file

@ -397,7 +397,8 @@ namespace mapnik
faces_(faces),
fill_(0,0,0),
halo_fill_(255,255,255),
halo_radius_(0) {}
halo_radius_(0),
opacity_(1.0) {}
void set_pixel_size(unsigned size)
{
@ -419,6 +420,11 @@ namespace mapnik
halo_radius_=radius;
}
void set_opacity( double opacity=1.0)
{
opacity_=opacity;
}
Envelope<double> prepare_glyphs(text_path *path)
{
//clear glyphs
@ -561,7 +567,7 @@ namespace mapnik
{
for (int n=-halo_radius_; n <=halo_radius_; ++n)
for (int m=-halo_radius_;m <= halo_radius_; ++m)
pixmap_.blendPixel(i+m,j+n,rgba,gray);
pixmap_.blendPixel2(i+m,j+n,rgba,gray,opacity_);
}
}
}
@ -580,7 +586,7 @@ namespace mapnik
int gray=bitmap->buffer[q*bitmap->width+p];
if (gray)
{
pixmap_.blendPixel(i,j,rgba,gray);
pixmap_.blendPixel2(i,j,rgba,gray,opacity_);
}
}
}
@ -594,6 +600,7 @@ namespace mapnik
unsigned text_ratio_;
unsigned wrap_width_;
glyphs_t glyphs_;
double opacity_;
};
}

View file

@ -187,11 +187,16 @@ namespace mapnik
}
}
inline void blendPixel(int x,int y,unsigned int rgba1,int t)
{
blendPixel2(x,y,rgba1,t,1.0); // do not change opacity
}
inline void blendPixel2(int x,int y,unsigned int rgba1,int t,double opacity)
{
if (checkBounds(x,y))
{
unsigned rgba0 = data_(x,y);
unsigned a1 = (rgba1 >> 24) & 0xff; //t;
unsigned a1 = (int)(((rgba1 >> 24) & 0xff) * opacity) & 0xff; // adjust for desired opacity
a1 = (t*a1) / 255;
if (a1 == 0) return;
unsigned r1 = rgba1 & 0xff;

View file

@ -58,6 +58,7 @@ namespace mapnik
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;
@ -71,6 +72,7 @@ namespace mapnik
bool has_dimensions;
bool allow_overlap;
std::pair<double, double> dimensions;
int text_size;
};
@ -82,7 +84,7 @@ namespace mapnik
placement_finder(DetectorT & detector);
//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, unsigned line_spacing=0, unsigned character_spacing=0);
void find_point_placement(placement & p, double pos_x, double pos_y, vertical_alignment_e = 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

@ -49,6 +49,16 @@ namespace mapnik
std::string const& file,
std::string const& type,
unsigned width,unsigned height);
bool get_unlock_image() const; // image is not locked to the text placement
void set_unlock_image(bool unlock_image);
bool get_no_text() const; // do no render text
void set_no_text(bool unlock_image);
private:
bool unlock_image_;
bool no_text_;
};
}

View file

@ -40,6 +40,7 @@ namespace mapnik
enum label_placement_enum {
POINT_PLACEMENT,
LINE_PLACEMENT,
VERTEX_PLACEMENT,
label_placement_enum_MAX
};
@ -55,6 +56,26 @@ namespace mapnik
DEFINE_ENUM( vertical_alignment_e, vertical_alignment );
enum horizontal_alignment
{
H_LEFT = 0,
H_MIDDLE,
H_RIGHT,
horizontal_alignment_MAX
};
DEFINE_ENUM( horizontal_alignment_e, horizontal_alignment );
enum justify_alignment
{
J_LEFT = 0,
J_MIDDLE,
J_RIGHT,
justify_alignment_MAX
};
DEFINE_ENUM( justify_alignment_e, justify_alignment );
enum text_convert
{
NONE = 0,
@ -124,6 +145,15 @@ namespace mapnik
double get_minimum_distance() const;
void set_allow_overlap(bool overlap);
bool get_allow_overlap() const;
void set_opacity(double opacity);
double get_opacity() const;
bool get_wrap_before() const; // wrap text at wrap_char immediately before current work
void set_wrap_before(bool wrap_before);
void set_horizontal_alignment(horizontal_alignment_e valign);
horizontal_alignment_e get_horizontal_alignment() const;
void set_justify_alignment(justify_alignment_e valign);
justify_alignment_e get_justify_alignment() const;
private:
std::string name_;
std::string face_name_;
@ -149,6 +179,10 @@ namespace mapnik
bool avoid_edges_;
double minimum_distance_;
bool overlap_;
double opacity_;
bool wrap_before_;
horizontal_alignment_e halign_;
justify_alignment_e jalign_;
};
}

View file

@ -458,7 +458,21 @@ namespace mapnik
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry2d> path_type;
UnicodeString text = feature[sym.get_name()].to_unicode();
UnicodeString text;
if( sym.get_no_text() )
text = UnicodeString( " " ); // use 'space' as the text to render
else
text = feature[sym.get_name()].to_unicode(); // use text from feature to render
if ( sym.get_text_convert() == TOUPPER)
{
text = text.toUpper();
}
else if ( sym.get_text_convert() == TOLOWER)
{
text = text.toLower();
}
boost::shared_ptr<ImageData32> const& data = sym.get_image();
if (text.length() > 0 && data)
{
@ -500,40 +514,67 @@ namespace mapnik
{
path_type path(t_,geom,prj_trans);
if (sym.get_label_placement() == POINT_PLACEMENT)
label_placement_enum how_placed = sym.get_label_placement();
if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT)
{
// for every vertex, try and place a shield/text
geom.rewind(0);
for( unsigned jj = 0; jj < geom.num_points(); jj++ )
{
double label_x;
double label_y;
double z=0.0;
placement text_placement(info, sym, false);
text_placement.avoid_edges = sym.get_avoid_edges();
geom.label_position(&label_x, &label_y);
text_placement.allow_overlap = sym.get_allow_overlap();
if( how_placed == VERTEX_PLACEMENT )
geom.vertex(&label_x,&label_y); // by vertex
else
geom.label_position(&label_x, &label_y); // by middle of line or by point
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
finder.find_point_placement(text_placement,label_x,label_y);
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
finder.find_point_placement( text_placement,label_x,label_y,sym.get_vertical_alignment(),sym.get_line_spacing(),
sym.get_character_spacing(),sym.get_horizontal_alignment(),sym.get_justify_alignment() );
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
if( text_placement.placements.size() > 0)
{
double x = text_placement.placements[ii].starting_x;
double y = text_placement.placements[ii].starting_y;
double x = text_placement.placements[0].starting_x;
double y = text_placement.placements[0].starting_y;
int px;
int py;
Envelope<double> label_ext;
if( !sym.get_unlock_image() )
{ // center image at text center position
// remove displacement from image label
position pos = sym.get_displacement();
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
int px=int(lx - (0.5 * w)) ;
int py=int(ly - (0.5 * h)) ;
Envelope<double> label_ext (floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h));
px=int(floor(lx - (0.5 * w))) ;
py=int(floor(ly - (0.5 * h))) ;
label_ext.init( floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h) );
}
else
{ // center image at reference location
px=int(floor(label_x - 0.5 * w));
py=int(floor(label_y - 0.5 * h));
label_ext.init( floor(label_x - 0.5 * w), floor(label_y - 0.5 * h), ceil (label_x + 0.5 * w), ceil (label_y + 0.5 * h));
}
if ( detector_.has_placement(label_ext))
if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) )
{
pixmap_.set_rectangle_alpha(px,py,*data);
Envelope<double> dim = ren.prepare_glyphs(&text_placement.placements[ii]);
//pixmap_.set_rectangle_alpha(px,py,*data);
pixmap_.set_rectangle_alpha2(*data,px,py,sym.get_opacity());
Envelope<double> dim = ren.prepare_glyphs(&text_placement.placements[0]);
ren.render(x,y);
detector_.insert(label_ext);
}
}
finder.update_detector(text_placement);
}
}
}
}
else if (geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
{
@ -795,6 +836,7 @@ namespace mapnik
ren.set_fill(fill);
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius());
ren.set_opacity(sym.get_opacity());
placement_finder<label_collision_detector4> finder(detector_);
@ -816,7 +858,8 @@ namespace mapnik
geom.label_position(&label_x, &label_y);
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
finder.find_point_placement(text_placement,label_x,label_y,sym.get_vertical_alignment(),sym.get_line_spacing(),sym.get_character_spacing());
finder.find_point_placement(text_placement,label_x,label_y,sym.get_vertical_alignment(),sym.get_line_spacing(),
sym.get_character_spacing(),sym.get_horizontal_alignment(),sym.get_justify_alignment());
finder.update_detector(text_placement);
}
else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)

View file

@ -859,8 +859,8 @@ namespace mapnik
text_symbol.set_label_placement( placement );
// vertical alignment
vertical_alignment_e valign = get_attr<vertical_alignment_e>(sym, "vertical_alignment", BOTTOM);
text_symbol.set_vertical_alignment(valign);
// halo fill and radius
optional<color> halo_fill = get_opt_attr<color>(sym, "halo_fill");
if (halo_fill)
@ -877,6 +877,10 @@ namespace mapnik
// 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");
@ -885,6 +889,13 @@ namespace mapnik
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");
@ -943,6 +954,14 @@ namespace mapnik
text_symbol.set_allow_overlap( * allow_overlap );
}
// opacity
optional<double> opacity =
get_opt_attr<double>(sym, "opacity");
if (opacity)
{
text_symbol.set_opacity( * opacity );
}
// max_char_angle_delta
optional<double> max_char_angle_delta =
get_opt_attr<double>(sym, "max_char_angle_delta");
@ -951,6 +970,14 @@ namespace mapnik
text_symbol.set_max_char_angle_delta( * max_char_angle_delta);
}
// horizontal alignment
horizontal_alignment_e halign = get_attr<horizontal_alignment_e>(sym, "horizontal_alignment", H_MIDDLE);
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);
rule.append(text_symbol);
}
catch (const config_error & ex)
@ -1078,6 +1105,91 @@ namespace mapnik
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", BOTTOM);
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_convert_e tconvert =
get_attr<text_convert_e>(sym, "text_convert", NONE);
shield_symbol.set_text_convert(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 );
}
// 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
optional<boolean> no_text =
get_opt_attr<boolean>(sym, "no_text");
if (no_text)
{
shield_symbol.set_no_text( * no_text );
}
rule.append(shield_symbol);
}
catch (ImageReaderException const & ex )

View file

@ -55,6 +55,8 @@ namespace mapnik
displacement_(sym.get_displacement()),
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(sym.get_label_spacing()),
label_position_tolerance(sym.get_label_position_tolerance()),
@ -65,7 +67,8 @@ namespace mapnik
has_dimensions(has_dimensions_),
allow_overlap(false),
dimensions(std::make_pair(sym.get_image()->width(),
sym.get_image()->height()))
sym.get_image()->height())),
text_size(sym.get_text_size())
{
}
@ -75,6 +78,7 @@ namespace mapnik
displacement_(sym.get_displacement()),
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(sym.get_label_spacing()),
@ -85,7 +89,8 @@ namespace mapnik
avoid_edges(sym.get_avoid_edges()),
has_dimensions(false),
allow_overlap(sym.get_allow_overlap()),
dimensions()
dimensions(),
text_size(sym.get_text_size())
{
}
placement::~placement()
@ -217,155 +222,185 @@ namespace mapnik
double label_y,
vertical_alignment_e valign,
unsigned line_spacing,
unsigned character_spacing)
unsigned character_spacing,
horizontal_alignment_e halign,
justify_alignment_e jalign)
{
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;
double string_width = string_dimensions.first + (character_spacing *(p.info.num_characters()-1));
double string_height = string_dimensions.second;
// 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
// check if we need to wrap the string
double wrap_at = string_width + 1;
double wrap_at = string_width + 1.0;
if (p.wrap_width && string_width > p.wrap_width)
{
if (p.text_ratio)
for (int i = 1; ((wrap_at = string_width/i)/(string_height*i)) > p.text_ratio && (string_width/i) > p.wrap_width; ++i);
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
// 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;
std::vector<double> line_heights;
if (wrap_at < string_width && p.info.num_characters() > 0)
{
int last_wrap_char = 0;
string_width = 0;
string_height = 0;
double line_width = 0;
double line_height = 0;
double word_width = 0;
double word_height = 0;
int last_wrap_char_width = 0;
string_width = 0.0;
string_height = 0.0;
double line_width = 0.0;
double word_width = 0.0;
for (unsigned int ii = 0; ii < p.info.num_characters(); ii++)
{
character_info ci;
ci = p.info.at(ii);
unsigned cwidth = ci.width + character_spacing;
double cwidth = ci.width + character_spacing;
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_wrap_char = ii;
last_wrap_char_width = cwidth;
line_width += word_width;
line_height = line_height > word_height ? line_height : word_height;
word_width = 0;
word_height = 0;
word_width = 0.0;
}
if (line_width > 0 && line_width > wrap_at)
// wrap text at first wrap_char after (default) the wrap width or immediately before the current word
if (line_width > 0 && (((line_width - character_spacing) > wrap_at && !p.wrap_before) ||
((line_width + word_width - character_spacing) > wrap_at && p.wrap_before)) )
{
// Remove width of breaking space character since it is not rendered
line_width -= cwidth;
// 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 += line_height;
string_height += max_character_height;
line_breaks.push_back(last_wrap_char);
line_widths.push_back(line_width);
line_heights.push_back(line_height);
ii = last_wrap_char;
line_width = 0;
line_height = 0;
word_width = 0;
word_height = 0;
line_width = 0.0;
word_width = 0.0;
}
}
line_width += word_width;
line_height = line_height > word_height ? line_height : word_height;
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 += line_height;
line_breaks.push_back(p.info.num_characters() + 1);
string_height += max_character_height;
line_breaks.push_back(p.info.num_characters());
line_widths.push_back(line_width);
line_heights.push_back(line_height);
}
if (line_breaks.size() == 0)
{
line_breaks.push_back(p.info.num_characters() + 1);
line_breaks.push_back(p.info.num_characters());
line_widths.push_back(string_width);
line_heights.push_back(string_height);
}
int total_lines = line_breaks.size();
p.info.set_dimensions(string_width, string_height);
p.info.set_dimensions( string_width, (string_height + (line_spacing * (total_lines-1))) );
unsigned int line_number = 0;
unsigned int index_to_wrap_at = line_breaks[line_number];
double line_width = line_widths[line_number];
double line_height = line_heights[line_number];
// if needed, adjust for desired vertical alignment
current_placement->starting_y = label_y; // no adjustment, default is MIDDLE
if (valign == 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)
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 )
current_placement->starting_y -= (p.text_size - max_character_height); // move up by the error
current_placement->starting_x = label_x;
if (valign == BOTTOM)
{
current_placement->starting_y = label_y;
}
else if (valign == MIDDLE)
{
current_placement->starting_y = label_y - 0.5 * (line_heights.size() - 1) * line_height ;
}
else // TOP
{
current_placement->starting_y = label_y - line_heights.size() * line_height;
}
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)
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 += boost::tuples::get<0>(p.displacement_);
current_placement->starting_y += boost::tuples::get<1>(p.displacement_);
x = -line_width/2.0;
y = -line_height/2.0;
// presets for first line
unsigned int line_number = 0;
unsigned int index_to_wrap_at = line_breaks[0];
double line_width = line_widths[0];
// set for upper left corner of text envelope for the first line, bottom left of first character
x = -(line_width / 2.0);
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( jalign == J_LEFT )
x = -(string_width / 2.0);
else if (jalign == J_RIGHT)
x = (string_width / 2.0) - line_width;
// save each character rendering position and build envelope as go thru loop
for (unsigned i = 0; i < p.info.num_characters(); i++)
{
character_info ci;
ci = p.info.at(i);
unsigned cwidth = ci.width + character_spacing;
double cwidth = ci.width + character_spacing;
unsigned c = ci.character;
if (i == index_to_wrap_at)
{
index_to_wrap_at = line_breaks[++line_number];
line_width = line_widths[line_number];
line_height = line_heights[line_number];
y -= line_height;
x = -line_width/2.0;
y -= (max_character_height + line_spacing); // move position down to line start
// reset to begining of line position
x = ((jalign == J_LEFT)? -(string_width / 2.0): ((jalign == J_RIGHT)? ((string_width /2.0) - line_width): -(line_width / 2.0)));
continue;
}
else
{
// place the character relative to the center of the string envelope
current_placement->add_node(c, x, y, 0.0);
// compute the Bounding Box for each character and test for:
// overlap, minimum distance or edge avoidance - exit if condition occurs
Envelope<double> e;
if (p.has_dimensions)
{
e.init(current_placement->starting_x - (p.dimensions.first/2.0),
e.init(current_placement->starting_x - (p.dimensions.first/2.0), // Top Left
current_placement->starting_y - (p.dimensions.second/2.0),
current_placement->starting_x + (p.dimensions.first/2.0),
current_placement->starting_x + (p.dimensions.first/2.0), // Bottom Right
current_placement->starting_y + (p.dimensions.second/2.0));
}
else
{
e.init(current_placement->starting_x + x,
e.init(current_placement->starting_x + x, // Bottom Left
current_placement->starting_y - y,
current_placement->starting_x + x + ci.width,
current_placement->starting_y - y - ci.height);
current_placement->starting_x + x + ci.width, // Top Right
current_placement->starting_y - y - max_character_height);
}
if (!dimensions_.intersects(e) ||
(!p.allow_overlap && !detector_.has_point_placement(e,
p.minimum_distance)))
(!p.allow_overlap && !detector_.has_point_placement(e,p.minimum_distance)))
{
return;
}
@ -374,14 +409,13 @@ p.minimum_distance)))
p.envelopes.push(e);
}
x += cwidth;
x += cwidth; // move position to next character
}
p.placements.push_back(current_placement.release());
//update_detector(p);
}
template <typename DetectorT>
template <typename PathT>
void placement_finder<DetectorT>::find_line_placements(placement & p, PathT & shape_path)

View file

@ -157,6 +157,20 @@ namespace mapnik
add_font_attributes( sym_node, sym);
add_image_attributes( sym_node, sym);
// pseudo-default-construct a shield_symbolizer. It is used
// to avoid printing of attributes with default values without
// repeating the default values here.
// maybe add a real, explicit default-ctor?
shield_symbolizer sym_dfl("<no default>", "<no default>", 0, color(0,0,0), "<no default>", "<no default>", 0, 0 );
if (sym.get_unlock_image() != sym_dfl.get_unlock_image() || explicit_defaults_ )
{
set_attr( sym_node, "unlock_image", sym.get_unlock_image() );
}
if (sym.get_no_text() != sym_dfl.get_no_text() || explicit_defaults_ )
{
set_attr( sym_node, "no_text", sym.get_no_text() );
}
}
void operator () ( const text_symbolizer & sym )
@ -279,6 +293,10 @@ namespace mapnik
{
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()) );
@ -311,6 +329,18 @@ namespace mapnik
{
set_attr( node, "avoid_edges", sym.get_avoid_edges() );
}
if (sym.get_opacity() != dfl.get_opacity() || explicit_defaults_ )
{
set_attr( node, "opacity", sym.get_opacity() );
}
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() );
}
}
ptree & rule_;
bool explicit_defaults_;

View file

@ -43,7 +43,9 @@ namespace mapnik
std::string const& type,
unsigned width,unsigned height)
: text_symbolizer(name, face_name, size, fill),
symbolizer_with_image( file, type, width, height )
symbolizer_with_image( file, type, width, height ),
unlock_image_(false),
no_text_(false)
{
}
@ -55,8 +57,30 @@ namespace mapnik
std::string const& type,
unsigned width,unsigned height)
: text_symbolizer(name, size, fill),
symbolizer_with_image( file, type, width, height )
symbolizer_with_image( file, type, width, height ),
unlock_image_(false),
no_text_(false)
{
}
void shield_symbolizer::set_unlock_image(bool unlock_image)
{
unlock_image_ = unlock_image;
}
bool shield_symbolizer::get_unlock_image() const
{
return unlock_image_;
}
void shield_symbolizer::set_no_text(bool no_text)
{
no_text_ = no_text;
}
bool shield_symbolizer::get_no_text() const
{
return no_text_;
}
}

View file

@ -32,6 +32,7 @@
static const char * label_placement_strings[] = {
"point",
"line",
"vertex",
""
};
@ -48,6 +49,26 @@ static const char * vertical_alignment_strings[] = {
IMPLEMENT_ENUM( mapnik::vertical_alignment_e, vertical_alignment_strings );
static const char * horizontal_alignment_strings[] = {
"left",
"middle",
"right",
""
};
IMPLEMENT_ENUM( mapnik::horizontal_alignment_e, horizontal_alignment_strings );
static const char * justify_alignment_strings[] = {
"left",
"middle",
"right",
""
};
IMPLEMENT_ENUM( mapnik::justify_alignment_e, justify_alignment_strings );
static const char * text_convert_strings[] = {
"none",
"toupper",
@ -85,7 +106,12 @@ namespace mapnik
displacement_(0.0,0.0),
avoid_edges_(false),
minimum_distance_(0.0),
overlap_(false) {}
overlap_(false),
opacity_(1.0),
wrap_before_(false),
halign_(H_MIDDLE),
jalign_(J_MIDDLE) {}
text_symbolizer::text_symbolizer(std::string const& name, unsigned size, color const& fill)
: name_(name),
//face_name_(""),
@ -110,7 +136,12 @@ namespace mapnik
displacement_(0.0,0.0),
avoid_edges_(false),
minimum_distance_(0.0),
overlap_(false) {}
overlap_(false),
opacity_(1.0),
wrap_before_(false),
halign_(H_MIDDLE),
jalign_(J_MIDDLE) {}
text_symbolizer::text_symbolizer(text_symbolizer const& rhs)
: name_(rhs.name_),
face_name_(rhs.face_name_),
@ -135,7 +166,11 @@ namespace mapnik
displacement_(rhs.displacement_),
avoid_edges_(rhs.avoid_edges_),
minimum_distance_(rhs.minimum_distance_),
overlap_(rhs.overlap_) {}
overlap_(rhs.overlap_),
opacity_(rhs.opacity_),
wrap_before_(rhs.wrap_before_),
halign_(rhs.halign_),
jalign_(rhs.jalign_) {}
text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other)
{
@ -165,6 +200,10 @@ namespace mapnik
avoid_edges_ = other.avoid_edges_;
minimum_distance_ = other.minimum_distance_;
overlap_ = other.overlap_;
opacity_ = other.opacity_;
wrap_before_ = other.wrap_before_;
halign_ = other.halign_;
jalign_ = other.jalign_;
return *this;
}
@ -218,6 +257,16 @@ namespace mapnik
wrap_width_ = width;
}
bool text_symbolizer::get_wrap_before() const
{
return wrap_before_;
}
void text_symbolizer::set_wrap_before(bool wrap_before)
{
wrap_before_ = wrap_before;
}
unsigned char text_symbolizer::get_wrap_char() const
{
return wrap_char_;
@ -417,4 +466,34 @@ namespace mapnik
{
return overlap_;
}
void text_symbolizer::set_opacity(double opacity)
{
opacity_ = opacity;
}
double text_symbolizer::get_opacity() const
{
return opacity_;
}
void text_symbolizer::set_horizontal_alignment(horizontal_alignment_e halign)
{
halign_ = halign;
}
horizontal_alignment_e text_symbolizer::get_horizontal_alignment() const
{
return halign_;
}
void text_symbolizer::set_justify_alignment(justify_alignment_e jalign)
{
jalign_ = jalign;
}
justify_alignment_e text_symbolizer::get_justify_alignment() const
{
return jalign_;
}
}