Python:指向最接近第三点的线

时间:2017-11-08 10:39:55

标签: python python-3.x numpy

我在两个XY点(p1和p2)之间有一条线/向量,在线外有一个第三个XY点(p3)。根据{{​​3}}我知道如何获得该点到线的距离。但我真正想要的是该线上的点(p4)与第三点(p3)的最小距离(d)。我找到了this post,但我觉得这不是正确的解决方案。也许Numpy或Python中包含了一些东西?

this post

根据@allo我尝试了以下内容。您可以将我的代码下载为distance of a point to a line including crossing pointPython file(两者都是Python3)。

points = [[1, 1], [3, 1], [2.5, 2], [2.5, 1]]
import matplotlib.pyplot as plt
%matplotlib inline

fig, ax = plt.subplots()
fig.set_size_inches(6,6)

x, y = zip(*points[:2])
l1, = ax.plot(x,y, color='blue')
scatter1 = ax.scatter(x=x,y=y, color='blue', marker='x', s=80, alpha=1.0)

x, y = zip(*points[2:])
l2, = ax.plot(x,y, color='red')
scatter2 = ax.scatter(x=x,y=y, color='red', marker='x', s=80, alpha=1.0)

p1 = Vector2D(*points[0])
p2 = Vector2D(*points[1])
p3 = Vector2D(*points[2])

p1p2 = p2.sub_vector(p1)
p1p3 = p3.sub_vector(p1)

angle_p1p2_p1p3 = p1p2.get_angle_radians(p1p3)
length_p1p3 = p1p3.get_length()
length_p1p2 = p1p2.get_length()

p4 = p1.add_vector(p1p2.multiply(p1p3.get_length()/p1p2.get_length()).multiply(math.cos(p1p2.get_angle_radians(p1p3))))

#p4 = p1 + p1p2 * length(p1p3)/length(p1p2)*cos(angle(p1p2, p1p3))

p4 = p1.add_vector(p1p2.multiply(length_p1p3/length_p1p2*math.cos(angle_p1p2_p1p3)))
p4

导致p4 = (1.8062257748298551,1.0),但显然应(2.5,1.0)

Jupyter Notebook

3 个答案:

答案 0 :(得分:1)

Shapely的distance()函数返回最小距离:

>>> from shapely.geometry import LineString as shLs
>>> from shapely.geometry import Point as shPt
>>> l = shLs([ (1,1), (3,1)])
>>> p = shPt(2,2)
>>> dist = p.distance(l)
1.0
>>> l.interpolate(dist).wkt
'POINT (2 1)'

enter image description here

答案 1 :(得分:1)

分析几何

让我们从指定的行开始,我们用string = '1,2,3-6,10' print string (x1, y1)上的两个点来定义行。

使用(x2, y2)dx = x2-x1,我们可以正式将该行中的每个点都写为dy = y2-y1,其中(x12, y12) = (x1, y1) + a*(dx, dy)是实数。

使用类似符号传入a并垂直于指定值的线上的点为(x3, y3)

要找到我们必须强加(x34, y34) = (x3, y3) + b*(-dy, +dx)(x12, y12) = (x34, y34)

分别编写(x1, y1) + a*(dx, dy) = (x3, y3) + b*(-dy, +dx)x

的等式
y

它是y1 + a dy - y3 - b dx = 0 x1 + a dx + b dy - x3 = 0 a中的线性系统,其解决方案是

b

位于该线上的最近点a = (dy y3 - dy y1 + dx x3 - dx x1) / (dy^2 + dx^2) b = (dy x3 - dy x1 - dx y3 + dx y1) / (dy^2 + dx^2) 的坐标 是(x3, y3) - 您只需要计算系数(x1+a*dx, y1+a*dy)

从数值上讲,线性系统的行列式是a,因此只有当两个初始点相对于它们与第三点的距离w / r非常接近时才会出现问题。

Python实施

我们使用2-uple浮点数来表示2D点,我们定义一个函数,其参数为3 2-uples,表示定义直线(dx**2+dy**2)和点({{1}的点})确定p1, p2在所述行上的位置。

p3

为了测试实现,我使用了OP使用的三个点 展示他们在这个问题上的问题:

p4

似乎In [16]: def p4(p1, p2, p3): ...: x1, y1 = p1 ...: x2, y2 = p2 ...: x3, y3 = p3 ...: dx, dy = x2-x1, y2-y1 ...: det = dx*dx + dy*dy ...: a = (dy*(y3-y1)+dx*(x3-x1))/det ...: return x1+a*dx, y1+a*dy 的结果与OP预期一致。

答案 2 :(得分:0)

你想要做的是vector projection

边缘<script src="//www.amcharts.com/lib/3/amcharts.js"></script> <script src="//www.amcharts.com/lib/3/serial.js"></script> <script src="//www.amcharts.com/lib/3/themes/light.js"></script> <div id="chartdiv"></div>会旋转到边p1p3,您需要找到段p1p2的正确长度。然后你可以使用p1p4。所需因子由p1+FACTOR*p1p2 / length(p1p2)p1p2之间角度的余弦给出。然后你得到

p1p3

这里以两个边缘情况为例:

  • 如果p4 = p1 + p1p2 * length(p1p3)/length(p1p2)*cos(angle(p1p2, p1p3)) 0正交,则余弦为p1p3,因此p1p2位于p4上。
  • p1位于p1p3时,余弦为1,因此p1p2仅按p1p2缩放以获得length(p1p3)/length(p1p2)

您还可以使用点积p1p4替换cosinus。

您可以找到更多详情和精美插图in the wikibook about linear algebra

这是一个从你的python代码派生的完整示例。我在这里使用了numpy而不是Vector2D。

dot(p1p2 / length(p1p2), p1p3 / length(p1p3)

我们可以使用标量积的线性来缩短points = [[1, 1], [3, 1], [2.5, 2], [2.5, 1]] import matplotlib.pyplot as plt %matplotlib inline import numpy as np fig, ax = plt.subplots() fig.set_size_inches(6,6) x, y = zip(*points[:2]) l1, = ax.plot(x,y, color='blue') scatter1 = ax.scatter(x=x,y=y, color='blue', marker='x', s=80, alpha=1.0) x, y = zip(*points[2:]) l2, = ax.plot(x,y, color='red') scatter2 = ax.scatter(x=x,y=y, color='red', marker='x', s=80, alpha=1.0) p1 = np.array(points[0]) p2 = np.array(points[1]) p3 = np.array(points[2]) p1p2 = p2 - p1 p1p3 = p3 - p1 p4 = p1 + p1p2 / np.linalg.norm(p1p2) * np.linalg.norm(p1p3) * ((p1p2/np.linalg.norm(p1p2)).T * (p1p3/np.linalg.norm(p1p3))) p1, p2, p3, p4, p1p2, p1p3 行:

p4