Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Computing a polygon that surrounds a multi-point line

Tags:

java

math

polygon

I am trying to compute a polygon that surrounds a line connecting multiple points (e.g. a GPX track). The image below shows an example with the track as red line and the desired polygon in blue.

Example of a track (red line) and a rough estimated polygon surrounding the track

As simplification the red points are denoted by x and y - not by latitude/longitude.

How do I compute such an environment (light blue polygon) if I only have the list of the three points specifying the path?

Partial solutions (e.g. for only two points) or hints about mathematical libraries (in Java) that provide algorithms for such a computation would also bring me one step forward.

Further assumptions:

  • The track is intersection free.

Update: Using the approach as presented by Rogach and xan I ran into some problems if the angle between the lines is smaller than 90 degree or larger than 270 degree: Example demonstrating the problem with the selected approach As you can see the polygon gets intersects itself which leads to a serious problem.

From my point of view using an java.awt.geom.Area is the better approach:

My solution (based on the code by Rogach):

For each line connecting two points of the track I compute a surrounding polygon. Afterwards I add (area union) the computed polygon to an Area which does all the necessary computation for me. As the Area strictly uses the "or" algorithm on adding new polygons I do not have to care about "self intersections" of the polygon as presented in the update above.

Area area = new Area();
for (int i = 1; i < points.size(); i++) {
    Point2D point1 = points.get(i - 1);
    Point2D point2 = points.get(i);

    Line2D.Double ln = new Line2D.Double(point1.getX(), point1.getY(), point2.getX(), point2.getY());
    double indent = 15.0; // distance from central line
    double length = ln.getP1().distance(ln.getP2());

    double dx_li = (ln.getX2() - ln.getX1()) / length * indent;
    double dy_li = (ln.getY2() - ln.getY1()) / length * indent;

    // moved p1 point
    double p1X = ln.getX1() - dx_li;
    double p1Y = ln.getY1() - dy_li;

    // line moved to the left
    double lX1 = ln.getX1() - dy_li;
    double lY1 = ln.getY1() + dx_li;
    double lX2 = ln.getX2() - dy_li;
    double lY2 = ln.getY2() + dx_li;

    // moved p2 point
    double p2X = ln.getX2() + dx_li;
    double p2Y = ln.getY2() + dy_li;

    // line moved to the right
    double rX1_ = ln.getX1() + dy_li;
    double rY1 = ln.getY1() - dx_li;
    double rX2 = ln.getX2() + dy_li;
    double rY2 = ln.getY2() - dx_li;

    Path2D p = new Path2D.Double();
    p.moveTo(lX1, lY1);
    p.lineTo(lX2, lY2);
    p.lineTo(p2X, p2Y);
    p.lineTo(rX2, rY2);
    p.lineTo(rX1_, rY1);
    p.lineTo(p1X, p1Y);
    p.lineTo(lX1, lY1);

    area.add(new Area(p));
}
like image 983
Robert Avatar asked Apr 24 '11 17:04

Robert


2 Answers

As I see, this problem is similar to polygon buffering problem.

I think following approach can help you:

  • For each segment of your track, find two lines - one to the left and one to the right.
  • Then, iterate for over your ofsetted lines, and resolve intersections. For example: http://img25.imageshack.us/img25/7660/temprhk.png
  • Add caps to ends, and you're done! :)

And some code:

Moving a line to the left:

Line2D l; 
double indent; // distance from central line
double dx = ln.getX2() - ln.getX1();
double dy = ln.getY2() - ln.getY1();
double length = ln.getP1().distance(ln.getP2());
double newX1 = l.getX1() - indent*(dy/length);
double newY1 = l.getY1() + indent*(dx/length);
double newX2 = l.getX2() - indent*(dy/length);
double newY2 = l.getY2() + indent*(dx/length);
Line2D leftLine = new Line2D.Double(newX1, newY1, newX2, newY2);

For moving it to the right, change "+" to "-" and vice versa in the last 4 lines of code.

About working with intersections - if two line segment intersect, you just output the intersection point. If they do not, then situation is a bit more complicated - you can, of course, still output the intersection, but in case of rapidly turning track, there will be strange outbursts. I inserted an arc segment in similar situation, but the code is to big and scattered, so I can't paste it here.
Or, you can do as you show on your picture - just connect end points.


And, by the way, if speed is not a big issue, you can use even better way - for each line of track, find left and right lines, add caps, pack it all into Path2D, then create Area from Path2D.
In such case, you can make this "line with caps" as intersection of three areas: rectangle, whose points are just end points of right and left line, and two circles with centers on original track segment ends.

When you compute Areas for all lines, just intersect them using Area add() method.

This approach deals with just any situations, even self-intersections and breaks in the track.

like image 118
Rogach Avatar answered Oct 15 '22 01:10

Rogach


See my answer to a similar question, "How to draw an outline around any line."

Same idea as Rogach provides here, but perhaps different drawings and explanations will help clarify it.

like image 41
xan Avatar answered Oct 15 '22 01:10

xan