Не найдя решения, я закончил тем, что написал этот алгоритм. Это делает работу, но может лучше обрабатывать ветви, т.е. выберите ветку, которая дала бы самый длинный непрерывный путь. Теперь он просто прилипает к первому отрезку и продолжает оттуда.
Учитывая геометрию GeoJSON MultiLineString, алгоритм упорядочивает отрезки линии в непрерывный путь и возвращает новую геометрию.
Код лицензирован в соответствии с лицензией «Сделай, что, черт возьми, ты хочешь».
import math
from collections import namedtuple
from operator import attrgetter
from copy import deepcopy
def arrange_geometry(original_geometry):
def distance(coords1, coords2):
return math.sqrt(math.pow(coords1[0] - coords2[0], 2) + math.pow(coords1[1] - coords2[1], 2))
MinDistance = namedtuple('MinDistance', 'target distance offset reverse_target')
geometry = deepcopy(original_geometry)
if geometry['type'] == 'MultiLineString':
lines = geometry['coordinates']
sorted_multistring = [lines.pop(0)]
while lines:
min_distances = []
for line in lines:
source_a = sorted_multistring[0][0]
source_b = sorted_multistring[-1][-1]
target_a = line[0]
target_b = line[-1]
distances = [
MinDistance(target=line, distance=distance(source_b, target_a), offset=1, reverse_target=False),
MinDistance(target=line, distance=distance(source_a, target_a), offset=-1, reverse_target=True),
MinDistance(target=line, distance=distance(source_b, target_b), offset=1, reverse_target=True),
MinDistance(target=line, distance=distance(source_a, target_b), offset=-1, reverse_target=False)
]
min_distance = min(distances, key=attrgetter('distance'))
min_distances.append(min_distance)
min_distance = min(min_distances, key=attrgetter('distance'))
target = min_distance.target
if min_distance.reverse_target:
target.reverse()
if min_distance.offset == 1:
sorted_multistring.append(target)
else:
sorted_multistring.insert(0, target)
lines.remove(target)
geometry['coordinates'] = sorted_multistring
return geometry