Фон
Из известной точки мне требуется установить ближайший окружающий «видимый периметр» по таблице MultiLineStrings, как показано на диаграмме.
Я искал этот сайт с несколькими терминами (например, минимальный край, минимальный периметр, ближайший сосед, клип, содержащий многоугольник, видимость, привязка, узлы разреза, трассировка лучей, заливка, внутренняя граница, маршрутизация, вогнутый корпус), но не могу найти ни одного предыдущего вопроса, который, кажется, соответствует этому сценарию.
схема
- Зеленый круг - это известная точка.
- Черные линии - известные MultiLineStrings.
- Серые линии указывают на радиальную развертку от известной точки.
- Красные точки являются ближайшим пересечением радиальной развертки и MultiLineStrings.
параметры
- Точка никогда не пересечет MultiLineStrings.
- Точка всегда будет номинально центрирована в MultiLineStrings.
- MultiLineStrings никогда не будет полностью заключать точку, поэтому периметр будет MultiLineString.
- Будет таблица, содержащая приблизительно 1000 строк MultiLineStrings (обычно содержащая одну строку из приблизительно 100 точек).
Рассмотренная методология
- Выполните радиальную развертку, построив ряд линий из известной точки (скажем, с шагом 1 градус).
- Установите ближайшую точку пересечения каждой радиальной линии развертки с помощью MultiLineStrings.
- Когда одна из линий радиальной развертки не пересекается ни с одной из строк MultiLineString, это указывает на зазор в периметре, который будет размещен в конструкции периметра 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}})