Coordinates of the closest points of two geometries in Shapely

前端 未结 2 734
旧巷少年郎
旧巷少年郎 2020-12-01 03:39

There is a polyline with a list of coordinates of the vertices = [(x1,y1), (x2,y2), (x3,y3),...] and a point(x,y). In Shapely, geometry1.distance(geometry2) re

相关标签:
2条回答
  • 2020-12-01 04:18

    In case you have a single segment (e.g.: a line, as referring to the title) rather than a list of segments, here is what I did, and with a passing test case. Please consider that some users on this page are looking just for that from looking at the title, coming from a Google search.

    Python code:

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

    A test case:

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

    Note: I assume this function is inside a Segment class. In case your line is infinite, don't limit the lerp from 0 to 1 only, but still at least provide two distinct a and b points.

    0 讨论(0)
  • 2020-12-01 04:23

    The GIS term you are describing is linear referencing, and Shapely has these methods.

    # Length along line that is closest to the point
    print(line.project(p))
    
    # Now combine with interpolated point on line
    np = line.interpolate(line.project(p))
    print(np)  # POINT (5 7)
    

    An alternative method is to use nearest_points:

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

    which provides the same answer as the linear referencing technique does, but can determine the nearest pair of points from more complicated geometry inputs, like two polygons.

    0 讨论(0)
提交回复
热议问题