web-dev-qa-db-ja.com

Shapelyの2つのジオメトリの最も近い点の座標

頂点の座標のリスト= [(x1、y1)、(x2、y2)、(x3、y3)、...]とpoint(x、y)を持つポリラインがあります。 Shapelyでは、geometry1.distance(geometry2)は2つのジオメトリ間の最短距離を返します。

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

しかし、point(x、y)に最も近いライン上のポイントの座標も見つける必要があります。上記の例では、これはLineStringオブジェクト上のポイントの座標であり、Point(4,8)から1.4142135623730951単位離れています。 distance()メソッドは、距離を計算するときに座標を持つ必要があります。このメソッドから返されるようにする方法はありますか?

28
Asif Rehan

あなたが説明しているGIS用語は 線形参照 であり、 Shapelyにはこれらのメソッドがあります です。

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

別の方法は nearest_points

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

これは、線形参照手法と同じ答えを提供しますが、2つのポリゴンなどのより複雑なジオメトリ入力から最も近いポイントのペアを決定できます。

30
Mike T

セグメントのリストではなく、単一のセグメント(例:タイトルを参照する行)がある場合、ここに私がやったことと、合格したテストケースがあります。このページの一部のユーザーは、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に制限しないでください。ただし、少なくとも2つの異なるaおよびbポイントを指定してください。

0