Düzgün kullanarak Point'e En Yakın Çizgi Segmentlerini mi Buluyorsunuz?


17

Arka fon

Bilinen bir noktadan, diyagramda gösterildiği gibi bir MultiLineStrings tablosuna en yakın çevreleyen "görünür çevre" yi kurmam gerekiyor.

Bu siteyi bir dizi terimle aradım (örneğin minimum kenar, minimum çevre, en yakın komşu, klip, çokgen, görünürlük, çıtçıt, kesim düğümleri, ışın izi, taşkın dolgu, iç sınır, yönlendirme, içbükey gövde) bu senaryo ile eşleşmiş gibi görünen herhangi bir soru bulamıyor

Diyagram

  • Yeşil daire bilinen noktadır.
  • Siyah çizgiler bilinen MultiLineStrings'tir.
  • Gri çizgiler bilinen Noktadan radyal bir süpürmenin bir göstergesidir.
  • Kırmızı noktalar, radyal süpürmenin ve MultiLineStrings'in en yakın kesişimidir.

resim açıklamasını buraya girin

Parametreler

  • Nokta asla MultiLineStrings ile kesişmez.
  • Nokta her zaman nominal olarak MultiLineStrings içinde ortalanır.
  • MultiLineStrings hiçbir zaman Noktayı tam olarak kapsamaz, bu nedenle çevre bir MultiLineString olacaktır.
  • Yaklaşık 1.000 MultiLineStrings içeren bir tablo olacaktır (normalde yaklaşık 100 puntoluk tek bir çizgi içerir).

Dikkate Alınan Metodoloji

  • Bilinen Noktadan (örneğin 1 derecelik artışlarla) bir dizi çizgi oluşturarak radyal bir tarama yapın.
  • MultiLineStrings ile her radyal süpürme hattının en yakın kesişim noktasını belirleyin.
  • Radyal süpürme hatlarından biri MultiLineStrings'in herhangi biriyle kesişmediğinde, bu çevre çevresinde MultiLineString yapısında kalacak bir boşluğu gösterir.

özet

Bu teknik en yakın kavşakları bulsa da, radyal süpürmenin çözünürlüğüne bağlı olarak en yakın çevre düğüm noktalarının hepsini bulamaz. Herkes tüm çevre noktalarını belirlemek veya radyal tarama tekniğini bir çeşit tamponlama, sektör oluşturma veya ofsetleme ile desteklemek için alternatif bir yöntem önerebilir mi?

Yazılım

Benim tercihim çözüm için SpatiaLite ve / veya Shapely kullanmak ama açık kaynak yazılım kullanılarak uygulanabilecek önerileri memnuniyetle karşılıyoruz.

Düzenleme: Çalışma Çözümü (@gene tarafından verilen cevaba göre)

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

Normalde radyal bir süpürmenin anlamlı bir "çözünürlüğü" yoktur: bir "olaydan" diğerine, olayların çoklu çizgilerin orijinal düğümlerinden ve karşılıklı kesişimlerinden oluştuğu (orijinalin etrafında gezinirken dinamik olarak bulunabilir) sırayla tarar düğümleri). Çıkışı mükemmel şekilde doğru olacaktır.
whuber

Yanıtlar:


17

Örneğini şekil dosyaları ile çoğalttım.

Sen kullanabilirsiniz düzgün ve Fiona sorunu çözmek için.

1) Sorununuz (düzgün bir şekilde Point):

resim açıklamasını buraya girin

2) keyfi bir çizgiden başlayarak (yeterli uzunlukta):

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

resim açıklamasını buraya girin

3) yarıçapları oluşturmak için shapely.affinity.rotate komutunu kullanarak (çizgiyi noktadan döndürerek, Mike Toews'in Python'daki cevabına da bakın , düzgün kütüphane: şekil çokgeninde afin işlemi yapmak mümkün mü? ):

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

resim açıklamasını buraya girin

4) şimdi, bir MultiLineString elde etmek için shapely : cascaded_union (veya shapely: unary_union ) kullanarak:

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

5) orijinal çizgilerle aynı (şekil dosyası)

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) iki multigeometri arasındaki kesişme hesaplanır ve sonuç bir şekil dosyasına kaydedilir:

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

Sonuç:

resim açıklamasını buraya girin

7) ancak sorun, daha uzun bir yarıçap kullanırsanız, sonuç farklıdır:

resim açıklamasını buraya girin

8) Ve ​​sonucunuzu almak istiyorsanız, yalnızca bir yarıçaptaki orijinal noktadan en kısa mesafeyi seçmeniz gerekir:

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

Son sonuç:

resim açıklamasını buraya girin

Umarım istediğin budur.


1
Mükemmel cevap - özellikle şekil dosyaları aracılığıyla giriş / çıkış için Fiona kullanımı konusunda bilgilendirici. Soruma cevabınızı kullanan bir kod ekledim ve intersectiongerekli hesaplama miktarını azaltmak için değiştirdim - teşekkürler.
Rusty Magoo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.