shapelyを使用して、ポイントに最も近いラインセグメントを検索しますか?


17

バックグラウンド

既知のポイントから、図に示されているように、MultiLineStringのテーブルに対して最も近い周囲の「見える境界」を確立する必要があります。

私はこのサイトをいくつかの用語(例:最小エッジ、最小周囲、最近傍、クリップ、ポリゴンを含む、可視性、スナップ、カットノード、レイトレース、フラッドフィル、内部境界、ルーティング、凹型ハル)で検索しました。このシナリオに一致すると思われる以前の質問を見つけることができません。

  • 緑の円は既知のポイントです。
  • 黒い線は、既知のMultiLineStringです。
  • 灰色の線は、既知のポイントからの放射状の掃引を示しています。
  • 赤い点は、放射状スイープとMultiLineStringsの最も近い交点です。

ここに画像の説明を入力してください

パラメーター

  • PointはMultiLineStringと交差しません。
  • Pointは常にMultiLineStrings内の名目上中央に配置されます。
  • MultiLineStringはPointを完全に囲むことはないため、境界線はMultiLineStringになります。
  • 約1,000個のMultiLineStringを含むテーブルがあります(通常は約100ポイントの単一行を含みます)。

考慮される方法論

  • 既知のポイントから一連の線を構築することにより、放射状スイープを実行します(たとえば、1度の増分で)。
  • MultiLineStringを使用して、各放射状スイープラインの最も近い交点を確立します。
  • 放射状スイープラインの1つがMultiLineStringのいずれとも交差しない場合、これは、MultiLineStringの周囲構造に収まる周囲のギャップを示します。

概要

この手法は最も近い交差点を検出しますが、放射状スイープの解像度に応じて、必ずしもすべての最も近い境界ノードポイントを検出するわけではありません。誰もがすべての周辺ポイントを確立するための代替方法を推奨したり、何らかの形のバッファリング、セクター化またはオフセットでラジアルスイープテクニックを補完できますか?

ソフトウェア

私は、SpatiaLiteやShapelyをソリューションに使用することを好みますが、オープンソースソフトウェアを使用して実装できる提案を歓迎します。

編集:実用的なソリューション(@geneの回答に基づく)

from shapely.geometry import Point, LineString, mapping, shape
from shapely.ops import cascaded_union
from shapely import affinity
import fiona

sweep_res = 10  # sweep resolution (degrees)
focal_pt = Point(0, 0)  # radial sweep centre point
sweep_radius = 100.0  # sweep radius

# create the radial sweep lines
line = LineString([(focal_pt.x,focal_pt.y), \
                   (focal_pt.x, focal_pt.y + sweep_radius)])

sweep_lines = [affinity.rotate(line, i, (focal_pt.x, focal_pt.y)) \
               for i in range(0, 360, sweep_res)]

radial_sweep = cascaded_union(sweep_lines)

# load the input lines and combine them into one geometry
input_lines = fiona.open("input_lines.shp")
input_shapes = [shape(f['geometry']) for f in input_lines]
all_input_lines = cascaded_union(input_shapes)

perimeter = []
# traverse each radial sweep line and check for intersection with input lines
for radial_line in radial_sweep:
    inter = radial_line.intersection(all_input_lines)

    if inter.type == "MultiPoint":
       # radial line intersects at multiple points
       inter_dict = {}
       for inter_pt in inter:
           inter_dict[focal_pt.distance(inter_pt)] = inter_pt
       # save the nearest intersected point to the sweep centre point
       perimeter.append(inter_dict[min(inter_dict.keys())])

    if inter.type == "Point":
       # radial line intersects at one point only
       perimeter.append(inter)

    if inter.type == "GeometryCollection":
       # radial line doesn't intersect, so skip
       pass

# combine the nearest perimeter points into one geometry
solution = cascaded_union(perimeter)

# save the perimeter geometry
schema = {'geometry': 'MultiPoint', 'properties': {'test': 'int'}}
with fiona.open('perimeter.shp', 'w', 'ESRI Shapefile', schema) as e:
     e.write({'geometry':mapping(solution), 'properties':{'test':1}})

通常、ラジアルスイープには意味のある「解像度」はありません。1つの「イベント」から次のイベントまで順番にスキャンします。ノード)。その出力は完全に正確です。
whuber

回答:


17

シェープファイルを使用して例を再現しました。

ShapelyFionaを使用して問題を解決できます。

1)あなたの問題(形の良いPoint):

ここに画像の説明を入力してください

2)任意の行(適切な長さ)で始まる:

from shapely.geometry import Point, LineString
line = LineString([(point.x,point.y),(final_pt.x,final_pt.y)])

ここに画像の説明を入力してください

3)shapely.affinity.rotateを使用して半径を作成します(ポイントから線を回転させます。PythonでMike Toewsの回答、形の良いライブラリも参照してください。シェイプポリゴンでアフィン操作を行うことは可能ですか?):

from shapely import affinity
# Rotate i degrees CCW from origin at point (step 10°)
radii= [affinity.rotate(line, i, (point.x,point.y)) for i in range(0,360,10)]

ここに画像の説明を入力してください

4)次にshapely:cascaded_union(またはshapely:unary_union)を使用してMultiLineStringを取得します。

from shapely.ops import cascaded_union
mergedradii = cascaded_union(radii)
print mergedradii.type
MultiLineString

5)元の行と同じ(シェープファイル)

import fiona
from shapely.geometry import shape
orlines = fiona.open("orlines.shp")
shapes = [shape(f['geometry']) for f in orlines]
mergedlines = cascaded_union(shapes)
print mergedlines.type
MultiLineString

6)2つのマルチジオメトリ間の交差が計算され、結果がシェープファイルに保存されます。

 points =  mergedlines.intersection(mergedradii)
 print points.type
 MultiPoint
 from shapely.geometry import mapping
 schema = {'geometry': 'MultiPoint','properties': {'test': 'int'}}
 with fiona.open('intersect.shp','w','ESRI Shapefile', schema) as e:
      e.write({'geometry':mapping(points), 'properties':{'test':1}})

結果:

ここに画像の説明を入力してください

7)しかし、問題は、より長い半径を使用する場合、結果は異なります:

ここに画像の説明を入力してください

8)結果を取得する場合は、半径上の元のポイントから最短距離のポイントのみを選択する必要があります。

points_ok = []
for line in mergeradii:
   if line.intersects(mergedlines):
       if line.intersection(mergedlines).type == "MultiPoint":
          # multiple points: select the point with the minimum distance
          a = {}
          for pt in line.intersection(merged):
              a[point.distance(pt)] = pt
          points_ok.append(a[min(a.keys())])
       if line.intersection(mergedlines).type == "Point":
          # ok, only one intersection
          points_ok.append(line.intersection(mergedlines))
solution = cascaded_union(points_ok)
schema = {'geometry': 'MultiPoint','properties': {'test': 'int'}}
with fiona.open('intersect3.shp','w','ESRI Shapefile', schema) as e:
     e.write({'geometry':mapping(solution), 'properties':{'test':1}})

最終結果:

ここに画像の説明を入力してください

それがあなたの望むものであることを願っています。


1
優れた回答-シェープファイルを介した入出力のFionaの使用に関して特に有益です。私はあなたの答えを使用し、intersection必要な計算の量を減らすためにそれを修正した私の質問にいくつかのコードを追加しました-ありがとう。
さびたMagoo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.