gis-tools-ts
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
89 lines • 3.71 kB
JavaScript
import { llGetDistance, pointDistance, radToDeg } from '../../index.js';
/**
* Check to see how far away the point is from the line
* @param line - the line to check against
* @param point - the point to check against the line
* @param method - the method to use, either 'euclidean' or 'haversine'. Defaults to 'euclidean'
* @returns The shortest distance between the point and a line. Returns -1 if line is empty
*/
export function pointToLineDistance(line, point, method = 'euclidean') {
const haversine = method === 'haversine';
const vectorLines = 'geometry' in line
? line.geometry.coordinates
: 'coordinates' in line
? line.coordinates
: line;
let closestIndex;
for (let i = 0; i < vectorLines.length; i++) {
// get the distance between the point and the line's point at index
const dist = haversine
? radToDeg(llGetDistance(point, vectorLines[i]))
: pointDistance(point, vectorLines[i]);
if (dist === 0) {
return 0;
}
if (closestIndex === undefined || dist < closestIndex.dist) {
closestIndex = { index: i, dist };
}
}
// If there is no closest point, return -1
if (closestIndex === undefined) {
return -1;
}
// If line is a single point, return distance to that point
if (vectorLines.length === 1) {
return closestIndex.dist;
}
const curr = vectorLines[closestIndex.index];
const prev = vectorLines[closestIndex.index - 1];
const next = vectorLines[closestIndex.index + 1];
// If the point is the start or end of the line, return distance to that point and next/prev
if (closestIndex.index === 0) {
return distancePointToSegment(curr, next, point, method);
}
if (closestIndex.index === vectorLines.length - 1) {
return distancePointToSegment(curr, prev, point, method);
}
// Check against both sides of the line's closest point
const dist1 = distancePointToSegment(curr, prev, point, method);
const dist2 = distancePointToSegment(curr, next, point, method);
return dist1 < dist2 ? dist1 : dist2;
}
/**
* Get the distance between a point and a segment
* @param a - the segment start point
* @param b - the segment end point
* @param p - the point to check
* @param method - the method to use, either 'euclidean' or 'haversine'. Defaults to 'euclidean'
* @returns - the distance
*/
function distancePointToSegment(a, b, p, method = 'euclidean') {
const { max, min, hypot } = Math;
if (method === 'haversine') {
// approximate by sampling along the great-circle segment
// project p onto AB using Euclidean math in lat/lon degrees
// but compute distances with llGetDistance
const abx = b.x - a.x;
const aby = b.y - a.y;
const apx = p.x - a.x;
const apy = p.y - a.y;
const abLenSq = abx * abx + aby * aby;
if (abLenSq === 0)
return radToDeg(llGetDistance(p, a));
let t = (apx * abx + apy * aby) / abLenSq;
t = max(0, min(1, t));
const proj = { x: a.x + t * abx, y: a.y + t * aby };
return radToDeg(llGetDistance(p, proj));
}
// Euclidean fallback
const ab = { x: b.x - a.x, y: b.y - a.y };
const ap = { x: p.x - a.x, y: p.y - a.y };
const abLenSq = ab.x * ab.x + ab.y * ab.y;
if (abLenSq === 0)
return hypot(p.x - a.x, p.y - a.y);
let t = (ap.x * ab.x + ap.y * ab.y) / abLenSq;
t = max(0, min(1, t));
const closest = { x: a.x + t * ab.x, y: a.y + t * ab.y };
return hypot(p.x - closest.x, p.y - closest.y);
}
//# sourceMappingURL=pointToLineDistance.js.map