meen
meen

Reputation: 2339

To find coordinates of nearest point on a line segment from a point

I need to calculate the foot of a perpendicular line drawn from a point P to a line segment AB. I need coordinates of point C where PC is perpendicular drawn from point P to line AB.

enter image description here

I found few answers on SO here but the vector product process does not work for me. Here is what I tried:

function nearestPointSegment(a, b, c) {
   var t = nearestPointGreatCircle(a,b,c);
   return t;
}

function nearestPointGreatCircle(a, b, c) {
  var a_cartesian = normalize(Cesium.Cartesian3.fromDegrees(a.x,a.y))
  var b_cartesian = normalize(Cesium.Cartesian3.fromDegrees(b.x,b.y))
  var c_cartesian = normalize(Cesium.Cartesian3.fromDegrees(c.x,c.y))
  var G = vectorProduct(a_cartesian, b_cartesian);
  var F = vectorProduct(c_cartesian, G);
  var t = vectorProduct(G, F);
  t = multiplyByScalar(normalize(t), R);
  return fromCartesianToDegrees(t);
}

function vectorProduct(a, b) {
    var result = new Object();
    result.x = a.y * b.z - a.z * b.y;
    result.y = a.z * b.x - a.x * b.z;
    result.z = a.x * b.y - a.y * b.x;
    return result;
}

function normalize(t) {
    var length = Math.sqrt((t.x * t.x) + (t.y * t.y) + (t.z * t.z));
    var result = new Object();
    result.x = t.x/length;
    result.y = t.y/length;
    result.z = t.z/length;
    return result;
}

function multiplyByScalar(normalize, k) {
    var result = new Object();
    result.x = normalize.x * k;
    result.y = normalize.y * k;
    result.z = normalize.z * k;
    return result;
}

function fromCartesianToDegrees(pos) {
  var carto  = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pos);     
  var lon = Cesium.Math.toDegrees(carto.longitude); 
  var lat = Cesium.Math.toDegrees(carto.latitude); 
  return [lon,lat];
}

What I am missing in this?

Upvotes: 6

Views: 2481

Answers (2)

Phrogz
Phrogz

Reputation: 303421

function closestPointOnLineSegment(pt, segA, segB) {
    const A = pt.x - segA.x,
          B = pt.y - segA.y,
          C = segB.x - segA.x,
          D = segB.y - segA.y

    const segLenSq = C**2 + D**2
    const t = (segLenSq != 0) ? (A*C + B*D) / segLenSq : -1

    return (t<0) ? segA : (t>1) ? segB : {
      x: segA.x + t * C,
      y: segA.y + t * D
    }
}

can.width = can.offsetWidth
can.height = can.offsetHeight
const ctx = can.getContext('2d')
const segA = {x:100,y:100},
      segB = {x:400, y:200},
      pt = {x:250, y:250}

visualize()

function visualize() {
  ctx.clearRect(0, 0, can.width, can.height)
  
  const t = Date.now()
  pt.x = Math.cos(t/1000) * 150 + 250
  pt.y = Math.sin(t/1000) * 100 + 150
  
  segA.x = Math.cos(t / 2000) * 50 + 150
  segA.y = Math.sin(t / 2500) * 50 + 50
  
  segB.x = Math.cos(t / 3000) * 75 + 400
  segB.y = Math.sin(t / 2700) * 75 + 100
  
  line(segA, segB, 'gray', 2)

  
  const closest = closestPointOnLineSegment(pt, segA, segB)
  ctx.setLineDash([5, 8])
  line(pt, closest, 'orange', 2)
  ctx.setLineDash([])
  dot(closest, 'rgba(255, 0, 0, 0.8)', 10)

  dot(pt, 'blue', 7)
  dot(segA, 'black', 7)
  dot(segB, 'black', 7)

  window.requestAnimationFrame(visualize)
}

function dot(p, color, w) {
   ctx.fillStyle = color
   ctx.fillRect(p.x - w/2, p.y - w/2, w, w)
}

function line(a, b, color, n) {
  ctx.strokeStyle = color
  ctx.lineWidth = n
  ctx.beginPath()
  ctx.moveTo(a.x, a.y)
  ctx.lineTo(b.x, b.y)
  ctx.stroke()
}
html, body { height:100%; min-height:100%; margin:0; padding:0; overflow:hidden }
canvas { width:100%; height:100%; background:#ddd }
<canvas id="can"></canvas>

Upvotes: 1

user5734311
user5734311

Reputation:

Here's a vector-based way:

function foot(A, B, P) {
  const AB = {
    x: B.x - A.x,
    y: B.y - A.y
  };
  const k = ((P.x - A.x) * AB.x + (P.y - A.y) * AB.y) / (AB.x * AB.x + AB.y * AB.y);
  return {
    x: A.x + k * AB.x,
    y: A.y + k * AB.y
  };
}

const A = { x: 1, y: 1 };
const B = { x: 4, y: 5 };
const P = { x: 4.5, y: 3 };
const C = foot(A, B, P);
console.log(C);

// perpendicular?
const AB = {
  x: B.x - A.x,
  y: B.y - A.y
};
const PC = {
  x: C.x - P.x,
  y: C.y - P.y
};
console.log((AB.x * PC.x + AB.y * PC.y).toFixed(3));

Theory:

enter image description here

I start with the vector from A to B, A➞B. By multiplying this vector by a scalar k and adding it to point A I can get to any point C on the line AB.

I) C = A + k × A➞B

Next I need to establish the 90° angle, which means the dot product of A➞B and P➞C is zero.

II) A➞B · P➞C = 0

Now solve for k.

Upvotes: 8

Related Questions