在 JavaScript 中找到一条直线上距离第三个点最近的点

3

我正在尝试找到一条直线上距离该直线外第三个点最近的点。这些点是纬度/经度。

简单的示意图展示了我的目标。我用它来编写javascript代码,但任何语言或公式仍然可以使用。我知道这是基本几何问题,但我在Google上仍然找不到公式:S lol...请好好学习!

var a = '48,-90';
var b = '49,-92';
var c = '48.25,-91.8';
var d = 'calculated point on line';

enter image description here


4
https://jsfiddle.net/soulwire/UA6H5/ 展示了一个视觉效果很好的例子。 - Steven
4个回答

3

RCrowe @ 在折线中查找离 latlng 最近的点


这个问题涉及到 IT 技术。
/* desc Static function. Find point on lines nearest test point
   test point pXy with properties .x and .y
   lines defined by array aXys with nodes having properties .x and .y 
   return is object with .x and .y properties and property i indicating nearest segment in aXys 
   and property fFrom the fractional distance of the returned point from aXy[i-1]
   and property fTo the fractional distance of the returned point from aXy[i]   */


function getClosestPointOnLines(pXy, aXys) {

    var minDist;
    var fTo;
    var fFrom;
    var x;
    var y;
    var i;
    var dist;

    if (aXys.length > 1) {

        for (var n = 1 ; n < aXys.length ; n++) {

            if (aXys[n].x != aXys[n - 1].x) {
                var a = (aXys[n].y - aXys[n - 1].y) / (aXys[n].x - aXys[n - 1].x);
                var b = aXys[n].y - a * aXys[n].x;
                dist = Math.abs(a * pXy.x + b - pXy.y) / Math.sqrt(a * a + 1);
            }
            else
                dist = Math.abs(pXy.x - aXys[n].x)

            // length^2 of line segment 
            var rl2 = Math.pow(aXys[n].y - aXys[n - 1].y, 2) + Math.pow(aXys[n].x - aXys[n - 1].x, 2);

            // distance^2 of pt to end line segment
            var ln2 = Math.pow(aXys[n].y - pXy.y, 2) + Math.pow(aXys[n].x - pXy.x, 2);

            // distance^2 of pt to begin line segment
            var lnm12 = Math.pow(aXys[n - 1].y - pXy.y, 2) + Math.pow(aXys[n - 1].x - pXy.x, 2);

            // minimum distance^2 of pt to infinite line
            var dist2 = Math.pow(dist, 2);

            // calculated length^2 of line segment
            var calcrl2 = ln2 - dist2 + lnm12 - dist2;

            // redefine minimum distance to line segment (not infinite line) if necessary
            if (calcrl2 > rl2)
                dist = Math.sqrt(Math.min(ln2, lnm12));

            if ((minDist == null) || (minDist > dist)) {
                if (calcrl2 > rl2) {
                    if (lnm12 < ln2) {
                        fTo = 0;//nearer to previous point
                        fFrom = 1;
                    }
                    else {
                        fFrom = 0;//nearer to current point
                        fTo = 1;
                    }
                }
                else {
                    // perpendicular from point intersects line segment
                    fTo = ((Math.sqrt(lnm12 - dist2)) / Math.sqrt(rl2));
                    fFrom = ((Math.sqrt(ln2 - dist2)) / Math.sqrt(rl2));
                }
                minDist = dist;
                i = n;
            }
        }

        var dx = aXys[i - 1].x - aXys[i].x;
        var dy = aXys[i - 1].y - aXys[i].y;

        x = aXys[i - 1].x - (dx * fTo);
        y = aXys[i - 1].y - (dy * fTo);

    }

    return { 'x': x, 'y': y, 'i': i, 'fTo': fTo, 'fFrom': fFrom };
}

3

以下是根据这篇原始的博客,为TypeScript量身定制的更简单的解决方案。

export function findNearestPointOnLine(px: number, py: number, ax: number, ay: number, bx: number, by: number)
{
    const atob = { x: bx - ax, y: by - ay };
    const atop = { x: px - ax, y: py - ay };
    const len = (atob.x * atob.x) + (atob.y * atob.y);
    let dot = (atop.x * atob.x) + (atop.y * atob.y);
    const t = Math.min(1, Math.max(0, dot / len));

    dot = ((bx - ax) * (py - ay)) - ((by - ay) * (px - ax));

    return { x: ax + (atob.x * t), y: ay + (atob.y * t) };
}

我将这个扩展功能应用到了给定的矩形上。
export function findNearestPointOnRect(px: number, py: number, x: number, y: number, width: number, height: number)
{
    const left = x;
    const right = x + width;
    const top = y;
    const bottom = top + height;

    // top, right, bottom, left
    const { x: topX, y: topY } = findNearestPointOnLine(px, py, left, top, right, top);
    const { x: rightX, y: rightY }  = findNearestPointOnLine(px, py, right, top, right, bottom);
    const { x: bottomX, y: bottomY }  = findNearestPointOnLine(px, py, left, bottom, right, bottom);
    const { x: leftX, y: leftY }  = findNearestPointOnLine(px, py, left, top, left, bottom);

    const topD = distanceBetween(px, py, topX, topY);
    const rightD = distanceBetween(px, py, rightX, rightY);
    const bottomD = distanceBetween(px, py, bottomX, bottomY);
    const leftD = distanceBetween(px, py, leftX, leftY);

    const points: {
        side: 'top' | 'right' | 'bottom' | 'left';
        d: number;
        x: number;
        y: number;
    }[] = [
        { side: 'top', d: topD, x: topX, y: topY },
        { side: 'right', d: rightD, x: rightX, y: rightY },
        { side: 'bottom', d: bottomD, x: bottomX, y: bottomY },
        { side: 'left', d: leftD, x: leftX, y: leftY },
    ];

    points.sort((a, b) =>
    {
        if (a.d < b.d)
        {
            return -1;
        }
        if (a.d > b.d)
        {
            return 1;
        }

        return 0;
    });

    return points[0];
}

注意:如果您的线条或矩形被转换了,请确保您首先将输入点转换为本地坐标,以使处理变得更加简单。

这应该是答案。它更简单和更快。 不过需要纠正一点,第二个点的计算 dot = ((bx - ax) * (py - ay)) - ((by - ay) * (px - ax)) 不是必要的,也没有被使用,并且它会得到相同的结果。看起来像是之前版本的遗留问题。 - Glantucan
此外,我会尽量只回答原始问题,而不包括与之无直接关系的额外代码。这可能会令人困惑。 - Glantucan

0

设A、B、C为double[],其中A = {a的x坐标,a的y坐标},B = {b的x坐标,b的y坐标},C = {c的x坐标,c的y坐标}。 如果线段ab是y = mx + z,则

m = (A[1]-B[1])/(A[0]-B[0])

z = A[1] - m*A[0]

现在我们需要通过c点作ab线段的垂线。如果这条线段是y = m'x + z',则

m' = -1/m = (A[0]-B[0])/(B[1]-A[1])

z' = C[1] - m'*C[0]

最后我们需要求出这些线段的交点。我们令y=y并解决

mx+z = m'x + z'

x(m-m') = z'-z

x = (z'-z)/(m-m')

y = m*x + z

D = {(z'-z)/(m-m'), m*x + z}。 现在只需将其转换为字符串即可。 希望对您有所帮助!


-1

通常可以通过绘制与点相交的垂线来确定直线上距离该点最近的点。 要找到垂直斜率,请执行以下代码:

var slope = (Number(a.substring(a.indexOf(",") + 1, a.length)) //The Y coordinate of A
 - Number(b.substring(b.indexOf(",") + 1, b.length))) // The Y coordinate of B
 / (Number(a.substring(0, a.indexOf(","))) // The X coordinate of A
 - Number(b.substring(0, b.indexOf(",")))); //The Y coordinate of B

这是斜率公式 (y2 - y1) / (x2 - x1)
现在我们有了斜率,将其转换为垂直斜率就很容易了。

var perpendicularSlope = -1 / slope;

现在,我们需要应用点斜式公式(y - y1 = slope * (x - x1))。
var newPointX = Number(c.substring(0, c.indexOf(",")); //Gets the X value of new point
var newPointY = Number(c.substring(c.indexOf(",") + 1, c.length)); //Gets the Y value of new point
//Note that in the formula provided above, y and x are not going to be assigned in code.
//I'm going to bypass formatting it like that and go right to the slope intercept form
var perpendicularBValue = newPointY - perpendicularSlope * newPointX;

//Slope intercept form is y = mx + b. (m is slope and b is where the line intersects the y axis)

接下来,我们需要获取第一条线的斜率截距形式。
var lineX = Number(a.substring(0, a.indexOf(",")); 
var lineY = Number(a.substring(a.indexOf(",") + 1, a.length));
var lineB = lineY - slope * newPointY;

我在这里创建了一个方程系统。要解决它,我们将不得不使用传递性质(如果a = b且b = c,则a = c);
var xCollision = (lineB - perpendicularBValue) / (perpendicularSlope - slope);
var yCollision = slope * xCollosion + lineB;
var d = xCollision + "," + yCollision;

我使用传递属性消除了y变量并联合了方程。 然后我解出了x的值。 然后我将x的值代入并解出了y的值。 这就是原始线和垂直线相交的地方。

还记得我之前说过这通常有效吗?
因为你正在使用线段而不是线,有时最近点将是端点。
以下是如何修复d值的方法

var aDistance = Math.sqrt(
    Math.pow(lineX - newPointX, 2) +
    Math.pow(lineY - newPointY, 2));
var bDistance = Math.sqrt(
    Math.pow(Number(b.substring(0, b.indexOf(",")) - newPointX, 2) +
    Math.pow(Number(b.substring(b.indexOf(",") + 1, b.length) - newPointY, 2));
var dDistance = Math.sqrt(
    Math.pow(xCollision - newPointX, 2) +
    Math.pow(yCollision - newPointY, 2));
var closestEndpoint = aDistance < bDistance ? aDistance : bDistance;
var closestPoint = closestEndpoint < dDistance ? closestEndpoint : dDistance;

我使用了一个叫做距离公式(平方根(x1 - x2)^2 +(y1 - y2)^2)的公式来确定点之间的距离。然后,我使用简写if语句来确定最近的点。
如果您需要更多帮助,请留下评论。

这个是否可以处理水平线?看起来它可能将垂直线的斜率视为-1/0。 - Jeremy Kahan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接