Get distance along polyline from lat/lng point

2020-05-11 01:23发布

问题:

I'm rendering a polyline on a Google map with data that looks like this:

data = [
  {
    distance: 3.45,
    lat: 37.37112776376307,
    lng: -122.13909476995468,
  },
  ...
];

When hovering over the polyline, I can get the lat/lng coordinates and I want to get the distance along the line based on those coordinates.

I haven't found an easy way to do this with the Maps API. I want the distance from the start to an arbitrary point along the polyline; most answers I've found deal simply with the (shortest) distance between two points, not along a predefined route.

My other thought was to use D3 (which I'm using to draw some related charts) to bisect the data and get the distance. However, I can't figure out how to bisect based on two data points (lat+ lng).

Any advice on how to accomplish what I'm trying to do?

EDIT: In my data, distance is the cumulative distance traveled at the given coordinates.

回答1:

You can do this (and a lot more) using the Turf library.

With Turf you could break your problem into a few smaller problems —

  • Find the exact place on the line nearest the cursor
  • Create a polyline from the start to that point
  • Measuring the length of that polyline.

For example, here is a route through several cities from Chicago to Portland, Maine and we want to figure out how far it is to Schenectady which is near the line between Syracuse and Boston but isn't on our route:

const turf = require('@turf/helpers')
const lineSlice = require('@turf/line-slice')
const pointOnLine = require('@turf/point-on-line')
const lineDistance = require('@turf/line-distance')

var route = turf.lineString([
[-87.627453, 41.853555], // Chicago
[-87.389126, 41.587682], // Gary IN
[-81.674692, 41.420385], // Clevland
[-76.077655, 42.988040], // Syracuse
[-71.066601, 42.323782], // Boston
[-70.277662, 43.627106]  // Portland, ME
]);

var Schenectady = turf.point([-73.899115, 42.815089]); //Schenectady

var snapped = pointOnLine(route, Schenectady, 'miles');
var sliced = lineSlice(route.geometry.coordinates[0], snapped, route);
var length = lineDistance(sliced, 'miles');

console.log(length) // => 736.8317888716095

This agrees pretty closely with Google's 810 mile route that is not all straight lines.



回答2:

I guess I was hoping the Maps API would have a method along the lines of

polyLine.computeDistanceBetween(point1, point2);

but that doesn't seem to be the case.

I ended up breaking down the problem similar to how @Mark_M describes above:

  1. Find the point on the polyline closest to the hovered point

    function getIndexOfNearestPoint(path, hoveredPoint) {
      let index = -1;
      let minDistance = 1000;
    
      path.forEach((pointOnPath, idx) => {
        const dist = window.google.maps.geometry.spherical.computeDistanceBetween(
          hoveredPoint,
          pointOnPath
        );
    
        if (dist < minDistance){
          minDistance = dist;
          index = idx;
        }
      });
    
      return index;
    }
    
  2. Calculate the distance between 2 points on the polyline

    function getDistanceAlongPath(path, startIndex, endIndex) {
      let distance = 0;
      for (let ii = startIndex; ii < endIndex; ii++) {
        distance += window.google.maps.geometry.spherical.computeDistanceBetween(
          path[ii],
          path[ii + 1]
        );
      }
      return distance;
    }
    

Putting it together to find the cumulative distance from the start of the polyline to the hovered point:

polyline.addListener('mousemove', e => {
  const path = polyline.getPath().getArray();
  const hoveredPoint = e.latLng;
  const startIndex = 0;

  const endIndex = getIndexOfNearestPoint(path, hoveredPoint);
  const distance = getDistanceAlongPath(path, startIndex, endIndex);
});


回答3:

If your data set is just a list of lat/lng, you could use this answer to calculate the gap between each point according to the Haversine formula (shortest distance over the earth's surface).