Hintergrund
Von einem bekannten Punkt aus muss der nächstgelegene "sichtbare Umfang" anhand einer Tabelle mit MultiLineStrings ermittelt werden, wie in der Abbildung dargestellt.
Ich habe diese Site mit einer Reihe von Begriffen durchsucht (z. B. Mindestkante, Mindestumfang, nächster Nachbar, Clip mit Polygon, Sichtbarkeit, Fang, Schnittknoten, Ray-Trace, Überflutungsfüllung, innere Begrenzung, Routing, konkaver Rumpf), aber Ich kann keine vorherige Frage finden, die diesem Szenario zu entsprechen scheint.
Diagramm
- Der grüne Kreis ist der bekannte Punkt.
- Die schwarzen Linien sind die bekannten MultiLineStrings.
- Die grauen Linien sind ein Hinweis auf eine radiale Abweichung vom bekannten Punkt.
- Die roten Punkte sind der nächstgelegene Schnittpunkt von Radial Sweep und MultiLineStrings.
Parameter
- Der Punkt schneidet niemals die MultiLineStrings.
- Der Punkt wird immer nominal in den MultiLineStrings zentriert.
- Die MultiLineStrings werden den Punkt niemals vollständig einschließen, daher wird der Umfang eine MultiLineString sein.
- Es wird eine Tabelle mit ungefähr 1.000 MultiLineStrings geben (normalerweise mit einer einzelnen Linie von ungefähr 100 Punkten).
Betrachtete Methodik
- Führen Sie einen Radial-Sweep durch, indem Sie eine Reihe von Linien vom bekannten Punkt aus konstruieren (z. B. in Schritten von 1 Grad).
- Stellen Sie mit den MultiLineStrings den nächstgelegenen Schnittpunkt jeder radialen Sweep-Linie fest.
- Wenn eine der radialen Sweep-Linien keine der MultiLineStrings schneidet, weist dies auf eine Lücke im Umfang hin, die in der MultiLineString-Umfangskonstruktion Platz findet.
Zusammenfassung
Während diese Technik die nächsten Schnittpunkte findet, werden abhängig von der Auflösung des radialen Sweeps nicht unbedingt alle nächsten Umfangsknotenpunkte gefunden. Kann jemand eine alternative Methode empfehlen, um alle Umfangspunkte festzulegen oder die Radial-Sweep-Technik durch eine Art Pufferung, Sektorierung oder Versetzung zu ergänzen?
Software
Ich bevorzuge die Verwendung von SpatiaLite und / oder Shapely für die Lösung, würde jedoch alle Vorschläge begrüßen, die mit Open-Source-Software umgesetzt werden könnten.
Bearbeiten: Arbeitslösung (basierend auf der Antwort von @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}})