sfondo
Da un punto noto, ho bisogno di stabilire il "perimetro visibile" circostante più vicino su una tabella di MultiLineStrings, come mostrato nel diagramma.
Ho cercato questo sito con una serie di termini (ad es. Bordo minimo, perimetro minimo, vicino più vicino, clip, contenente poligono, visibilità, snap, nodi di taglio, ray-trace, riempimento di alluvione, bordo interno, percorso, scafo concavo) ma non riesco a trovare alcuna domanda precedente che sembra corrispondere a questo scenario.
Diagramma
- Il cerchio verde è il punto noto.
- Le linee nere sono note MultiLineStrings.
- Le linee grigie sono un'indicazione di uno sweep radiale dal punto noto.
- I punti rossi sono l'intersezione più vicina dello sweep radiale e MultiLineStrings.
parametri
- Il punto non intersecherà mai MultiLineStrings.
- Il punto sarà sempre centrato nominalmente all'interno di MultiLineStrings.
- Le MultiLineString non racchiuderanno mai completamente il Punto, pertanto il perimetro sarà una MultiLineString.
- Ci sarà una tabella contenente circa 1.000 MultiLineStrings (normalmente contenente una sola riga di circa 100 punti).
Metodologia considerata
- Intraprendi una radiale costruendo una serie di linee dal Punto noto (ad esempio, con incrementi di 1 grado).
- Stabilire il punto di intersezione più vicino di ciascuna linea di sweep radiale con MultiLineStrings.
- Quando una delle linee di sweep radiali non si interseca con nessuna delle MultiLineString, ciò indicherebbe uno spazio nel perimetro che verrebbe sistemato nella costruzione del MultiLineString perimetrale.
Sommario
Mentre questa tecnica troverà le intersezioni più vicine, non troverà necessariamente tutti i punti del nodo perimetrale più vicini, a seconda della risoluzione dello sweep radiale. Qualcuno può raccomandare un metodo alternativo per stabilire tutti i punti perimetrali o integrare la tecnica di sweep radiale con una qualche forma di buffering, settaggio o compensazione?
Software
La mia preferenza è quella di utilizzare SpatiaLite e / o Shapely per la soluzione, ma gradirei qualsiasi suggerimento che possa essere implementato utilizzando software open source.
Modifica: soluzione di lavoro (basata sulla risposta di @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}})