バックグラウンド
既知のポイントから、図に示されているように、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