Creating an abitrary, curved Well Known Text LineS

2019-05-28 10:08发布

问题:

I am dynamically generating a WKT LineString between points in a map layer being generated for display in OpenLayers. I'd like to make the lines between the points curved, and I'd like to be able to dynamically change the curvature based on various input variables.

This is for a network monitoring app, and we'd like the curvature to based on delay times between the points (not the raw delay itself, but the deviation from "normal" values during a given time period).

While some GIS app and databases support a CircularString extension to WKT, OpenLayers doesn't know anything 'bout that.

So I'll need to generate a curved line out of line segments:

Right now, the line strings are simply:

LINESTRING(hop1_long hop1_lat, hop2_long hop2_lat)

The only way I can determine to make the linesegment "curved" is to insert intermediary points:

LINESTRING(hop1_long hop1_lat, long1 lat1, long2 lat2, ..., hop2_long hop2_lat)

This should be perfectly adequate for our application, but I don't know how to generate the intermediary points!

I assume there are well-known methods/algorithms for generating a "curved" line out of line segments in a 2d plane. Does anyone have any ideas as to how to accomplish this, or books/articles/online resources that could be helpful?

UPDATE (2010-08-13):

Bezier curves were the ticket, and implementing the basic Bezier algorithm was pretty easy after reading up on it. But I had to write some code to generate the control points. Here's the PHP code I came up with. This assumes a "Vector2d" class with x and y members.

function get_control_points($to, $from, $mag_scale, $angle) {
  $dirX = $to->x - $from->x;
  $dirY = $to->y - $from->y;

  $mag = sqrt(($dirX * $dirX) + ($dirY * $dirY));
  if (!$mag) {
    return array($to, $from);
  }

  $length = $mag * $mag_scale;

  $dirX = $dirX / $mag;
  $dirY = $dirY / $mag;

  $sin = sin($angle);
  $cos = cos($angle);

  $rotX = $cos * $dirX - $sin * $dirY;
  $rotY = $sin * $dirX + $cos * $dirY;
  $rotNegX = $cos * -$dirX - $sin * $dirY;
  $rotNegY = $sin * $dirX - $cos * $dirY;

  // Flip control points for "backwards" curves
  if ($dirX x;
    $y1 = -$rotNegY * $length + $from->y;
    $x2 = -$rotX * $length + $to->x;
    $y2 = -$rotY * $length + $to->y;
  }
  // Or generate "normal" control points
  else {
    $x1 = $rotX * $length + $from->x;
    $y1 = $rotY * $length + $from->y;
    $x2 = $rotNegX * $length + $to->x;
    $y2 = $rotNegY * $length + $to->y;
  }

  return array(new Vector2d($x2, $y2), new Vector2d($x1, $y1));
}

回答1:

If you'd like to generate a series of line segments that make a curve, start by looking at curves that are easily parameterized by a single variable. For example, a circle centered at (cx,cy) with radius r is parameterized by the following expression:

(x,y) = (cx,cy)+r*(cos(t), sin(t)),

where t runs from 0 to 2π.

So you could create a semi-circular shape by, say, evaluating the circle at values tk=π×k/30, where k runs from 0 to 30. This would give you a semi-circular arc made out of 30 line segments.

If you want more general curves, check out Bezier curves. They are parameterized by a value t that can be evaluated at uniform intervals between 0 and 1.