Tìm phân đoạn đường gần nhất tới điểm bằng cách sử dụng hình dạng?


17

Lý lịch

Từ một điểm đã biết, tôi yêu cầu thiết lập "chu vi có thể nhìn thấy" gần nhất dựa vào bảng MultiLineStrings, như thể hiện trên sơ đồ.

Tôi đã tìm kiếm trang web này với một số thuật ngữ (ví dụ: cạnh tối thiểu, chu vi tối thiểu, hàng xóm gần nhất, clip, chứa đa giác, khả năng hiển thị, snap, cắt nút, theo dõi tia, lấp lũ, ranh giới bên trong, định tuyến, vỏ lõm) nhưng không thể tìm thấy bất kỳ câu hỏi trước đó có vẻ phù hợp với kịch bản này.

Biểu đồ

  • Vòng tròn màu xanh lá cây là Điểm được biết đến.
  • Các đường màu đen là MultiLineStrings được biết đến.
  • Các đường màu xám là một dấu hiệu của một quét xuyên tâm từ Điểm đã biết.
  • Các điểm màu đỏ là giao điểm gần nhất của quét xuyên tâm và MultiLineStrings.

nhập mô tả hình ảnh ở đây

Thông số

  • Điểm sẽ không bao giờ giao nhau với MultiLineStrings.
  • Điểm sẽ luôn được đặt ở vị trí trung tâm trong MultiLineStrings.
  • MultiLineStrings sẽ không bao giờ bao gồm đầy đủ Điểm, do đó chu vi sẽ là MultiLineString.
  • Sẽ có một bảng chứa khoảng 1.000 MultiLineStrings (thông thường chứa một dòng duy nhất khoảng 100 điểm).

Phương pháp luận được xem xét

  • Thực hiện quét xuyên tâm bằng cách xây dựng một loạt các đường từ Điểm đã biết (tại, giả sử, tăng 1 độ).
  • Thiết lập điểm giao nhau gần nhất của mỗi đường quét hướng tâm với MultiLineStrings.
  • Khi một trong các đường quét hướng tâm không giao nhau với bất kỳ MultiLineStrings nào, điều này sẽ chỉ ra một khoảng trống trong chu vi sẽ được cung cấp trong cấu trúc MultiLineString theo chu vi.

Tóm lược

Trong khi kỹ thuật này sẽ tìm thấy các giao điểm gần nhất, nó sẽ không nhất thiết tìm thấy tất cả các điểm nút chu vi gần nhất, phụ thuộc vào độ phân giải của quét xuyên tâm. Bất cứ ai cũng có thể đề xuất một phương pháp thay thế để thiết lập tất cả các điểm chu vi hoặc bổ sung kỹ thuật quét xuyên tâm bằng một số hình thức đệm, phân vùng hoặc bù đắp?

Phần mềm

Sở thích của tôi là sử dụng SpatiaLite và / hoặc Shapely cho giải pháp nhưng sẽ hoan nghênh mọi đề xuất có thể được thực hiện bằng phần mềm nguồn mở.

Chỉnh sửa: Giải pháp làm việc (dựa trên câu trả lời của @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}})

Thông thường, quét xuyên tâm không có "độ phân giải" có ý nghĩa: nó quét từ "sự kiện" này sang thứ tự tiếp theo, trong đó các sự kiện bao gồm các nút gốc của polylines và các giao điểm lẫn nhau của chúng (có thể được tìm thấy một cách linh hoạt trong khi quét xung quanh bản gốc điểm giao). Đầu ra của nó sẽ hoàn toàn chính xác.
whuber

Câu trả lời:


17

Tôi đã sao chép ví dụ của bạn với shapefiles.

Bạn có thể sử dụng ShapelyFiona để giải quyết vấn đề của bạn.

1) Vấn đề của bạn (với một hình dạng Point):

nhập mô tả hình ảnh ở đây

2) bắt đầu bằng một dòng tùy ý (với độ dài phù hợp):

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

nhập mô tả hình ảnh ở đây

3) sử dụng shapely.affinity.rotate để tạo bán kính (xoay đường thẳng từ điểm, cũng xem câu trả lời của Mike Toews tại Python, thư viện tạo hình: có thể thực hiện thao tác affine trên đa giác hình dạng không? ):

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

nhập mô tả hình ảnh ở đây

4) bây giờ, sử dụng shapely: cascaded_union (hoặc shapely: unary_union ) để nhận MultiLineString:

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

5) giống với các dòng gốc (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) giao điểm giữa hai multigeometries được tính toán và kết quả được lưu vào một 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}})

Kết quả:

nhập mô tả hình ảnh ở đây

7) nhưng, vấn đề, nếu bạn sử dụng aa bán kính dài hơn, kết quả sẽ khác:

nhập mô tả hình ảnh ở đây

8) Và nếu bạn muốn nhận kết quả của mình, bạn chỉ cần chọn điểm có khoảng cách ngắn nhất từ ​​điểm ban đầu trên bán kính:

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

Kết quả cuối cùng:

nhập mô tả hình ảnh ở đây

Tôi hy vọng đó là những gì bạn muốn.


1
Câu trả lời tuyệt vời - đặc biệt nhiều thông tin liên quan đến việc sử dụng Fiona cho đầu vào / đầu ra thông qua các shapefiles. Tôi đã thêm một số mã vào câu hỏi của mình, sử dụng câu trả lời của bạn và sửa đổi nó để giảm số lượng intersectiontính toán cần thiết - cảm ơn bạn.
Rusty Magoo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.