【问题标题】:Coordinates of the closest points of two geometries in ShapelyShapely中两个几何图形最近点的坐标
【发布时间】:2014-08-16 10:30:18
【问题描述】:

有一条折线,其中包含顶点坐标列表 = [(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() 方法在计算距离时应该有坐标。有没有办法从这个方法中返回它?

【问题讨论】:

    标签: python distance geos shapely


    【解决方案1】:

    您描述的 GIS 术语是 linear referencingShapely has these methods

    # 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)
    

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

    【讨论】:

    • 对可能关注该帖子的任何人的快速说明:line.project(p) 从 LineString() 方法的参数中给出的起始节点坐标测量沿线的投影点。但是np = line.interpolate(line.project(p)) 以全局坐标的形式给出 POINT (5 7),而不是从行的开头。这里线从 (0,0) 开始。所以它可能会令人困惑。
    • 小心声明变量“np”,因为这可能与 numpy 导入冲突。
    • 如果他指的是线上的一个顶点,他正在询问 线上点的坐标,Shapely 文档说:注意最近的点可能不是几何中的现有顶点
    【解决方案2】:

    如果您只有一个片段(例如:一行,指的是标题)而不是片段列表,这就是我所做的,并且通过了一个通过的测试用例。请注意,此页面上的某些用户只是通过查看标题来寻找来自 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 点。

    【讨论】:

    • 这是一个完美的解决方案。使用“preciselerp 来计算xy 怎么样?就像x = (1 - lerp) * self.a.x + lerp * self.b.x 一样?例如,请参阅stackoverflow.com/a/23716956/1328439 了解更多详细信息和比较。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-26
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 2012-12-12
    • 1970-01-01
    • 2022-01-14
    相关资源
    最近更新 更多