javascript计算一个坐标点到一个区域的最短距离

// 判断坐标点是否存在区域内
function isPointInArea(point, area) {
    let x = point[0];
    let y = point[1];
    let isInside = false;

    for (let i = 0, j = area.length - 1; i < area.length; j = i++) {
        let xi = area[i][0];
        let yi = area[i][1];
        let xj = area[j][0];
        let yj = area[j][1];

        let intersect = ((yi > y) != (yj > y)) &&
            (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
        if (intersect) isInside = !isInside;
    }

    return isInside;
}

// 获取最短距离的端点坐标
function minDistanceLineSegmentToArea(point, area) {
    let minDistance = Infinity;
    let isProjectionPoint,startPoint1,endPoint1;

    for (let i = 0; i < area.length; i++) {
        let startPoint = area[i];
        let endPoint = area[(i + 1) % area.length]; // 下一个顶点,形成边
        
        // [是否投影点,点到线段的距离,线段的起始点(若不是投影点则返回 点到线段最短端点的端点坐标)]
        const [isProjectionPoint1,distance,startPoint2, endPoint2] = pointToLineDistance(point, startPoint, endPoint);
        console.log(point, startPoint, endPoint,distance);
        if (distance < minDistance) {
            minDistance = distance;
            isProjectionPoint= isProjectionPoint1
            startPoint1= startPoint2
            endPoint1= endPoint2
        }
    }

    // 返回[是否投影点,点到线段的距离,线段的起始点(若不是投影点则返回 点到线段最短端点的端点坐标)]
    return [isProjectionPoint,startPoint1,endPoint1];
}

// 计算点到线段之间的距离
function pointToLineDistance(point, segmentStart, segmentEnd) {
    let isProjectionPoint = false   // 是否投影点
    // 计算线段的方向向量
    let dx = segmentEnd[0] - segmentStart[0];
    let dy = segmentEnd[1] - segmentStart[1];

    // 计算点到线段的投影点
    let u = ((point[0] - segmentStart[0]) * dx + (point[1] - segmentStart[1]) * dy) / (dx * dx + dy * dy);
   
    let px, py;
    if (u <= 0) {
        px = segmentStart[0];
        py = segmentStart[1];
    } else if (u >= 1) {
        px = segmentEnd[0];
        py = segmentEnd[1];
    } else {
        px = segmentStart[0] + u * dx;
        py = segmentStart[1] + u * dy;
    }

    // 计算最短距离
    let distanceToSegment;

    if (u <= 0 || u >= 1) {
        // 点在线段外部
        let distanceToStart = Math.sqrt(Math.pow(point[0] - segmentStart[0], 2) + Math.pow(point[1] - segmentStart[1], 2));
        let distanceToEnd = Math.sqrt(Math.pow(point[0] - segmentEnd[0], 2) + Math.pow(point[1] - segmentEnd[1], 2));
        distanceToSegment = Math.min(distanceToStart, distanceToEnd);
        
        segmentStart = distanceToSegment ===  distanceToStart ? segmentStart:segmentEnd
        segmentEnd = distanceToSegment ===  distanceToStart ? segmentStart:segmentEnd
    } else {
        isProjectionPoint = true
        // 点在线段内部
        distanceToSegment = Math.sqrt(Math.pow(point[0] - px, 2) + Math.pow(point[1] - py, 2));
         console.log('u:',u);
        console.log('---:',point, segmentStart, segmentEnd,distanceToSegment);
    }

    // 返回  [是否投影点,点到线段的距离,线段的起始点(若不是投影点则返回 点到线段最短端点的端点坐标)]
    return [isProjectionPoint,distanceToSegment,segmentStart, segmentEnd];

}

// 计算投影点的坐标
function calculateProjectionPoint(point, segmentStart, segmentEnd) {
    let dx = segmentEnd[0] - segmentStart[0];
    let dy = segmentEnd[1] - segmentStart[1];

    let u = ((point[0] - segmentStart[0]) * dx + (point[1] - segmentStart[1]) * dy) / (dx * dx + dy * dy);
    let px = segmentStart[0] + u * dx;
    let py = segmentStart[1] + u * dy;

    return [px, py];
}



function deg2rad(deg) {
  return deg * (Math.PI / 180);
}

function getDistanceFromLatLonInKm( lon1,lat1,  lon2,lat2) {
  const R = 6371; // 地球半径,单位为千米
  const dLat = deg2rad(lat2 - lat1);
  const dLon = deg2rad(lon2 - lon1);
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
    Math.sin(dLon / 2) * Math.sin(dLon / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  const distance = R * c; // 单位为千米
  return distance;
}



// 示例坐标点和坐标区域
let point = [120.502665,31.60207]; // 坐标点 [纬度, 经度]
let area = [
    [120.50161, 31.60445],
    [120.501484, 31.607747],
    [120.501905, 31.610399],
    [120.506745, 31.611654],
    [120.507165, 31.610005],
    [120.508554, 31.608643],
    [120.509943, 31.605919],
    [120.512889, 31.604306],
    [120.517476, 31.60126],
    [120.520506, 31.597209],
    [120.513646, 31.597245],
    [120.512047, 31.599862],
    [120.50948, 31.601654],
    [120.505903, 31.603697]
]; // 坐标区域的多个点

let isPointOutArea = !isPointInArea(point, area)
// 坐标点如果在区域内则不用计算距离
if(!isPointOutArea) return 

let [isProjectionPoint,segmentStart,segmentEnd] = minDistanceLineSegmentToArea(point, area);

let projectionPoint

if(isProjectionPoint){
    // 计算投影点坐标
    projectionPoint = calculateProjectionPoint(point, segmentStart, segmentEnd);
}else{
    // 将最短距离的端点坐标当作投影点
    projectionPoint = segmentStart;
}


// 计算两点之间的距离
const distance = getDistanceFromLatLonInKm(...point,...projectionPoint);

console.log(distance); // 输出两点之间的距离,单位为千米
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值