在 Shapely 中查找多边形上最近点的坐标

Find coordinate of the closest point on polygon in Shapely

假设我有以下多边形和点:

>>> poly = Polygon([(0, 0), (2, 8), (14, 10), (6, 1)])
>>> point = Point(12, 4)

我可以计算点到多边形的距离...

>>> dist = point.distance(poly)
>>> print(dist)
2.49136439561

...但我想知道最短距离测量到的多边形边界上的点的坐标。

我最初的方法是通过点到多边形的距离来缓冲点,并找到该圆与多边形相切的点:

>>> buff = point.buffer(dist) 

但是,我不确定如何计算该点。这两个多边形不相交,所以 list(poly.intersection(buff)) 不会给我那个点。

我的方向正确吗?有没有更直接的方法?

有两种情况需要考虑:(1) 最近点位于边上,(2) 最近点是顶点。情况 (2) 很容易检查 - 只需计算到每个顶点的距离并找到最小值。案例 (1) 涉及更多的数学,但还算不错。对于情况 (1),您需要做两件事:(a) 找到从点到边的法线与边相交的位置,以及 (b) 验证它位于线段内(而不是延伸超过其中一个)结束)。如果它不在线段上,则忽略它(其中一个顶点将是该边上最近的点)。

请不要对这个答案投赞成票,正确答案是下面@Georgy 的答案。

我的回答供参考:

有一种简单的方法可以依靠 Shapely 函数来做到这一点。 首先,您需要获取多边形的外环并将点投影到环上。由于多边形,必须将外观设置为 LinearRing 没有投影功能。与直觉相反,这给出了一个距离,即从环的第一个点到环中最接近给定点的点的距离。然后,您只需使用该距离通过插值函数获取点。请参阅下面的代码。

from shapely.geometry import Polygon, Point, LinearRing

poly = Polygon([(0, 0), (2,8), (14, 10), (6, 1)])
point = Point(12, 4)

pol_ext = LinearRing(poly.exterior.coords)
d = pol_ext.project(point)
p = pol_ext.interpolate(d)
closest_point_coords = list(p.coords)[0]

值得一提的是,此方法仅在您知道该点位于多边形外部的情况下才有效。如果该点位于其中一个内环内,则需要针对该情况调整代码。

如果多边形没有内环,该代码甚至对多边形内的点也有效。这是因为我们实际上是将外环作为线串来处理,而忽略了线串是否来自多边形。

很容易将此代码扩展到计算任意点(多边形内部或外部)到多边形边界中最近点的距离的一般情况。您只需要计算从点到所有线环的最近点(和距离):外环和多边形的每个内环。然后,你只保留其中的最小值。

does the job, there is a more natural way to get the closest point using shapely.ops.nearest_points 函数的答案:

from shapely.geometry import Point, Polygon
from shapely.ops import nearest_points

poly = Polygon([(0, 0), (2, 8), (14, 10), (6, 1)])
point = Point(12, 4)
# The points are returned in the same order as the input geometries:
p1, p2 = nearest_points(poly, point)
print(p1.wkt)
# POINT (10.13793103448276 5.655172413793103)

结果与另一个答案相同:

from shapely.geometry import LinearRing
pol_ext = LinearRing(poly.exterior.coords)
d = pol_ext.project(point)
p = pol_ext.interpolate(d)
print(p.wkt)
# POINT (10.13793103448276 5.655172413793103)
print(p.equals(p1))
# True

我喜欢将多边形 poly 与以点 point[= 为中心的圆 buff 相交的想法16=],正如您在问题中所写。我会建议: poly.boundary.intersection(buff.boundary)