Distance between point and a line (from two points)
Asked Answered
R

10

46

I'm using Python+Numpy (can maybe also use Scipy) and have three 2D points

(P1, P2, P3); 

I am trying to get the distance from P3 perpendicular to a line drawn between P1 and P2. Let P1=(x1,y1), P2=(x2,y2) and P3=(x3,y3)

In vector notation this would be pretty easy, but I'm fairly new to python/numpy and can't get anythng that works (or even close).

Any tips appreciated, thanks!

Ritch answered 3/10, 2016 at 20:40 Comment(1)
In case you need the perpendicular vector, use P12 = p2 - p1 P13 = p3 - p1 proj_13over12 = np.dot(P13, P12) * P12 / norm(P12) ** 2 perpendicular = P13 - proj_13over12 ref. [#76644580Mayst
A
63

Try using the norm function from numpy.linalg

d = norm(np.cross(p2-p1, p1-p3))/norm(p2-p1)
Anility answered 3/10, 2016 at 20:51 Comment(8)
Should be np.abs(np.cross(p2-p1, p1-p3)) / norm(p2-p1))?Dissever
unsupported operand type(s) for -: 'tuple' and 'tuple'? What's this?Judiciary
you need to convert the points to numpy arrays. This can be done like so: p1 = np.asarray(p1)Roi
abs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / np.sqrt(np.square(x2-x1) + np.square(y2-y1)) I used it this way, and it gave me the same answer in pythonEyebolt
Does this assume the line extends to infinity, or the distance it generates become larger as points P3 grow farther away from the endpoints of the line segment generated by P1,P2?Bokbokhara
brilliant. cross is correct. line does extend to infinity. this gives you |p1-p3|sin(alpha).Laconism
why abs ? @DisseverAtive
@Ative You need abs if you have a vector of p1, p2 and p3; or one could say Nx2 arrays. Since we are working in 2D, there is no cross-product for 2D coordinates, a Z axis is appended with value 0. Numpy doc says for np.cross "In cases where both input vectors have dimension 2, the z-component of the cross product is returned.", therefore you will receive an Nx1 array of scalars, but mathematically you would expect a Nx3 array of vectors (x and y are equal to 0). If you use the np.norm, you will get the norm of the Nx1 array, but what you want is the norm of each vector in the Nx3 array.Gentlemanfarmer
W
21

np.cross returns the z-coordinate of the cross product only for 2D vectors. So the first norm in the accepted answer is not needed, and is actually dangerous if p3 is an array of vectors rather than a single vector. Best just to use

d=np.cross(p2-p1,p3-p1)/norm(p2-p1)

which for an array of points p3 will give you an array of distances from the line.

Willardwillcox answered 7/1, 2018 at 13:30 Comment(4)
could you explain how norm in the numerator is dangerous if p3 is an array?Fornof
@Fornof because norm will treat the 1D array returned by np.cross as one big vector and return its "size".Willardwillcox
I only got a signed distance after removing the first norm, that is, a distance that tells me if the point is at the right or left side of the line, according to some convention.Pentose
missing imports for np and norm, see Szymon Szott's answer for complete codeMuleteer
V
14

For the above-mentioned answers to work, the points need to be numpy arrays, here's a working example:

import numpy as np
p1=np.array([0,0])
p2=np.array([10,10])
p3=np.array([5,7])
d=np.cross(p2-p1,p3-p1)/np.linalg.norm(p2-p1)
Vicarage answered 11/10, 2018 at 9:2 Comment(0)
K
5

To find distance to line from point if you have slope and intercept you can use formula from wiki https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line Python:

def distance(point,coef):
    return abs((coef[0]*point[0])-point[1]+coef[1])/math.sqrt((coef[0]*coef[0])+1)

coef is a tuple with slope and intercept

Kaftan answered 7/3, 2019 at 15:14 Comment(0)
A
5

Based on the accepted answer

Test with below line equation -

Find the perpendicular distance from the point (5, 6) to the line −2x + 3y + 4 = 0

import numpy as np
norm = np.linalg.norm

p1 = np.array([0,-4/3])
p2 = np.array([2, 0])

p3 = np.array([5, 6])
d = np.abs(norm(np.cross(p2-p1, p1-p3)))/norm(p2-p1)
# output d = 3.328201177351375

Ative answered 14/6, 2020 at 1:59 Comment(0)
E
4
abs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / np.sqrt(np.square(x2-x1) + np.square(y2-y1))

Can be used directly through the formula, just have to plug in the values and boom it will work.

Eyebolt answered 23/10, 2018 at 19:36 Comment(3)
This won't work for vertical lines.Centaury
where is x3,y3 in the equation?Fenestella
For the given OP question of (x1,y1);(x2,y2); (x3,y3) this formula should be d = abs((x2-x1)*(y1-y3) - (x1-x3)*(y2-y1)) / np.sqrt(np.square(x2-x1) + np.square(y2-y1)) Sardanapalus
A
1

Shortest Distance from Point to a Line

This is the code I got from https://www.geeksforgeeks.org:

import math 

# Function to find distance 
def shortest_distance(x1, y1, a, b, c):    
    d = abs((a * x1 + b * y1 + c)) / (math.sqrt(a * a + b * b)) 
    print("Perpendicular distance is", d)

Now you have to find A, B, C, x, and y.

import numpy as np
closest = []
x = (x ,y)
y = (x, y)
coef = np.polyfit(x, y, 1)
A = coef[0]
B = coef[1]
C = A*x[0] + B*x[1]

Now you can plug in the values:

shortest_dis = shortest_distance(x, y, A, B, C)

The full code may look like this:

import math
import numpy as np

def shortest_distance(x1, y1, a, b, c):    
    d = abs((a * x1 + b * y1 + c)) / (math.sqrt(a * a + b * b)) 
    print("Perpendicular distance is", d)

closest = []
x = (x ,y)
y = (x, y)
coef = np.polyfit(x, y, 1)
A = coef[0]
B = coef[1]
C = A*x[0] + B*x[1]
shortest_dis = shortest_distance(x, y, A, B, C)

Please let me know if any of this is unclear.

Anglesey answered 30/6, 2019 at 16:48 Comment(3)
not clear which are the coordinates of the line and which are the coordinates of the pointBiblical
@LorenzoSciuto These are coefficients for linear equations, but clarifications would help.Walkover
I have compared both the np.cross method as described above and this method. The polyfit works fine for both. However, when I compared the distance using np.cross and the formula you posted, there was a difference in results (very slightly). Since I was working with moderately large dataset, I summed the distance from point to the line. Imagine what, the "geekforGeeks" method gave the sum nearly zero.Laband
T
1

Cross products are helpful for the 2D case, but they do not generalize well to other dimensions. Dot products do however. The dot product of two orthogonal vectors is zero in any space, which you can use to come up with a simple solution.

Let's say you have P4 on the same line as P1-P2. You could parametrize it with parameter t such that

P4 = P1 + t * (P2 - P1)

The goal is to find P4 such that

(P3 - P4) . (P2 - P1) == 0

Expanding P4 in terms of t and simplifying:

(P3 - P1 - t * (P2 - P1)) . (P2 - P1) == 0
(P3 - P1) . (P2 - P1) == t * ||P2 - P1||^2
t = (P3 - P1) . (P2 - P1) / ||P2 - P1||^2

You can now plug t back into the original definition of P4 to solve for the length ||P3 - P4||

D = ||P3 - P4||
  = ||P3 - P1 - t * (P2 - P1)||
  = ||(P3 - P1) - (P3 - P1) . (P2 - P1) / ||P2 - P1||^2 * (P2 - P1)||

Conceptually, this is the norm of the vector (P3 - P1) with the component along (P2 - P1) subtracted off.

I've written a function in my library of utility routines called haggis. You can use haggis.math.segment_distance to compute the distance to the entire line (not just the bounded line segment) like this:

d = haggis.math.segment_distance(P3, P1, P2, segment=False)
Tiemroth answered 14/1, 2022 at 7:30 Comment(2)
Very nice, except your final D expression seems to be missing one term and one factor. Should be ||P3 - P1 - (P3 - P1) . (P2 - P1) / (||P2 - P1||^2) * (P2 - P1) || if I'm not mistaken.Perforate
@HansBrende. Good catch. Looks like I stopped a little short of the final answer. Updated now.Tiemroth
S
0

For anyone who would like this operation to be performed as fast as possible (with the compromise of readability) in 2D (~8x faster than otherwise on Python 3.12), I would recommend:

from math import sqrt
p1_0, p2_0, p3_0, p1_1, p2_1, p3_1 = p1[0], p2[0], p3[0], p1[1], p2[1], p3[1]
p2_0_minus_p1_0, p2_1_minus_p1_1, p3_0_minus_p1_0, p3_1_minus_p1_1 = p2_0-p1_0, p2_1-p1_1, p3_0-p1_0, p3_1-p1_1
closest_distance = (p2_0_minus_p1_0*p3_1_minus_p1_1 - p3_0_minus_p1_0*p2_1_minus_p1_1)/sqrt(p2_0_minus_p1_0*p2_0_minus_p1_0 + p2_1_minus_p1_1*p2_1_minus_p1_1)

Note that this will give negative distances if the point is on the right-hand side of the line (this can be amended by adding the abs() function around the numerator). The code calculates the closest distance between the point and the infinite straight line that extends beyond p1 and p2. Also math.sqrt() is much faster than np.sqrt() because it refers to the sqrt() function written in C.

Sempstress answered 2/2 at 15:24 Comment(0)
G
-3

3D distance should use np.dot def threeD_corres(points_3_d,pre_points_3_d,points_camera):

  for j in  range (0,len(pre_points_3_d)):
      vec1 = list(map(lambda x:x[0]- x[1],zip(pre_points_3_d[j], points_camera)))
      vec2 = list(map(lambda x:x[0]- x[1],zip(pre_points_3_d[j], points_3_d[j])))
      vec3 =  list(map(lambda x:x[0]- x[1],zip(points_3_d[j], points_camera)))
      distance = np.abs(np.dot(vec1_1,vec2_2))/np.linalg.norm(vec3)

      print("#########distance:\n",distance)
  return  distance
Gratitude answered 21/8, 2020 at 16:34 Comment(1)
Welcome to Stack Overflow. This question has been already answered several times, and while different approaches are encouraged, you should provide some background in the key differences your implemented solution has. Please edit your answer to explain your code and when this approach should be taken. Thanks for taking your time to answer, though!Feme

© 2022 - 2024 — McMap. All rights reserved.