+ add VertexSource based implementations of label position algos
This commit is contained in:
parent
710b7eb04b
commit
f6fa57da26
1 changed files with 201 additions and 0 deletions
|
@ -32,6 +32,7 @@
|
|||
|
||||
// stl
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
@ -216,6 +217,206 @@ struct filter_at_point
|
|||
return extent.contains(pt_);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
template <typename PathType>
|
||||
double path_length(PathType & path)
|
||||
{
|
||||
double x0,y0,x1,y1;
|
||||
path.rewind(0);
|
||||
unsigned command = path.vertex(&x0,&y0);
|
||||
if (command == SEG_END) return 0;
|
||||
double length = 0;
|
||||
while (SEG_END != (command = path.vertex(&x1, &y1)))
|
||||
{
|
||||
length += distance(x0,y0,x1,y1);
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
template <typename PathType>
|
||||
bool middle_point(PathType & path, double & x, double & y)
|
||||
{
|
||||
double x0,y0,x1,y1;
|
||||
double mid_length = 0.5 * path_length(path);
|
||||
path.rewind(0);
|
||||
unsigned command = path.vertex(&x0,&y0);
|
||||
if (command == SEG_END) return false;
|
||||
double dist = 0.0;
|
||||
while (SEG_END != (command = path.vertex(&x1, &y1)))
|
||||
{
|
||||
double seg_length = distance(x0, y0, x1, y1);
|
||||
|
||||
if ( dist + seg_length >= mid_length)
|
||||
{
|
||||
double r = (mid_length - dist)/seg_length;
|
||||
x = x0 + (x1 - x0) * r;
|
||||
y = y0 + (y1 - y0) * r;
|
||||
break;
|
||||
}
|
||||
dist += seg_length;
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename PathType>
|
||||
bool centroid(PathType & path, double & x, double & y)
|
||||
{
|
||||
double x0;
|
||||
double y0;
|
||||
double x1;
|
||||
double y1;
|
||||
double start_x;
|
||||
double start_y;
|
||||
|
||||
path.rewind(0);
|
||||
unsigned command = path.vertex(&x0, &y0);
|
||||
if (command == SEG_END) return false;
|
||||
|
||||
start_x = x0;
|
||||
start_y = y0;
|
||||
|
||||
double atmp = 0;
|
||||
double xtmp = 0;
|
||||
double ytmp = 0;
|
||||
|
||||
while (SEG_END != (command = path.vertex(&x1, &y1)))
|
||||
{
|
||||
double dx0 = x0 - start_x;
|
||||
double dy0 = y0 - start_y;
|
||||
double dx1 = x1 - start_x;
|
||||
double dy1 = y1 - start_y;
|
||||
|
||||
double ai = dx0 * dy1 - dx1 * dy0;
|
||||
atmp += ai;
|
||||
xtmp += (dx1 + dx0) * ai;
|
||||
ytmp += (dy1 + dy0) * ai;
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
}
|
||||
|
||||
if (atmp != 0)
|
||||
{
|
||||
x = (xtmp/(3*atmp)) + start_x;
|
||||
y = (ytmp/(3*atmp)) + start_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = x0;
|
||||
y = y0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename PathType>
|
||||
bool hit_test(PathType & path, double x, double y, double tol)
|
||||
{
|
||||
bool inside=false;
|
||||
double x0, y0, x1, y1;
|
||||
path.rewind(0);
|
||||
unsigned command = path.vertex(&x0, &y0);
|
||||
if (command == SEG_END) return false;
|
||||
unsigned count = 0;
|
||||
while (SEG_END != (command = path.vertex(&x1, &y1)))
|
||||
{
|
||||
++count;
|
||||
if (command == SEG_MOVETO)
|
||||
{
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
continue;
|
||||
}
|
||||
if ((((y1 <= y) && (y < y0)) ||
|
||||
((y0 <= y) && (y < y1))) &&
|
||||
(x < (x0 - x1) * (y - y1)/ (y0 - y1) + x1))
|
||||
inside=!inside;
|
||||
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
}
|
||||
|
||||
if (count == 0) // one vertex
|
||||
{
|
||||
return distance(x, y, x0, y0) <= fabs(tol);
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
|
||||
template <typename PathType>
|
||||
void label_interior_position(PathType & path, double & x, double & y)
|
||||
{
|
||||
// start with the centroid
|
||||
centroid(path, x,y);
|
||||
|
||||
// if we are not a polygon, or the default is within the polygon we are done
|
||||
if (hit_test(path,x,y,0.001))
|
||||
return;
|
||||
|
||||
// otherwise we find a horizontal line across the polygon and then return the
|
||||
// center of the widest intersection between the polygon and the line.
|
||||
|
||||
std::vector<double> intersections; // only need to store the X as we know the y
|
||||
|
||||
double x0;
|
||||
double y0;
|
||||
path.rewind(0);
|
||||
unsigned command = path.vertex(&x0, &y0);
|
||||
double x1,y1;
|
||||
while (SEG_END != (command = path.vertex(&x1, &y1)))
|
||||
{
|
||||
if (command != SEG_MOVETO)
|
||||
{
|
||||
// if the segments overlap
|
||||
if (y0==y1)
|
||||
{
|
||||
if (y0==y)
|
||||
{
|
||||
double xi = (x0+x1)/2.0;
|
||||
intersections.push_back(xi);
|
||||
}
|
||||
}
|
||||
// if the path segment crosses the bisector
|
||||
else if ((y0 <= y && y1 >= y) ||
|
||||
(y0 >= y && y1 <= y))
|
||||
{
|
||||
// then calculate the intersection
|
||||
double xi = x0;
|
||||
if (x0 != x1)
|
||||
{
|
||||
double m = (y1-y0)/(x1-x0);
|
||||
double c = y0 - m*x0;
|
||||
xi = (y-c)/m;
|
||||
}
|
||||
|
||||
intersections.push_back(xi);
|
||||
}
|
||||
}
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
}
|
||||
// no intersections we just return the default
|
||||
if (intersections.empty())
|
||||
return;
|
||||
x0=intersections[0];
|
||||
double max_width = 0;
|
||||
for (unsigned ii = 1; ii < intersections.size(); ++ii)
|
||||
{
|
||||
double x1=intersections[ii];
|
||||
double xc=(x0+x1)/2.0;
|
||||
double width = std::fabs(x1-x0);
|
||||
if (width > max_width && hit_test(path,xc,y,0))
|
||||
{
|
||||
x=xc;
|
||||
max_width = width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // MAPNIK_GEOM_UTIL_HPP
|
||||
|
|
Loading…
Reference in a new issue