Фон
З відомої точки я вимагаю встановити найближчий оточуючий "видимий периметр" проти таблиці MultiLineStrings, як показано на схемі.
Я шукав цей сайт за допомогою декількох термінів (наприклад, мінімальний край, мінімальний периметр, найближчий сусід, кліп, що містить багатокутник, видимість, оснащення, вирізані вузли, променева стежка, заливна заливка, внутрішня межа, маршрутизація, увігнутий корпус), не вдається знайти жодне попереднє запитання, яке, схоже, відповідає цьому сценарію.
Діаграма
- Зелене коло - відома Точка.
- Чорні лінії - це відомі MultiLineStrings.
- Сірі лінії є вказівкою на радіальну розгортку з відомої точки.
- Червоні точки є найближчим перетином променевої розгортки та MultiLineStrings.
Параметри
- Точка ніколи не перетинатиме MultiLineStrings.
- Точка завжди буде номінально зосереджена в MultiLineStrings.
- MultiLineStrings ніколи не повністю закриє точку, тому периметр буде MultiLineString.
- Буде таблиця, що містить приблизно 1000 MultiLineStrings (як правило, містить один рядок приблизно 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}})