mapnik/src/placement_finder.cpp

1060 lines
37 KiB
C++
Raw Normal View History

/*****************************************************************************
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.
2009-10-19 15:52:53 +02:00
*
* 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
*
*****************************************************************************/
2007-10-08 19:42:41 +02:00
//mapnik
2013-01-30 18:32:20 +01:00
#include <mapnik/debug.hpp>
2013-01-04 08:56:11 +01:00
#include <mapnik/feature.hpp>
2007-10-08 19:42:41 +02:00
#include <mapnik/placement_finder.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/fastmath.hpp>
2012-02-12 03:55:13 +01:00
#include <mapnik/text_placements/base.hpp>
// agg
#include "agg_path_length.h"
// boost
#include <boost/shared_ptr.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/foreach.hpp>
2007-10-08 19:42:41 +02:00
//stl
#include <string>
#include <vector>
#include <cmath>
2006-11-28 11:38:56 +01:00
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace mapnik
{
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.
2009-10-19 15:52:53 +02:00
template<typename T>
std::pair<double, double> get_position_at_distance(double target_distance, T & shape_path)
{
double x1 = 0.0;
double y1 = 0.0;
double x2 = 0.0;
double y2 = 0.0;
double distance = 0.0;
bool first = true;
unsigned cmd;
double x = 0.0;
double y = 0.0;
shape_path.rewind(0);
while (!agg::is_stop(cmd = shape_path.vertex(&x2,&y2)))
{
2010-06-02 13:03:30 +02:00
if (first || agg::is_move_to(cmd))
{
first = false;
2010-06-02 13:03:30 +02:00
}
else
{
double dx = x2-x1;
double dy = y2-y1;
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.
2009-10-19 15:52:53 +02:00
2009-06-03 07:26:46 +02:00
double segment_length = std::sqrt(dx*dx + dy*dy);
distance +=segment_length;
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.
2009-10-19 15:52:53 +02:00
if (distance > target_distance)
{
2010-06-02 13:03:30 +02:00
x = x2 - dx * (distance - target_distance)/segment_length;
y = y2 - dy * (distance - target_distance)/segment_length;
break;
}
2010-06-02 13:03:30 +02:00
}
x1 = x2;
y1 = y2;
}
return std::pair<double, double>(x, y);
}
template<typename T>
double get_total_distance(T & shape_path)
{
return agg::path_length(shape_path);
}
template <typename DetectorT>
2013-01-04 08:56:11 +01:00
placement_finder<DetectorT>::placement_finder(feature_impl const& feature,
text_placement_info const& placement_info,
string_info const& info,
DetectorT & detector,
box2d<double> const& extent)
: detector_(detector),
2012-01-22 18:41:04 +01:00
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_(),
collect_extents_(false)
{
init_string_size();
init_alignment();
}
template <typename DetectorT>
template <typename T>
2012-01-22 18:41:04 +01:00
void placement_finder<DetectorT>::find_point_placements(T & shape_path)
{
unsigned cmd;
double new_x = 0.0;
double new_y = 0.0;
double old_x = 0.0;
double old_y = 0.0;
bool first = true;
double total_distance = get_total_distance<T>(shape_path);
shape_path.rewind(0);
if (total_distance == 0) //Point data, not a line
{
2010-06-02 13:03:30 +02:00
double x, y;
shape_path.vertex(&x,&y);
2012-01-22 18:41:04 +01:00
find_point_placement(x, y);
2010-06-02 13:03:30 +02:00
return;
}
int num_labels = 1;
if (p.label_spacing > 0)
2012-01-22 18:41:04 +01:00
num_labels = static_cast<int> (floor(total_distance / pi.get_actual_label_spacing()));
2012-01-22 18:41:04 +01:00
if (p.force_odd_labels && num_labels % 2 == 0)
2010-06-02 13:03:30 +02:00
num_labels--;
if (num_labels <= 0)
2010-06-02 13:03:30 +02:00
num_labels = 1;
double distance = 0.0; // distance from last label
double spacing = total_distance / num_labels;
double target_distance = spacing / 2; // first label should be placed at half the spacing
while (!agg::is_stop(cmd = shape_path.vertex(&new_x,&new_y))) //For each node in the shape
{
2010-06-02 13:03:30 +02:00
if (first || agg::is_move_to(cmd)) //Don't do any processing if it is the first node
{
first = false;
2010-06-02 13:03:30 +02:00
}
else
{
//Add the length of this segment to the total we have saved up
2009-06-03 07:26:46 +02:00
double segment_length = std::sqrt(std::pow(old_x-new_x,2) + std::pow(old_y-new_y,2)); //Pythagoras
distance += segment_length;
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.
2009-10-19 15:52:53 +02:00
//While we have enough distance to place text in
while (distance > target_distance)
{
2010-06-02 13:03:30 +02:00
//Try place at the specified place
double new_weight = (segment_length - (distance - target_distance))/segment_length;
2012-01-22 18:41:04 +01:00
find_point_placement(old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight);
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
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.
}
2010-06-02 13:03:30 +02:00
}
2010-06-02 13:03:30 +02:00
old_x = new_x;
old_y = new_y;
}
}
template <typename DetectorT>
2012-01-22 18:41:04 +01:00
void placement_finder<DetectorT>::init_string_size()
{
2012-01-22 18:41:04 +01:00
// Get total string size
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
string_height_ -= first_line_space_; //First line is a bit smaller
2012-01-22 18:41:04 +01:00
}
2012-01-22 18:41:04 +01:00
template <typename DetectorT>
void placement_finder<DetectorT>::find_line_breaks()
{
if (!line_sizes_.empty()) return;
2012-01-22 18:41:04 +01:00
bool first_line = true;
// check if we need to wrap the string
2012-01-22 18:41:04 +01:00
double wrap_at = string_width_ + 1.0;
if (p.wrap_width && string_width_ > p.wrap_width)
{
2010-06-02 13:03:30 +02:00
if (p.text_ratio)
2012-03-05 20:31:58 +01:00
{
2012-01-22 18:41:04 +01:00
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) ;
2012-03-05 20:31:58 +01:00
}
2010-06-02 13:03:30 +02:00
else
2012-03-05 20:31:58 +01:00
{
wrap_at = p.wrap_width;
2012-03-05 20:31:58 +01:00
}
}
// work out where our line breaks need to be and the resultant width to the 'wrapped' string
2012-01-22 18:41:04 +01:00
if ((wrap_at < string_width_) || info_.has_line_breaks())
{
2012-01-22 18:41:04 +01:00
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;
2010-06-02 13:03:30 +02:00
double line_width = 0.0;
2012-01-22 18:41:04 +01:00
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 and word_width include char width + spacing, but not the spacing after the last char
2010-06-02 13:03:30 +02:00
2012-01-22 18:41:04 +01:00
for (unsigned int ii = 0; ii < info_.num_characters(); ii++)
2010-06-02 13:03:30 +02:00
{
2012-01-22 18:41:04 +01:00
char_info const& ci = info_.at(ii);
2012-01-22 01:08:14 +01:00
unsigned c = ci.c;
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.
2009-10-19 15:52:53 +02:00
2012-01-22 18:41:04 +01:00
if ((c == ci.format->wrap_char) || (c == '\n'))
{
2012-01-22 18:41:04 +01:00
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; //Current one is included in last_wrap_char_width
2010-06-02 13:03:30 +02:00
word_width = 0.0;
2012-01-22 18:41:04 +01:00
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);
//TODO: I think this calculation could be wrong if height changes for the first word in the second line
2012-01-22 18:41:04 +01:00
if (first_line) first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height);
}
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.
2009-10-19 15:52:53 +02:00
// wrap text at first wrap_char after (default) the wrap width or immediately before the current word
if ((c == '\n') ||
2012-03-05 20:31:58 +01:00
(line_width > 0 &&
2012-03-13 15:56:11 +01:00
((line_width > wrap_at && !ci.format->wrap_before) ||
2012-03-05 20:31:58 +01:00
((line_width + last_wrap_char_width + word_width) > wrap_at && ci.format->wrap_before)) )
2012-03-13 15:56:11 +01:00
)
{
add_line(line_width, line_height, first_line);
2012-01-22 18:41:04 +01:00
line_breaks_.push_back(last_wrap_char_pos);
2010-06-02 13:03:30 +02:00
line_width = 0.0;
2012-01-22 18:41:04 +01:00
line_height = 0.0;
last_wrap_char_width = 0; //Wrap char supressed
first_line = false;
}
2010-06-02 13:03:30 +02:00
}
2012-01-22 18:41:04 +01:00
line_width += last_wrap_char_width + word_width;
line_height = std::max(line_height, word_height);
add_line(line_width, line_height, first_line);
2012-01-22 18:41:04 +01:00
} else {
//No linebreaks
line_sizes_.push_back(std::make_pair(string_width_, string_height_));
}
2012-01-22 18:41:04 +01:00
line_breaks_.push_back(info_.num_characters());
}
template <typename DetectorT>
void placement_finder<DetectorT>::add_line(double width, double height, bool first_line)
{
if (first_line) height -= first_line_space_;
string_width_ = std::max(string_width_, width); //Total width is the longest line
string_height_ += height;
line_sizes_.push_back(std::make_pair(width, height));
}
2012-01-22 18:41:04 +01:00
template <typename DetectorT>
void placement_finder<DetectorT>::init_alignment()
{
valign_ = p.valign;
if (valign_ == V_AUTO)
{
if (p.displacement.second > 0.0)
{
2012-01-22 18:41:04 +01:00
valign_ = V_BOTTOM;
} else if (p.displacement.second < 0.0)
{
2012-01-22 18:41:04 +01:00
valign_ = V_TOP;
} else
{
2012-01-22 18:41:04 +01:00
valign_ = V_MIDDLE;
}
}
2012-01-22 18:41:04 +01:00
halign_ = p.halign;
if (halign_ == H_AUTO)
{
if (p.displacement.first > 0.0)
{
2012-01-22 18:41:04 +01:00
halign_ = H_RIGHT;
} else if (p.displacement.first < 0.0)
{
2012-01-22 18:41:04 +01:00
halign_ = H_LEFT;
} else
{
2012-01-22 18:41:04 +01:00
halign_ = H_MIDDLE;
}
}
jalign_ = p.jalign;
if (jalign_ == J_AUTO)
{
if (p.displacement.first > 0.0)
{
jalign_ = J_LEFT;
} else if (p.displacement.first < 0.0)
{
jalign_ = J_RIGHT;
} else {
jalign_ = J_MIDDLE;
}
}
2012-01-22 18:41:04 +01:00
}
2012-01-22 18:41:04 +01:00
template <typename DetectorT>
void placement_finder<DetectorT>::adjust_position(text_path *current_placement)
2012-01-22 18:41:04 +01:00
{
// if needed, adjust for desired vertical alignment
if (valign_ == V_TOP)
{
current_placement->center.y -= 0.5 * string_height_; // move center up by 1/2 the total height
} else if (valign_ == V_BOTTOM)
{
current_placement->center.y += 0.5 * string_height_; // move center down by the 1/2 the total height
2012-01-22 18:41:04 +01:00
}
// set horizontal position to middle of text
2012-01-22 18:41:04 +01:00
if (halign_ == H_LEFT)
{
current_placement->center.x -= 0.5 * string_width_; // move center left by 1/2 the string width
} else if (halign_ == H_RIGHT)
{
current_placement->center.x += 0.5 * string_width_; // move center right by 1/2 the string width
}
2012-01-22 18:41:04 +01:00
// adjust text envelope position by user's x-y displacement (dx, dy)
current_placement->center.x += pi.get_scale_factor() * p.displacement.first;
current_placement->center.y += pi.get_scale_factor() * p.displacement.second;
2012-01-22 18:41:04 +01:00
}
2012-01-22 18:41:04 +01:00
template <typename DetectorT>
2012-03-05 20:31:58 +01:00
void placement_finder<DetectorT>::find_point_placement(double label_x,
double label_y,
double angle)
2012-01-22 18:41:04 +01:00
{
find_line_breaks();
2012-01-22 18:41:04 +01:00
double rad = M_PI * angle/180.0;
double cosa = std::cos(rad);
double sina = std::sin(rad);
double x, y;
std::auto_ptr<text_path> current_placement(new text_path(label_x, label_y));
2012-01-22 18:41:04 +01:00
adjust_position(current_placement.get());
// presets for first line
unsigned int line_number = 0;
2012-01-22 18:41:04 +01:00
unsigned int index_to_wrap_at = line_breaks_[0];
double line_width = line_sizes_[0].first;
double line_height = line_sizes_[0].second;
/* IMPORTANT NOTE:
x and y are relative to the center of the text
coordinate system:
x: grows from left to right
y: grows from bottom to top (opposite of normal computer graphics)
*/
// set for upper left corner of text envelope for the first line, bottom left of first character
y = string_height_ / 2.0 - line_height;
2012-03-16 01:01:12 +01:00
// RTL text is converted to a mirrored representation in get_string_info()
// so we have to fix line break order here
if (info_.get_rtl()) y = -y;
2012-01-22 18:41:04 +01:00
// adjust for desired justification
if (jalign_ == J_LEFT)
2012-01-22 18:41:04 +01:00
x = -(string_width_ / 2.0);
else if (jalign_ == J_RIGHT)
2012-01-22 18:41:04 +01:00
x = (string_width_ / 2.0) - line_width;
else /* J_MIDDLE */
2012-01-22 18:41:04 +01:00
x = -(line_width / 2.0);
// save each character rendering position and build envelope as go thru loop
std::queue< box2d<double> > c_envelopes;
2012-01-22 18:41:04 +01:00
for (unsigned i = 0; i < info_.num_characters(); i++)
{
2012-01-22 18:41:04 +01:00
char_info const& ci = info_.at(i);
2012-01-22 18:41:04 +01:00
double cwidth = ci.width + ci.format->character_spacing;
2010-06-02 13:03:30 +02:00
if (i == index_to_wrap_at)
{
2012-01-22 18:41:04 +01:00
index_to_wrap_at = line_breaks_[++line_number];
line_width = line_sizes_[line_number].first;
line_height= line_sizes_[line_number].second;
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.
2009-10-19 15:52:53 +02:00
2012-03-16 01:01:12 +01:00
if (info_.get_rtl())
{
y += line_height;
} else
{
y -= line_height; // move position down to line start
}
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.
2009-10-19 15:52:53 +02:00
// reset to begining of line position
if (jalign_ == J_LEFT)
2012-01-22 18:41:04 +01:00
x = -(string_width_ / 2.0);
else if (jalign_ == J_RIGHT)
2012-01-22 18:41:04 +01:00
x = (string_width_ / 2.0) - line_width;
else
x = -(line_width / 2.0);
continue;
2010-06-02 13:03:30 +02:00
}
else
{
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.
2009-10-19 15:52:53 +02:00
// place the character relative to the center of the string envelope
2010-06-02 13:03:30 +02:00
double dx = x * cosa - y*sina;
double dy = x * sina + y*cosa;
current_placement->add_node(&ci, dx, dy, rad);
2012-02-02 02:53:35 +01:00
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.
2009-10-19 15:52:53 +02:00
// compute the Bounding Box for each character and test for:
// overlap, minimum distance or edge avoidance - exit if condition occurs
2009-12-16 21:02:06 +01:00
box2d<double> e;
2012-01-22 18:41:04 +01:00
/*x axis: left to right, y axis: top to bottom (negative values higher)*/
e.init(current_placement->center.x + dx, // Bottom Left
current_placement->center.y - dy - ci.ymin, // ymin usually <0
current_placement->center.x + dx + ci.width, // Top Right
current_placement->center.y - dy - ci.ymax);
2012-02-02 02:53:35 +01:00
// if there is an overlap with existing envelopes, then exit - no placement
if (!detector_.extent().intersects(e) ||
(!p.allow_overlap &&
2012-03-13 15:56:11 +01:00
!detector_.has_point_placement(e, pi.get_actual_minimum_distance())))
{
2010-06-02 13:03:30 +02:00
return;
2012-01-22 18:41:04 +01:00
}
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.
2009-10-19 15:52:53 +02:00
2012-02-02 02:53:35 +01:00
// if avoid_edges test dimensions contains e
2012-03-05 20:31:58 +01:00
if (p.avoid_edges && !dimensions_.contains(e))
{
2010-06-02 13:03:30 +02:00
return;
2012-01-22 18:41:04 +01:00
}
2012-02-02 02:53:35 +01:00
2012-01-22 18:41:04 +01:00
if (p.minimum_padding > 0)
{
box2d<double> epad = e;
epad.pad(pi.get_actual_minimum_padding());
2012-02-02 02:53:35 +01:00
if (!dimensions_.contains(epad))
{
return;
}
}
2012-02-02 02:53:35 +01:00
c_envelopes.push(e); // add character's envelope to temp storage
2010-06-02 13:03:30 +02:00
}
x += cwidth; // move position to next character
}
// check the placement of any additional envelopes
2012-07-13 11:25:12 +02:00
if (!p.allow_overlap && !additional_boxes_.empty())
{
2012-07-13 11:25:12 +02:00
BOOST_FOREACH(box2d<double> const& box, additional_boxes_)
2011-12-15 15:57:57 +01:00
{
box2d<double> pt(box.minx() + current_placement->center.x,
box.miny() + current_placement->center.y,
box.maxx() + current_placement->center.x,
box.maxy() + current_placement->center.y);
2011-12-15 15:57:57 +01:00
// abort the whole placement if the additional envelopes can't be placed.
if (!detector_.has_point_placement(pt, p.minimum_distance)) return;
c_envelopes.push(pt);
}
}
// since there was no early exit, add the character envelopes to the placements' envelopes
while (!c_envelopes.empty())
{
envelopes_.push(c_envelopes.front());
c_envelopes.pop();
}
placements_.push_back(current_placement.release());
}
template <typename DetectorT>
template <typename PathT>
2012-01-22 18:41:04 +01:00
void placement_finder<DetectorT>::find_line_placements(PathT & shape_path)
{
2012-04-08 02:20:56 +02:00
#ifdef MAPNIK_LOG
if (! line_sizes_.empty())
{
MAPNIK_LOG_WARN(placement_finder) << "Internal error. Text contains line breaks, but line placement is used. Please file a bug report!";
}
#endif
unsigned cmd;
double new_x = 0.0;
double new_y = 0.0;
double old_x = 0.0;
double old_y = 0.0;
bool first = true;
//Pre-Cache all the path_positions and path_distances
//This stops the PathT from having to do multiple re-projections if we need to reposition ourself
// and lets us know how many points are in the shape.
std::vector<vertex2d> path_positions;
std::vector<double> path_distances; // distance from node x-1 to node x
double total_distance = 0;
shape_path.rewind(0);
while (!agg::is_stop(cmd = shape_path.vertex(&new_x,&new_y))) //For each node in the shape
{
2010-06-02 13:03:30 +02:00
if (!first && agg::is_line_to(cmd))
{
double dx = old_x - new_x;
double dy = old_y - new_y;
2009-06-03 07:26:46 +02:00
double distance = std::sqrt(dx*dx + dy*dy);
total_distance += distance;
path_distances.push_back(distance);
2010-06-02 13:03:30 +02:00
}
else
{
path_distances.push_back(0);
2010-06-02 13:03:30 +02:00
}
first = false;
path_positions.push_back(vertex2d(new_x, new_y, cmd));
old_x = new_x;
old_y = new_y;
}
//Now path_positions is full and total_distance is correct
//shape_path shouldn't be used from here
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.
2009-10-19 15:52:53 +02:00
// Ensure lines have a minimum length.
if (total_distance < p.minimum_path_length)
return;
double distance = 0.0;
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.
2009-10-19 15:52:53 +02:00
double displacement = p.displacement.second; // displace by dy
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.
2009-10-19 15:52:53 +02:00
//Calculate a target_distance that will place the labels centered evenly rather than offset from the start of the linestring
2012-01-22 18:41:04 +01:00
if (total_distance < string_width_) //Can't place any strings
return;
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.
2009-10-19 15:52:53 +02:00
//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)
2012-03-05 20:31:58 +01:00
num_labels = static_cast<int>(floor(total_distance / (pi.get_actual_label_spacing() + string_width_)));
2012-01-22 18:41:04 +01:00
if (p.force_odd_labels && (num_labels % 2 == 0))
2010-06-02 13:03:30 +02:00
num_labels--;
if (num_labels <= 0)
2010-06-02 13:03:30 +02:00
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;
2012-01-22 18:41:04 +01:00
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;
if (p.label_position_tolerance > 0)
{
2010-06-02 13:03:30 +02:00
tolerance = p.label_position_tolerance;
tolerance_delta = std::max ( 1.0, p.label_position_tolerance/100.0 );
}
else
{
2010-06-02 13:03:30 +02:00
tolerance = spacing/2.0;
tolerance_delta = std::max ( 1.0, spacing/100.0 );
}
first = true;
for (unsigned index = 0; index < path_positions.size(); index++) //For each node in the shape
{
2010-06-02 13:03:30 +02:00
cmd = path_positions[index].cmd;
new_x = path_positions[index].x;
new_y = path_positions[index].y;
2010-06-02 13:03:30 +02:00
if (first || agg::is_move_to(cmd)) //Don't do any processing if it is the first node
{
first = false;
2010-06-02 13:03:30 +02:00
}
else
{
//Add the length of this segment to the total we have saved up
double segment_length = path_distances[index];
distance += segment_length;
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.
2009-10-19 15:52:53 +02:00
//While we have enough distance to place text in
while (distance > target_distance)
{
2010-06-02 13:03:30 +02:00
for (double diff = 0; diff < tolerance; diff += tolerance_delta)
{
for(int dir = -1; dir < 2; dir+=2) //-1, +1
{
//Record details for the start of the string placement
int orientation = 0;
2012-01-28 00:09:58 +01:00
std::auto_ptr<text_path> current_placement = get_placement_offset(path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir));
2010-06-02 13:03:30 +02:00
//We were unable to place here
if (current_placement.get() == NULL)
continue;
//Apply displacement
//NOTE: The text is centered on the line in get_placement_offset, so we are offsetting from there
if (displacement != 0)
{
//Average the angle of all characters and then offset them all by that angle
double anglesum = 0;
for (unsigned i = 0; i < current_placement->nodes_.size(); i++)
{
double angle = current_placement->nodes_[i].angle;
//Normalize angle in range -PI ... PI
while (angle > M_PI) {
angle -= 2*M_PI;
}
anglesum += angle;
2010-06-02 13:03:30 +02:00
}
anglesum /= current_placement->nodes_.size(); //Now it is angle average
double cosa = orientation * std::cos(anglesum);
double sina = orientation * std::sin(anglesum);
2010-06-02 13:03:30 +02:00
//Offset all the characters by this angle
for (unsigned i = 0; i < current_placement->nodes_.size(); i++)
{
current_placement->nodes_[i].pos.x -=
pi.get_scale_factor() * displacement * sina;
current_placement->nodes_[i].pos.y +=
pi.get_scale_factor() * displacement * cosa;
2010-06-02 13:03:30 +02:00
}
}
2012-01-22 18:41:04 +01:00
bool status = test_placement(current_placement, orientation);
2010-06-02 13:03:30 +02:00
if (status) //We have successfully placed one
{
placements_.push_back(current_placement.release());
2012-01-22 18:41:04 +01:00
update_detector();
2010-06-02 13:03:30 +02:00
//Totally break out of the loops
diff = tolerance;
break;
}
else
{
//If we've failed to place, remove all the envelopes we've added up
while (!envelopes_.empty())
envelopes_.pop();
2010-06-02 13:03:30 +02:00
}
//Don't need to loop twice when diff = 0
if (diff == 0)
break;
}
}
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.
}
2010-06-02 13:03:30 +02:00
}
2010-06-02 13:03:30 +02:00
old_x = new_x;
old_y = new_y;
}
}
template <typename DetectorT>
2012-03-05 20:31:58 +01:00
std::auto_ptr<text_path> placement_finder<DetectorT>::get_placement_offset(std::vector<vertex2d> const& path_positions,
2012-03-13 15:56:11 +01:00
std::vector<double> const& 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)
{
2010-06-02 13:03:30 +02:00
index--;
distance += path_distances[index];
}
if (index <= 1 && distance < 0) //We've gone off the start, fail out
2012-01-28 00:09:58 +01:00
return std::auto_ptr<text_path>(NULL);
//Same thing, checking if we go off the end
while (index < path_distances.size() && distance > path_distances[index])
{
2010-06-02 13:03:30 +02:00
distance -= path_distances[index];
index++;
}
if (index >= path_distances.size())
2012-01-28 00:09:58 +01:00
return std::auto_ptr<text_path>(NULL);
//Keep track of the initial index,distance incase we need to re-call get_placement_offset
const unsigned initial_index = index;
const double initial_distance = distance;
double old_x = path_positions[index-1].x;
double old_y = path_positions[index-1].y;
double new_x = path_positions[index].x;
double new_y = path_positions[index].y;
double dx = new_x - old_x;
double dy = new_y - old_y;
double segment_length = path_distances[index];
if (segment_length == 0) {
2010-06-02 13:03:30 +02:00
// Not allowed to place across on 0 length segments or discontinuities
2012-01-28 00:09:58 +01:00
return std::auto_ptr<text_path>(NULL);
}
std::auto_ptr<text_path> current_placement(
2012-03-13 15:56:11 +01:00
new text_path((old_x + dx*distance/segment_length),
(old_y + dy*distance/segment_length)
)
);
2012-01-22 18:41:04 +01:00
double angle = atan2(-dy, dx);
bool orientation_forced = (orientation != 0); // Whether the orientation was set by the caller
if (!orientation_forced)
2010-06-02 13:03:30 +02:00
orientation = (angle > 0.55*M_PI || angle < -0.45*M_PI) ? -1 : 1;
unsigned upside_down_char_count = 0; //Count of characters that are placed upside down.
2012-01-22 18:41:04 +01:00
for (unsigned i = 0; i < info_.num_characters(); ++i)
{
2012-01-22 18:41:04 +01:00
// grab the next character according to the orientation
char_info const &ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1);
double cwidth = ci.width + ci.format->character_spacing;
2010-06-02 13:03:30 +02:00
double last_character_angle = angle;
2010-06-02 13:03:30 +02:00
//Coordinates this character will start at
if (segment_length == 0) {
// Not allowed to place across on 0 length segments or discontinuities
2012-01-28 00:09:58 +01:00
return std::auto_ptr<text_path>(NULL);
}
2010-06-02 13:03:30 +02:00
double start_x = old_x + dx*distance/segment_length;
double start_y = old_y + dy*distance/segment_length;
//Coordinates this character ends at, calculated below
double end_x = 0;
double end_y = 0;
if (segment_length - distance >= cwidth)
2010-06-02 13:03:30 +02:00
{
//if the distance remaining in this segment is enough, we just go further along the segment
distance += cwidth;
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.
2009-10-19 15:52:53 +02:00
end_x = old_x + dx*distance/segment_length;
end_y = old_y + dy*distance/segment_length;
2010-06-02 13:03:30 +02:00
}
else
{
//If there isn't enough distance left on this segment
// then we need to search until we find the line segment that ends further than ci.width away
do
{
2010-06-02 13:03:30 +02:00
old_x = new_x;
old_y = new_y;
index++;
if (index >= path_positions.size()) //Bail out if we run off the end of the shape
{
//MAPNIK_LOG_ERROR(placement_finder) << "FAIL: Out of space";
2012-01-28 00:09:58 +01:00
return std::auto_ptr<text_path>(NULL);
2010-06-02 13:03:30 +02:00
}
new_x = path_positions[index].x;
new_y = path_positions[index].y;
dx = new_x - old_x;
dy = new_y - old_y;
segment_length = path_distances[index];
}
while (std::sqrt(std::pow(start_x - new_x, 2) + std::pow(start_y - new_y, 2)) < cwidth); //Distance from start_ to new_
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.
2009-10-19 15:52:53 +02:00
//Calculate the position to place the end of the character on
find_line_circle_intersection(
start_x, start_y, cwidth,
2010-06-02 13:03:30 +02:00
old_x, old_y, new_x, new_y,
end_x, end_y); //results are stored in end_x, end_y
//Need to calculate distance on the new segment
2009-06-03 07:26:46 +02:00
distance = std::sqrt(std::pow(old_x - end_x, 2) + std::pow(old_y - end_y, 2));
2010-06-02 13:03:30 +02:00
}
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
//Calculate angle from the start of the character to the end based on start_/end_ position
angle = fast_atan2(start_y-end_y, end_x-start_x);
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
//Test last_character_angle vs angle
// since our rendering angle has changed then check against our
// max allowable angle change.
double angle_delta = last_character_angle - angle;
// normalise between -180 and 180
while (angle_delta > M_PI)
angle_delta -= 2*M_PI;
2010-06-02 13:03:30 +02:00
while (angle_delta < -M_PI)
angle_delta += 2*M_PI;
2010-06-02 13:03:30 +02:00
if (p.max_char_angle_delta > 0 &&
std::fabs(angle_delta) > p.max_char_angle_delta)
2010-06-02 13:03:30 +02:00
{
//MAPNIK_LOG_ERROR(placement_finder) << "FAIL: Too Bendy!";
2012-01-28 00:09:58 +01:00
return std::auto_ptr<text_path>(NULL);
2010-06-02 13:03:30 +02:00
}
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
double render_angle = angle;
double cosa = fast_cos(angle);
double sina = fast_sin(angle);
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
double render_x = start_x;
double render_y = start_y;
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
//Center the text on the line
2012-01-22 18:41:04 +01:00
double char_height = ci.avg_height;
render_x += char_height/2.0*sina;
render_y += char_height/2.0*cosa;
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
if (orientation < 0)
{
// rotate in place
2012-03-03 13:50:26 +01:00
render_x += cwidth*cosa - char_height*sina;
render_y -= cwidth*sina + char_height*cosa;
render_angle += M_PI;
2010-06-02 13:03:30 +02:00
}
current_placement->add_node(&ci,
2012-03-13 15:56:11 +01:00
render_x - current_placement->center.x,
-render_y + current_placement->center.y,
render_angle);
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
//Normalise to 0 <= angle < 2PI
while (render_angle >= 2*M_PI)
render_angle -= 2*M_PI;
2010-06-02 13:03:30 +02:00
while (render_angle < 0)
render_angle += 2*M_PI;
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
if (render_angle > M_PI/2 && render_angle < 1.5*M_PI)
upside_down_char_count++;
}
//If we placed too many characters upside down
2012-01-22 18:41:04 +01:00
if (upside_down_char_count >= info_.num_characters()/2.0)
{
2010-06-02 13:03:30 +02:00
//if we auto-detected the orientation then retry with the opposite orientation
if (!orientation_forced)
{
orientation = -orientation;
2012-03-05 20:31:58 +01:00
current_placement = get_placement_offset(path_positions,
path_distances,
orientation,
initial_index,
initial_distance);
2010-06-02 13:03:30 +02:00
}
else
{
//Otherwise we have failed to find a placement
//MAPNIK_LOG_ERROR(placement_finder) << "FAIL: Double upside-down!";
2012-01-28 00:09:58 +01:00
return std::auto_ptr<text_path>(NULL);
2010-06-02 13:03:30 +02:00
}
}
return current_placement;
}
template <typename DetectorT>
2012-03-05 20:31:58 +01:00
bool placement_finder<DetectorT>::test_placement(std::auto_ptr<text_path> const& current_placement,
2012-03-13 15:56:11 +01:00
int orientation)
{
//Create and test envelopes
bool status = true;
2012-01-22 18:41:04 +01:00
for (unsigned i = 0; i < info_.num_characters(); ++i)
{
//TODO: I think this can be simplified by taking the char_info from vertex() but this needs to be carefully tested!
2010-06-02 13:03:30 +02:00
// grab the next character according to the orientation
2012-01-22 18:41:04 +01:00
char_info const& ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1);
double cwidth = ci.width + ci.format->character_spacing;
char_info_ptr c;
2010-06-02 13:03:30 +02:00
double x, y, angle;
current_placement->vertex(c, x, y, angle);
x = current_placement->center.x + x;
y = current_placement->center.y - y;
double sina = fast_sin(angle);
double cosa = fast_cos(angle);
2010-06-02 13:03:30 +02:00
if (orientation < 0)
{
// rotate in place
x += cwidth * cosa - string_height_ * sina;
y -= cwidth * sina + string_height_ * cosa;
angle += M_PI;
//sin(x+PI) = -sin(x)
sina = -sina;
cosa = -cosa;
2010-06-02 13:03:30 +02:00
}
box2d<double> e(x, y, x + cwidth*cosa, y - cwidth*sina);
// put four corners of the letter into envelope
e.expand_to_include(x - ci.height()*sina,
y - ci.height()*cosa);
e.expand_to_include(x + (cwidth*cosa - ci.height()*sina),
y - (cwidth*sina + ci.height()*cosa));
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.
2009-10-19 15:52:53 +02:00
if (!detector_.extent().intersects(e) ||
(!p.allow_overlap &&
!detector_.has_placement(e, info_.get_string(), pi.get_actual_minimum_distance())
2012-03-13 15:56:11 +01:00
)
)
2010-06-02 13:03:30 +02:00
{
//MAPNIK_LOG_ERROR(placement_finder) << "No Intersects:" << !dimensions_.intersects(e) << ": " << e << " @ " << dimensions_;
//MAPNIK_LOG_ERROR(placement_finder) << "No Placements:" << !detector_.has_placement(e, info.get_string(), p.minimum_distance);
status = false;
break;
2010-06-02 13:03:30 +02:00
}
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.
2009-10-19 15:52:53 +02:00
2010-06-02 13:03:30 +02:00
if (p.avoid_edges && !dimensions_.contains(e))
{
//MAPNIK_LOG_ERROR(placement_finder) << "Fail avoid edges";
status = false;
break;
2010-06-02 13:03:30 +02:00
}
2012-01-22 18:41:04 +01:00
if (p.minimum_padding > 0)
2012-02-02 02:53:35 +01:00
{
box2d<double> epad = e;
epad.pad(pi.get_actual_minimum_padding());
2012-02-02 02:53:35 +01:00
if (!dimensions_.contains(epad))
{
status = false;
break;
}
}
envelopes_.push(e);
}
2012-02-02 02:53:35 +01:00
current_placement->rewind();
return status;
}
template <typename DetectorT>
void placement_finder<DetectorT>::find_line_circle_intersection(
double cx, double cy, double radius,
double x1, double y1, double x2, double y2,
double & ix, double & iy)
{
double dx = x2 - x1;
double dy = y2 - y1;
double A = dx * dx + dy * dy;
double B = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
double C = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy) - radius * radius;
double det = B * B - 4 * A * C;
if (A <= 0.0000001 || det < 0)
{
2010-06-02 13:03:30 +02:00
//Should never happen
//' No real solutions.
return;
}
else if (det == 0)
{
2010-06-02 13:03:30 +02:00
//Could potentially happen....
//One solution.
double t = -B / (2 * A);
ix = x1 + t * dx;
iy = y1 + t * dy;
return;
}
else
{
2010-06-02 13:03:30 +02:00
//Two solutions.
2010-06-02 13:03:30 +02:00
//Always use the 1st one
//We only really have one solution here, as we know the line segment will start in the circle and end outside
double t = (-B + std::sqrt(det)) / (2 * A);
ix = x1 + t * dx;
iy = y1 + t * dy;
2010-06-02 13:03:30 +02:00
//t = (-B - std::sqrt(det)) / (2 * A);
//ix = x1 + t * dx;
//iy = y1 + t * dy;
2010-06-02 13:03:30 +02:00
return;
}
}
template <typename DetectorT>
2012-01-22 18:41:04 +01:00
void placement_finder<DetectorT>::update_detector()
{
if (collect_extents_) extents_.init(0,0,0,0);
// add the bboxes to the detector and remove from the placement
while (!envelopes_.empty())
{
box2d<double> e = envelopes_.front();
2012-01-22 18:41:04 +01:00
detector_.insert(e, info_.get_string());
envelopes_.pop();
if (collect_extents_)
{
extents_.expand_to_include(e);
}
}
}
template <typename DetectorT>
void placement_finder<DetectorT>::clear_placements()
{
placements_.clear();
while (!envelopes_.empty()) envelopes_.pop();
}
template class placement_finder<DetectorType>;
template void placement_finder<DetectorType>::find_point_placements<ClippedPathType>(ClippedPathType &);
template void placement_finder<DetectorType>::find_line_placements<ClippedPathType>(ClippedPathType &);
2012-01-22 18:41:04 +01:00
template void placement_finder<DetectorType>::find_point_placements<PathType>(PathType &);
template void placement_finder<DetectorType>::find_line_placements<PathType>(PathType &);
} // namespace