簡體   English   中英

Javascript - 如何計算線串的中點?

[英]Javascript - how to calculate midpoint of linestring?

使用給定的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

我怎樣才能實現它?

這是一個有效的CodePen

以下代碼將返回特定距離處的點。

對於這種特定情況,中點距離是總長度/ 2

假設我們的線串數組存儲在points ,就像這樣調用main函數

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()遍歷行並計算總長度。 在此輸入圖像描述

*來源和信用在這里伊戈爾Šarčević

2.中點距離是總長度/ 2:

  • 現在我們有了所需的距離,我們循環通過線並檢查這個線 - 起始點這個線 - 端點的 距離 ,看看它們之間是否有中點距離
  • 一旦我們得到了我們知道我們的中點在的單線,我們計算它與該線 - 起點的距離(總長度 - 線 - 起點距離)

  • 現在我們使用這個公式 (函數pntOnLine() )來找到xtyt (想要的中點)

在此輸入圖像描述

*積分給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();
    }
  });
}

所以我假設你有一個坐標列表,它描述了一個球體上有航點的線。 每個坐標都有緯度和經度。 這種實現貫穿直到它達到距離的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)) 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM