Search code examples
javascriptgeojson

Javascript - how to calculate midpoint of linestring?


With given GeoJSON linestring:

{
  "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
          ]
        ]
      }
    }
  ]
}

I'd like to find exact middle point. I don't mean the middle element of a given array but calculating a midpoint which is exactly placed at the middle of line. So, if a line is 30km long in total then midpoint would be placed at 15km.

I tried searching npm for something like that but couldn't find. The only library which is close gives ability to place n points on line, and then I can get the middle one. But it's pretty bad in my case because precision is not that good.

Perfect option would be implementation of this http://postgis.net/docs/manual-1.5/ST_Line_Interpolate_Point.html in JavaScript.

How can I achieve it?


Solution

  • Here's a working CodePen

    the following code will return the point at a specific distance.

    for this specific case the midpoint distance is total length / 2

    call the main function like this, assuming our linestring array is stored in 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 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);
    };
    

    ##Explanation

    1. we find the total length of the linestring:

    • function lineLen() calculates the length of a single line.
    • function totalLen() loops through the lines and calculates the total length. enter image description here

    *source and credits here (Igor Šarčević)

    2. the distance of midpoint is total length / 2:

    • now that we have the wanted distance we loop through lines and check distance of this line-startpoint and this line-endpoint and see if midpoint distance is between them.

    • once we have the single line that we know our midpoint is on we calculate it's distance from this line-startpoint (total length - line-startpoint distance)

    • now we use this formula (function pntOnLine()) to find xt,yt (wanted midpoint)

    enter image description here

    *credits to Sen Jacob.


    ##Visualization I included an HTML5 canvas to draw (visualize) the linestring and midpoint, you don't have to include them. but they're useful if you want to test the code and see the result live

    // 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();
        }
      });
    }