寻找最接近的线段以定型使用?


17

背景

从一个已知的点开始,我需要针对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

Answers:


17

我用shapefile复制了您的示例。

您可以使用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的shapely库中查看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)现在,用匀称:cascaded_union(或匀称:unary_union)获得MULTILINESTRING:

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

5)与原始行相同(shapefile)

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)计算两个多重几何之间的交集,并将结果保存到shapefile中:

 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
极好的答案-特别是有关通过shapefile输入/输出Fiona的信息。我在问题上附加了一些代码,该代码使用您的答案并对其进行了修改,以减少所需的intersection计算量-谢谢。
Rusty Magoo 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.