背景
从一个已知的点开始,我需要针对MultiLineStrings表建立最近的周围“可见周长”,如图所示。
我在此站点上搜索了许多术语(例如,最小边,最小周长,最近的邻居,剪辑,包含多边形,可见性,捕捉,切割节点,光线跟踪,洪水填充,内部边界,路线,凹面船体),但是找不到与该场景匹配的任何先前问题。
图表
- 绿色圆圈是已知点。
- 黑线是已知的MultiLineStrings。
- 灰线表示从已知点开始的径向扫描。
- 红点是径向扫描和MultiLineStrings的最近交点。
参量
- 该点永远不会与MultiLineStrings相交。
- 该点将始终名义上位于MultiLineString中。
- MultiLineString将永远不会完全包围该点,因此周长将为MultiLineString。
- 将有一个包含大约1,000个MultiLineString的表(通常包含大约100点的单行)。
考虑的方法
- 通过从已知点构造一系列线(例如,以1度为增量)进行径向扫描。
- 用MultiLineStrings建立每个径向扫掠线的最近交点。
- 当一条径向扫掠线不与任何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}})
通常,径向扫描没有有意义的“分辨率”:它从一个“事件”到下一个顺序扫描,其中事件由折线的原始节点及其相互交点组成(可以在绕原始扫描时动态找到)节点)。其输出将完全准确。
—
whuber