Remove find_placements, build_path_follow, build_path_horizontal.
Add new find_point_placements to cover the gap left. Change shield symbolizer to use find_point_placements. Results are the same as before, but with much less duplicate code.
This commit is contained in:
parent
f11a62b849
commit
5bbe90b85f
3 changed files with 64 additions and 414 deletions
|
@ -87,26 +87,21 @@ namespace mapnik
|
|||
public:
|
||||
placement_finder(DetectorT & detector);
|
||||
|
||||
template <typename T>
|
||||
void find_placements(placement & p, T & path);
|
||||
|
||||
//Try place a single label at the given point
|
||||
void find_point_placement(placement & p, double, double);
|
||||
|
||||
//Iterate over the given path, placing point labels with respect to label_spacing
|
||||
template <typename T>
|
||||
void find_line_placement(placement & p, T & path);
|
||||
void find_point_placements(placement & p, T & path);
|
||||
|
||||
//Iterate over the given path, placing line-following labels with respect to label_spacing
|
||||
template <typename T>
|
||||
void find_line_placements(placement & p, T & path);
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
bool build_path_follow(placement & p, double target_distance, T & path);
|
||||
|
||||
template <typename T>
|
||||
bool build_path_horizontal(placement & p, double target_distance, T & path);
|
||||
|
||||
void get_ideal_placements(placement & p, double distance, std::vector<double>&);
|
||||
|
||||
//Helpers for find_line_placement
|
||||
///Helpers for find_line_placement
|
||||
|
||||
///Returns a possible placement on the given line, does not test for collisions
|
||||
//index: index of the node the current line ends on
|
||||
|
@ -133,6 +128,7 @@ namespace mapnik
|
|||
const double &x1, const double &y1, const double &x2, const double &y2,
|
||||
double &ix, double &iy);
|
||||
|
||||
///General Internals
|
||||
|
||||
void update_detector(placement & p);
|
||||
|
||||
|
|
|
@ -464,7 +464,7 @@ namespace mapnik
|
|||
path_type path(t_,geom,prj_trans);
|
||||
placement text_placement(info, sym);
|
||||
text_placement.avoid_edges = sym.get_avoid_edges();
|
||||
finder.find_placements<path_type>(text_placement,path);
|
||||
finder.find_point_placements<path_type>(text_placement,path);
|
||||
|
||||
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
|
||||
{
|
||||
|
@ -683,7 +683,7 @@ namespace mapnik
|
|||
}
|
||||
else //LINE_PLACEMENT
|
||||
{
|
||||
finder.find_line_placement<path_type>(text_placement,path);
|
||||
finder.find_line_placements<path_type>(text_placement,path);
|
||||
}
|
||||
|
||||
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii)
|
||||
|
|
|
@ -143,87 +143,69 @@ namespace mapnik
|
|||
}
|
||||
|
||||
template <typename DetectorT>
|
||||
void placement_finder<DetectorT>::get_ideal_placements(placement & p, double distance, std::vector<double> & ideal_label_distances)
|
||||
template <typename T>
|
||||
void placement_finder<DetectorT>::find_point_placements(placement & p, T & shape_path)
|
||||
{
|
||||
std::pair<double, double> string_dimensions = p.info.get_dimensions();
|
||||
double string_width = string_dimensions.first;
|
||||
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;
|
||||
|
||||
if (p.label_placement == LINE_PLACEMENT && string_width > distance)
|
||||
double total_distance = get_total_distance<T>(shape_path);
|
||||
shape_path.rewind(0);
|
||||
|
||||
if (distance == 0) //Point data, not a line
|
||||
{
|
||||
//Empty!
|
||||
return ;
|
||||
double x, y;
|
||||
shape_path.vertex(&x,&y);
|
||||
find_point_placement(p, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
int num_labels = 0;
|
||||
if (p.label_spacing && p.label_placement == LINE_PLACEMENT)
|
||||
{
|
||||
num_labels = static_cast<int> (floor(distance / (p.label_spacing + string_width)));
|
||||
}
|
||||
else if (p.label_spacing && p.label_placement == POINT_PLACEMENT)
|
||||
{
|
||||
num_labels = static_cast<int> (floor(distance / p.label_spacing));
|
||||
}
|
||||
int num_labels = 1;
|
||||
if (p.label_spacing > 0)
|
||||
num_labels = static_cast<int> (floor(total_distance / p.label_spacing));
|
||||
|
||||
if (p.force_odd_labels && num_labels%2 == 0)
|
||||
num_labels--;
|
||||
if (num_labels <= 0)
|
||||
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
|
||||
|
||||
double ideal_spacing = distance/num_labels;
|
||||
|
||||
double middle; //try draw text centered
|
||||
if (p.label_placement == LINE_PLACEMENT)
|
||||
middle = (distance / 2.0) - (string_width/2.0);
|
||||
else // (p.label_placement == point_placement)
|
||||
middle = distance / 2.0;
|
||||
|
||||
if (num_labels % 2) //odd amount of labels
|
||||
while (!agg::is_stop(cmd = shape_path.vertex(&new_x,&new_y))) //For each node in the shape
|
||||
{
|
||||
for (int a = 0; a < (num_labels+1)/2; a++)
|
||||
{
|
||||
ideal_label_distances.push_back(middle - (a*ideal_spacing));
|
||||
|
||||
if (a != 0)
|
||||
ideal_label_distances.push_back(middle + (a*ideal_spacing));
|
||||
if (first || agg::is_move_to(cmd)) //Don't do any processing if it is the first node
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
else //even amount of labels
|
||||
else
|
||||
{
|
||||
for (int a = 0; a < num_labels/2; a++)
|
||||
//Add the length of this segment to the total we have saved up
|
||||
double segment_length = sqrt(pow(old_x-new_x,2) + pow(old_y-new_y,2)); //Pythagoras
|
||||
distance += segment_length;
|
||||
|
||||
//While we have enough distance to place text in
|
||||
while (distance > target_distance)
|
||||
{
|
||||
ideal_label_distances.push_back(middle - (ideal_spacing/2.0) - (a*ideal_spacing));
|
||||
ideal_label_distances.push_back(middle + (ideal_spacing/2.0) + (a*ideal_spacing));
|
||||
//Try place at the specified place
|
||||
double new_weight = (segment_length - (distance - target_distance))/segment_length;
|
||||
find_point_placement(p, old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight);
|
||||
|
||||
distance -= target_distance; //Consume the spacing gap we have used up
|
||||
target_distance = spacing; //Need to reset the target_distance as it is spacing/2 for the first label.
|
||||
}
|
||||
}
|
||||
|
||||
if (p.label_position_tolerance == 0)
|
||||
{
|
||||
p.label_position_tolerance = unsigned(ideal_spacing/2.0);
|
||||
}
|
||||
old_x = new_x;
|
||||
old_y = new_y;
|
||||
}
|
||||
|
||||
template <typename DetectorT>
|
||||
template <typename T>
|
||||
void placement_finder<DetectorT>::find_placements(placement & p, T & shape_path)
|
||||
{
|
||||
double distance = get_total_distance<T>(shape_path);
|
||||
std::vector<double> ideal_label_distances;
|
||||
get_ideal_placements(p,distance,ideal_label_distances);
|
||||
std::vector<double>::const_iterator itr = ideal_label_distances.begin();
|
||||
std::vector<double>::const_iterator end = ideal_label_distances.end();
|
||||
for (; itr != end; ++itr)
|
||||
{
|
||||
|
||||
if ((p.label_placement == LINE_PLACEMENT &&
|
||||
build_path_follow(p, *itr , shape_path ) ) ||
|
||||
(p.label_placement == POINT_PLACEMENT &&
|
||||
build_path_horizontal(p, *itr, shape_path)) )
|
||||
{
|
||||
update_detector(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DetectorT>
|
||||
|
@ -376,7 +358,7 @@ namespace mapnik
|
|||
|
||||
template <typename DetectorT>
|
||||
template <typename PathT>
|
||||
void placement_finder<DetectorT>::find_line_placement(placement & p, PathT & shape_path)
|
||||
void placement_finder<DetectorT>::find_line_placements(placement & p, PathT & shape_path)
|
||||
{
|
||||
unsigned cmd;
|
||||
double new_x = 0.0;
|
||||
|
@ -437,7 +419,7 @@ namespace mapnik
|
|||
num_labels = 1;
|
||||
|
||||
//Now we know how many labels we are going to place, calculate the spacing so that they will get placed evenly
|
||||
double spacing = (total_distance / num_labels);
|
||||
double spacing = total_distance / num_labels;
|
||||
double target_distance = (spacing - string_width) / 2; // first label should be placed at half the spacing
|
||||
|
||||
//Calculate or read out the tolerance
|
||||
|
@ -843,334 +825,6 @@ namespace mapnik
|
|||
}
|
||||
}
|
||||
|
||||
template <typename DetectorT>
|
||||
template <typename PathT>
|
||||
bool placement_finder<DetectorT>::build_path_follow(placement & p,
|
||||
double target_distance,
|
||||
PathT & shape_path)
|
||||
{
|
||||
double new_x = 0.0;
|
||||
double new_y = 0.0;
|
||||
double old_x = 0.0;
|
||||
double old_y = 0.0;
|
||||
double next_char_x = 0.0;
|
||||
double next_char_y = 0.0;
|
||||
|
||||
double angle = 0.0;
|
||||
int orientation = 0;
|
||||
double displacement = boost::tuples::get<1>(p.displacement_); // displace by dy
|
||||
|
||||
std::auto_ptr<placement_element> current_placement(new placement_element);
|
||||
|
||||
double x = 0.0;
|
||||
double y = 0.0;
|
||||
|
||||
double distance = 0.0;
|
||||
|
||||
std::pair<double, double> string_dimensions = p.info.get_dimensions();
|
||||
double string_height = string_dimensions.second;
|
||||
|
||||
// find the segment that our text should start on
|
||||
shape_path.rewind(0);
|
||||
|
||||
unsigned cmd;
|
||||
bool first = true;
|
||||
while (!agg::is_stop(cmd = shape_path.vertex(&new_x,&new_y)))
|
||||
{
|
||||
if (first || agg::is_move_to(cmd))
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
double dx = new_x - old_x;
|
||||
double dy = new_y - old_y;
|
||||
double segment_length = sqrt(dx*dx + dy*dy);
|
||||
distance += segment_length;
|
||||
if (distance > target_distance)
|
||||
{
|
||||
current_placement->starting_x = new_x - dx*(distance - target_distance)/segment_length;
|
||||
current_placement->starting_y = new_y - dy*(distance - target_distance)/segment_length;
|
||||
|
||||
// angle text starts at and orientation
|
||||
angle = atan2(-dy, dx);
|
||||
orientation = (angle > 0.55*M_PI || angle < -0.45*M_PI) ? -1 : 1;
|
||||
|
||||
distance -= target_distance;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
old_x = new_x;
|
||||
old_y = new_y;
|
||||
}
|
||||
|
||||
// now find the placement of each character starting from our initial segment
|
||||
// determined above
|
||||
double last_angle = angle;
|
||||
for (unsigned i = 0; i < p.info.num_characters(); ++i)
|
||||
{
|
||||
character_info ci;
|
||||
unsigned c;
|
||||
|
||||
// grab the next character according to the orientation
|
||||
ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1);
|
||||
c = ci.character;
|
||||
|
||||
double angle_delta = 0;
|
||||
|
||||
// if the distance remaining in this segment is less than the character width
|
||||
// move to the next segment
|
||||
if (distance <= ci.width)
|
||||
{
|
||||
last_angle = angle;
|
||||
while (distance <= ci.width)
|
||||
{
|
||||
double dx, dy;
|
||||
old_x = new_x;
|
||||
old_y = new_y;
|
||||
|
||||
if (agg::is_stop(shape_path.vertex(&new_x,&new_y)))
|
||||
return false;
|
||||
dx = new_x - old_x;
|
||||
dy = new_y - old_y;
|
||||
|
||||
angle = atan2(-dy, dx );
|
||||
distance += sqrt(dx*dx+dy*dy);
|
||||
}
|
||||
// since our rendering angle has changed then check against our
|
||||
// max allowable angle change.
|
||||
angle_delta = last_angle - angle;
|
||||
// normalise between -180 and 180
|
||||
while (angle_delta > M_PI)
|
||||
angle_delta -= 2*M_PI;
|
||||
while (angle_delta < -M_PI)
|
||||
angle_delta += 2*M_PI;
|
||||
if (p.max_char_angle_delta > 0 && fabs(angle_delta) > p.max_char_angle_delta*(M_PI/180))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Envelope<double> e;
|
||||
if (p.has_dimensions)
|
||||
{
|
||||
e.init(x, y, x + p.dimensions.first, y + p.dimensions.second);
|
||||
}
|
||||
|
||||
double render_angle = angle;
|
||||
|
||||
x = new_x - (distance)*cos(angle);
|
||||
y = new_y + (distance)*sin(angle);
|
||||
//Center the text on the line, unless displacement != 0
|
||||
if (displacement == 0.0) {
|
||||
x -= (((double)string_height/2.0) - 1.0)*cos(render_angle+M_PI/2);
|
||||
y += (((double)string_height/2.0) - 1.0)*sin(render_angle+M_PI/2);
|
||||
} else if (displacement*orientation > 0.0) {
|
||||
x -= ((fabs(displacement) - (double)string_height) + 1.0)*cos(render_angle+M_PI/2);
|
||||
y += ((fabs(displacement) - (double)string_height) + 1.0)*sin(render_angle+M_PI/2);
|
||||
} else { // displacement < 0
|
||||
x -= ((fabs(displacement) + (double)string_height) - 1.0)*cos(render_angle+M_PI/2);
|
||||
y += ((fabs(displacement) + (double)string_height) - 1.0)*sin(render_angle+M_PI/2);
|
||||
}
|
||||
distance -= ci.width;
|
||||
next_char_x = ci.width*cos(render_angle);
|
||||
next_char_y = ci.width*sin(render_angle);
|
||||
|
||||
double render_x = x;
|
||||
double render_y = y;
|
||||
|
||||
if (!p.has_dimensions)
|
||||
{
|
||||
// put four corners of the letter into envelope
|
||||
e.init(render_x, render_y, render_x + ci.width*cos(render_angle), render_y - ci.width*sin(render_angle));
|
||||
e.expand_to_include(render_x - ci.height*sin(render_angle), render_y - ci.height*cos(render_angle));
|
||||
e.expand_to_include(render_x + (ci.width*cos(render_angle) - ci.height*sin(render_angle)),
|
||||
render_y - (ci.width*sin(render_angle) + ci.height*cos(render_angle)));
|
||||
}
|
||||
|
||||
if (!dimensions_.intersects(e) ||
|
||||
!detector_.has_placement(e, p.info.get_string(), p.minimum_distance))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p.avoid_edges && !dimensions_.contains(e))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p.envelopes.push(e);
|
||||
|
||||
if (orientation < 0)
|
||||
{
|
||||
// rotate in place
|
||||
render_x += ci.width*cos(render_angle) - (string_height-2)*sin(render_angle);
|
||||
render_y -= ci.width*sin(render_angle) + (string_height-2)*cos(render_angle);
|
||||
render_angle += M_PI;
|
||||
}
|
||||
|
||||
|
||||
current_placement->add_node(c,render_x - current_placement->starting_x,
|
||||
-render_y + current_placement->starting_y,
|
||||
render_angle);
|
||||
x += next_char_x;
|
||||
y -= next_char_y;
|
||||
}
|
||||
|
||||
p.placements.push_back(current_placement.release());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename DetectorT>
|
||||
template <typename PathT>
|
||||
bool placement_finder<DetectorT>::build_path_horizontal(placement & p, double target_distance, PathT & shape_path)
|
||||
{
|
||||
double x, y;
|
||||
std::auto_ptr<placement_element> current_placement(new placement_element);
|
||||
|
||||
std::pair<double, double> string_dimensions = p.info.get_dimensions();
|
||||
double string_width = string_dimensions.first;
|
||||
double string_height = string_dimensions.second;
|
||||
|
||||
// check if we need to wrap the string
|
||||
double wrap_at = string_width + 1;
|
||||
if (p.wrap_width && string_width > p.wrap_width)
|
||||
{
|
||||
if (p.text_ratio)
|
||||
for (int i = 1; ((wrap_at = string_width/i)/(string_height*i)) > p.text_ratio && (string_width/i) > p.wrap_width; ++i);
|
||||
else
|
||||
wrap_at = p.wrap_width;
|
||||
}
|
||||
|
||||
// work out where our line breaks need to be
|
||||
std::vector<int> line_breaks;
|
||||
std::vector<double> line_widths;
|
||||
if (wrap_at < string_width && p.info.num_characters() > 0)
|
||||
{
|
||||
int line_count=0;
|
||||
int last_space = 0;
|
||||
string_width = 0;
|
||||
string_height = 0;
|
||||
double line_width = 0;
|
||||
double line_height = 0;
|
||||
double word_width = 0;
|
||||
double word_height = 0;
|
||||
for (unsigned int ii = 0; ii < p.info.num_characters(); ii++)
|
||||
{
|
||||
character_info ci;
|
||||
ci = p.info.at(ii);
|
||||
|
||||
unsigned c = ci.character;
|
||||
word_width += ci.width;
|
||||
word_height = word_height > ci.height ? word_height : ci.height;
|
||||
++line_count;
|
||||
|
||||
if (c == ' ')
|
||||
{
|
||||
last_space = ii;
|
||||
line_width += word_width;
|
||||
line_height = line_height > word_height ? line_height : word_height;
|
||||
word_width = 0;
|
||||
word_height = 0;
|
||||
}
|
||||
if (line_width > 0 && line_width > wrap_at)
|
||||
{
|
||||
string_width = string_width > line_width ? string_width : line_width;
|
||||
string_height += line_height;
|
||||
line_breaks.push_back(last_space);
|
||||
line_widths.push_back(line_width);
|
||||
ii = last_space;
|
||||
line_count = 0;
|
||||
line_width = 0;
|
||||
line_height = 0;
|
||||
word_width = 0;
|
||||
word_height = 0;
|
||||
}
|
||||
}
|
||||
line_width += word_width;
|
||||
string_width = string_width > line_width ? string_width : line_width;
|
||||
line_breaks.push_back(p.info.num_characters() + 1);
|
||||
line_widths.push_back(line_width);
|
||||
}
|
||||
if (line_breaks.size() == 0)
|
||||
{
|
||||
line_breaks.push_back(p.info.num_characters() + 1);
|
||||
line_widths.push_back(string_width);
|
||||
}
|
||||
|
||||
p.info.set_dimensions(string_width, string_height);
|
||||
|
||||
std::pair<double, double> starting_pos =
|
||||
get_position_at_distance<PathT>(target_distance,shape_path);
|
||||
current_placement->starting_x = starting_pos.first;
|
||||
current_placement->starting_y = starting_pos.second;
|
||||
|
||||
double line_height = 0;
|
||||
unsigned int line_number = 0;
|
||||
unsigned int index_to_wrap_at = line_breaks[line_number];
|
||||
double line_width = line_widths[line_number];
|
||||
|
||||
x = -line_width/2.0 - 1.0;
|
||||
y = -string_height/2.0 + 1.0;
|
||||
|
||||
for (unsigned i = 0; i < p.info.num_characters(); i++)
|
||||
{
|
||||
character_info ci;;
|
||||
ci = p.info.at(i);
|
||||
|
||||
unsigned c = ci.character;
|
||||
if (i == index_to_wrap_at)
|
||||
{
|
||||
index_to_wrap_at = line_breaks[++line_number];
|
||||
line_width = line_widths[line_number];
|
||||
y -= line_height;
|
||||
x = -line_width/2.0;
|
||||
line_height = 0;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_placement->add_node(c, x, y, 0.0);
|
||||
|
||||
Envelope<double> e;
|
||||
if (p.has_dimensions)
|
||||
{
|
||||
e.init(current_placement->starting_x - (p.dimensions.first/2.0),
|
||||
current_placement->starting_y - (p.dimensions.second/2.0),
|
||||
current_placement->starting_x + (p.dimensions.first/2.0),
|
||||
current_placement->starting_y + (p.dimensions.second/2.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
e.init(current_placement->starting_x + x,
|
||||
current_placement->starting_y - y,
|
||||
current_placement->starting_x + x + ci.width,
|
||||
current_placement->starting_y - y - ci.height);
|
||||
}
|
||||
|
||||
if (!dimensions_.intersects(e) ||
|
||||
!detector_.has_placement(e, p.info.get_string(), p.minimum_distance))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p.avoid_edges && !dimensions_.contains(e))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p.envelopes.push(e);
|
||||
}
|
||||
x += ci.width;
|
||||
line_height = line_height > ci.height ? line_height : ci.height;
|
||||
}
|
||||
p.placements.push_back(current_placement.release());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename DetectorT>
|
||||
void placement_finder<DetectorT>::clear()
|
||||
{
|
||||
|
@ -1181,7 +835,7 @@ namespace mapnik
|
|||
typedef label_collision_detector4 DetectorType;
|
||||
|
||||
template class placement_finder<DetectorType>;
|
||||
template void placement_finder<DetectorType>::find_placements<PathType> (placement&, PathType & );
|
||||
template void placement_finder<DetectorType>::find_line_placement<PathType> (placement&, PathType & );
|
||||
template void placement_finder<DetectorType>::find_point_placements<PathType> (placement&, PathType & );
|
||||
template void placement_finder<DetectorType>::find_line_placements<PathType> (placement&, PathType & );
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in a new issue