ค้นหากลุ่มบรรทัดที่ใกล้ที่สุดเพื่อชี้โดยใช้หุ่นดีหรือไม่


17

พื้นหลัง

จากจุดที่รู้จักฉันต้องสร้าง "ปริมณฑลที่มองเห็นได้" ใกล้เคียงที่สุดกับตารางของ MultiLineStrings ดังที่แสดงในแผนภาพ

ฉันค้นหาไซต์นี้ด้วยคำศัพท์จำนวนหนึ่ง (เช่นขอบต่ำสุด, ขอบเขตต่ำสุด, เพื่อนบ้านที่อยู่ใกล้ที่สุด, คลิป, มีรูปหลายเหลี่ยม, การมองเห็น, snap, โหนดตัด, การติดตามเรย์, การเติมน้ำท่วม, ขอบเขตภายใน, เส้นทาง, ลำเรือเว้า) ไม่พบคำถามก่อนหน้านี้ที่ดูเหมือนจะตรงกับสถานการณ์นี้

แผนภาพ

  • วงกลมสีเขียวคือจุดที่รู้จัก
  • เส้นสีดำเป็นที่รู้จักกันในชื่อ MultiLineStrings
  • เส้นสีเทาเป็นตัวบ่งชี้การกวาดแบบเรเดียลจากจุดที่รู้จัก
  • จุดสีแดงคือจุดตัดที่ใกล้ที่สุดของการกวาดด้วยรัศมีและ MultiLineStrings

ป้อนคำอธิบายรูปภาพที่นี่

พารามิเตอร์

  • จุดจะไม่ตัดกัน MultiLineStrings
  • คะแนนจะถูกจัดให้อยู่กึ่งกลางชื่อใน MultiLineStrings
  • MultiLineStrings จะไม่ล้อมรอบจุดทั้งหมดดังนั้นปริมณฑลจะเป็น MultiLineString
  • จะมีตารางที่มี MultiLineStrings ประมาณ 1,000 (ปกติจะมีบรรทัดเดียวประมาณ 100 คะแนน)

พิจารณาวิธีการ

  • ดำเนินการกวาดแนวรัศมีด้วยการสร้างชุดของเส้นจากจุดที่รู้จัก (ที่, พูด, เพิ่มทีละ 1 องศา)
  • สร้างจุดตัดที่ใกล้ที่สุดของแต่ละบรรทัดกวาดเรเดียลด้วย MultiLineStrings
  • เมื่อหนึ่งในสายการกวาดแบบเรเดียลไม่ได้ตัดกับ MultiLineStrings ใด ๆ สิ่งนี้จะบ่งบอกถึงช่องว่างในขอบเขตซึ่งจะอยู่ในขอบเขตการก่อสร้าง 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}})

โดยทั่วไปแล้วการกวาดเรเดียลไม่มีความหมาย "การแก้ปัญหา": มันสแกนจาก "เหตุการณ์" หนึ่งไปยังลำดับถัดไปที่ซึ่งเหตุการณ์ประกอบด้วยโหนดดั้งเดิมของ polylines และจุดตัดร่วมกันของพวกเขา (ซึ่งสามารถพบได้แบบไดนามิกในขณะที่กวาดรอบต้นฉบับ โหนด) ผลลัพธ์ของมันจะแม่นยำอย่างสมบูรณ์แบบ
whuber

คำตอบ:


17

ฉันทำซ้ำตัวอย่างของคุณด้วย shapefiles

คุณสามารถใช้ShapelyและFionaเพื่อแก้ปัญหาของคุณ

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เพื่อสร้างradii (หมุนเส้นจากจุดดูคำตอบของMike Toewsที่Python, ไลบรารี shapely: เป็นไปได้ไหมที่จะทำการเลียนแบบรูปหลายเหลี่ยมรูปร่าง? ):

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) เดียวกันกับบรรทัดต้นฉบับ (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) การตัดกันระหว่างสองมัลติมิเตอร์ถูกคำนวณและผลลัพธ์จะถูกบันทึกลงในรูปร่างไฟล์:

 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 สำหรับอินพุต / เอาต์พุตผ่าน Shapefiles ฉันได้ผนวกรหัสบางส่วนกับคำถามของฉันซึ่งใช้คำตอบของคุณและแก้ไขเพื่อลดจำนวนการintersectionคำนวณที่ต้องการ - ขอบคุณ
สนิม Magoo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.