两个几何图形中距离最近的点的坐标在Shapely中如何表示?

48

给定一个由顶点坐标构成的多段线 [(x1,y1), (x2,y2), (x3,y3),...] 和一个点 (x,y),在 Shapely 中,geometry1.distance(geometry2) 返回两个几何对象之间的最短距离。

>>> from shapely.geometry import LineString, Point
>>> line = LineString([(0, 0), (5, 7), (12, 6)])  # geometry2
>>> list(line.coords)
[(0.0, 0.0), (5.0, 7.0), (12.0, 6.0)]
>>> p = Point(4,8)  # geometry1
>>> list(p.coords)
[(4.0, 8.0)]
>>> p.distance(line)
1.4142135623730951
但是我还需要找到在线上离点(x,y)最近的点的坐标。在上面的例子中,这是LineString对象上距离Point(4,8) 1.4142135623730951单位远的点的坐标。在计算距离时,方法distance()应该有这些坐标。是否有任何方法可以从这个方法中返回它们?
2个回答

64

你正在描述的GIS术语是线性参考,而Shapely具有这些方法

# Length along line that is closest to the point
print(line.project(p))

# Now combine with interpolated point on line
p2 = line.interpolate(line.project(p))
print(p2)  # POINT (5 7)

另一种方法是使用nearest_points函数:

from shapely.ops import nearest_points
p2 = nearest_points(line, p)[0]
print(p2)  # POINT (5 7)

它提供与线性参考技术相同的答案,但可以确定来自更复杂几何输入的最近点对,例如两个多边形。


8
针对可能正在关注该帖子的任何人的快速说明: line.project(p) 用于计算给定在LineString()方法参数中的起始节点坐标沿着该线的投影点。但是,np = line.interpolate(line.project(p)) 在全局坐标下给出了点(5,7),而不是从线段起点开始计算。这里线段从(0,0)开始,可能会令人困惑。 - Asif Rehan
3
注意不要将变量命名为 'np',因为这可能会与导入的numpy模块产生冲突。 - user14241
如果他参考线上的一个顶点,他正在询问该点的坐标,Shapely文档指出:请注意,最近的点可能不是几何图形中存在的顶点。 - SchemeSonic

3

如果您只有一个段落(例如:一行,如标题所示)而不是一系列段落,请参考以下内容,并附上通过的测试用例。请注意,从标题来看,该页面上的某些用户只是在寻找这个内容,他们是从 Google 搜索进入的。

Python 代码:

def sq_shortest_dist_to_point(self, other_point):
    dx = self.b.x - self.a.x
    dy = self.b.y - self.a.y
    dr2 = float(dx ** 2 + dy ** 2)

    lerp = ((other_point.x - self.a.x) * dx + (other_point.y - self.a.y) * dy) / dr2
    if lerp < 0:
        lerp = 0
    elif lerp > 1:
        lerp = 1

    x = lerp * dx + self.a.x
    y = lerp * dy + self.a.y

    _dx = x - other_point.x
    _dy = y - other_point.y
    square_dist = _dx ** 2 + _dy ** 2
    return square_dist

def shortest_dist_to_point(self, other_point):
    return math.sqrt(self.sq_shortest_dist_to_point(other_point))

一个测试用例:

def test_distance_to_other_point(self):
    # Parametrize test with multiple cases:
    segments_and_point_and_answer = [
        [Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 4.0), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 3.0), 1.0],
        [Segment(Point(0.0, 0.0), Point(0.0, 3.0)), Point(1.0, 1.0), 1.0],
        [Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(2.0, 2.0), 0.0],
        [Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(2.0, 2.0), 0.0],
        [Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 3.0), 1.0],
        [Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 4.0), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-3.0, -4.0), 1],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-4.0, -3.0), 1],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(1, 2), 1],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(2, 1), 1],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-3, -1), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-1, -3), math.sqrt(2.0)],
        [Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(3, 1), math.sqrt(2.0)],
        [Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(1, 3), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(3, 1), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(1, 3), math.sqrt(2.0)]
    ]

    for i, (segment, point, answer) in enumerate(segments_and_point_and_answer):
        result = segment.shortest_dist_to_point(point)
        self.assertAlmostEqual(result, answer, delta=0.001, msg=str((i, segment, point, answer)))

注意:我假设这个函数在一个Segment类内。 如果你的线是无限的,请不要仅将lerp限制在0到1之间,但仍至少提供两个不同的ab点。

这是一个完美的解决方案。使用“_precise_” lerp 计算 xy,例如 x = (1 - lerp) * self.a.x + lerp * self.b.x,如 https://dev59.com/pW855IYBdhLWcg3wbjnO#23716956 所示,了解更多细节和比较。 - Dima Chubarov

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