Javascript - 如何计算线串的中点?

时间:2017-02-21 15:52:56

标签: javascript geojson

使用给定的GeoJSON线串:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [
            2.6806640625,
            46.437856895024204
          ],
          [
            4.7021484375,
            50.20503326494332
          ],
          [
            13.271484375,
            47.010225655683485
          ],
          [
            17.2265625,
            52.855864177853974
          ]
        ]
      }
    }
  ]
}

我想找到确切的中间点。我并不是指给定数组的中间元素,而是计算一个恰好位于线中间的中点。因此,如果一条线总长30公里,那么中点将被放置在15公里处。

我试着搜索npm这样的东西,但无法找到。唯一靠近的图书馆能够在线上放置n个点,然后我可以得到中间点。但在我的情况下它非常糟糕,因为精确度不是那么好。

完美选项是在JavaScript中实现此http://postgis.net/docs/manual-1.5/ST_Line_Interpolate_Point.html

我怎样才能实现它?

2 个答案:

答案 0 :(得分:6)

Here's a working CodePen

以下代码将返回特定距离的点。

对于这种特定情况,中点距离是总长度/ 2

调用main函数,假设我们的linestring数组存储在points

var totalLength = totalLen(points);
var midDistance = totalLength / 2;
var midPoint = getPointByDistance(points, midDistance)

的Javascript

myData = {
  "type": "FeatureCollection",
  "features": [{
    "type": "Feature",
    "properties": {},
    "geometry": {
      "type": "LineString",
      "coordinates": [
        [
           2.6806640625,
          46.437856895024204
        ],
        [
          4.7021484375,
          50.20503326494332
        ],
        [
          13.271484375,
          47.010225655683485
        ],
        [
          17.2265625,
          52.855864177853974
        ]
      ]
    }
  }]
}
var points = myData.features[0].geometry.coordinates
var totalLength = totalLen(points);
var midDistance = totalLength / 2;
var midPoint = getPointByDistance(points, midDistance)
alert ("midPoint = " + midPoint[0] + ", " + midPoint[1])
// main function
function getPointByDistance(pnts, distance) {
  var tl = totalLen(pnts);
  var cl = 0;
  var ol;
  var result;
  pnts.forEach(function(point, i, points) {
    ol = cl;
    cl += i ? lineLen([points[i-1], point]) : 0;
    if (distance <= cl && distance > ol){
      var dd = distance - ol;
      result = pntOnLine([points[i-1], point], dd);
    }
  });
  return result
};
// returns a point on a single line (two points) using distance // line=[[x0,y0],[x1,y1]]
function pntOnLine(line, distance) {
  t = distance / lineLen(line)
  xt = (1 - t) * line[0][0] + (t * line[1][0])
  yt = (1 - t) * line[0][1] + (t * line[1][1])
  return [xt, yt]
};
// returns the total length of a linestring (multiple points) // pnts=[[x0,y0],[x1,y1],[x2,y2],...]
function totalLen(pnts) {
  var tl = 0;
  pnts.forEach(function(point, i, points) {
    tl += i ? lineLen([points[i - 1], point]) : 0;
  });
  return tl;
};
// returns the length of a line (two points) // line=[[x0,y0],[x1,y1]]
function lineLen(line) {
  var xd = line[0][0] - line[1][0];
  var yd = line[0][1] - line[1][1];
  return Math.sqrt(xd * xd + yd * yd);
};

说明

<强> 1。我们找到了线串的总长度:

  • function lineLen()计算单行的长度。
  • function totalLen()遍历这些行并计算总长度。 enter image description here

*来源和信用hereIgorŠarčević

<强> 2。中点的距离是总长度/ 2:

  • 现在我们有了想要的距离,我们遍历线并检查此线 - 起点此线 - 终点的距离,看看中点距离< / em>介于他们之间。
  • 一旦我们知道我们的中点在我们的单线,我们计算它与线 - 起点的距离(总长度 - 线 - 起点距离)

  • 现在我们使用this formula(函数pntOnLine())查找xtyt(想要中点)

enter image description here

*信用 Sen Jacob

可视化

我包含了一个HTML5 canvas来绘制(可视化)线串和中点,您不必包含它们。但如果你想测试代码并实时查看结果,它们就很有用

// Just for visualizing on canvas (not needed)
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext('2d');
drawLine(points);
//
ctx.beginPath();
ctx.arc(midPoint[0], midPoint[1], 2, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = "red";
ctx.fill();
//
function drawLine(pnts) {
  pnts.forEach(function(point, i, points) {
    if (i === 0) {
      ctx.beginPath();
      ctx.moveTo(point[0], point[1]);
    } else {
      ctx.lineTo(point[0], point[1]);
    }
    if (i === points.length - 1) {
      ctx.stroke();
    }
  });
}

答案 1 :(得分:0)

所以我假设你有一个坐标列表,它描述了一个球体上有航点的线。每个坐标都有纬度和经度。 这种实现贯穿直到它达到距离的50%为止:

var coordinates = [
  [
    2.6806640625,
    46.437856895024204
  ],
  [
    4.7021484375,
    50.20503326494332
  ],
  [
    13.271484375,
    47.010225655683485
  ],
  [
    17.2265625,
    52.855864177853974
  ]
];

// From http://stackoverflow.com/a/18883823/5710637
function calcCrow(lat1, lon1, lat2, lon2) 
{
  var R = 6371; // km
  var dLat = toRad(lat2-lat1);
  var dLon = toRad(lon2-lon1);
  var lat1 = toRad(lat1);
  var lat2 = toRad(lat2);

  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c;
  return d;
}

// Converts numeric degrees to radians
// From http://stackoverflow.com/a/18883823/5710637
function toRad(Value) 
{
    return Value * Math.PI / 180;
}

// Returns a point from a line
// Should use halversine but i'm too bad at math
function getPoint(factor, lat1, lon1, lat2, lon2)
{
  while (lat1 < 0) {lat1 += 360}
  while (lat2 < lat1) {lat2 += 360}
  latPoint = lat1 + factor * (lat2 - lat1)
  latPoint = ((latPoint + 180) % 360) - 180
  otherLat = latPoint < 0 ? latPoint + 180 : latPoint - 180
  latPoint = Math.abs(latPoint) < Math.abs(otherLat) ? latPoint : otherLat
  lonPoint = lon1 + factor * (lon2 - lon1)
  return [latPoint, lonPoint]
}

function getHalfDistance(coordinates)
{
  // Calculate complete distance
  var totalDistance = 0;
  for (var i = 1; i < coordinates.length; i++) {
    totalDistance += calcCrow(coordinates[i-1][0], coordinates[i-1][1], coordinates[i][0], coordinates[i][1])
  }

  // Find the 50%
  var target = 0.5
  var currentDistance = 0
  for (var i = 1; i < coordinates.length; i++) {
    var thisDistance = calcCrow(coordinates[i-1][0], coordinates[i-1][1], coordinates[i][0], coordinates[i][1]);
    if (target * totalDistance < currentDistance + thisDistance) {
       var midDistance = target * totalDistance - currentDistance;
       var factor = midDistance / thisDistance;
       return getPoint(factor, coordinates[i-1][0], coordinates[i-1][1], coordinates[i][0], coordinates[i][1]);
    }
    currentDistance += thisDistance
  }
}

console.log(getHalfDistance(coordinates))