Получение координат ограниченного многоугольника из ячеек Вороного

У меня есть очки (например, широта, пара пар местоположений вышек сотовой связи), и мне нужно получить многоугольник ячеек Вороного, которые они образуют.

from scipy.spatial import Voronoi

tower = [[ 24.686 ,  46.7081],
       [ 24.686 ,  46.7081],
       [ 24.686 ,  46.7081]]

c = Voronoi(towers)

Теперь мне нужно получить границы многоугольника в широтах, координатах долготы для каждой ячейки (и какой центр тяжести окружает этот многоугольник). Мне нужно, чтобы этот Вороной тоже был ограничен. Это означает, что границы не уходят в бесконечность, а скорее в ограничивающую рамку.

Ответы на вопрос(1)

Решение Вопроса

моей первой идеей было определить вид операции пересечения между этой ограничительной рамкой и диаграммой Вороного, полученной с помощьюscipy.spatial.Voronoi, Идея не обязательно велика, так как для этого требуется закодировать большое количество основных функций вычислительной геометрии.

Однако мне пришла в голову вторая идея (хак?): Алгоритмы для вычисления диаграммы Вороного из набораn точки в плоскости имеют временную сложностьO(n ln(n)), Как насчет добавления точек, чтобы ограничить ячейки Вороного начальных точек, чтобы они лежали в ограничительной рамке?

Решение для ограниченной диаграммы Вороного

Картинка стоит большой речи:

Что я здесь сделал? Это довольно просто! Начальные точки (синим цветом) лежат в[0.0, 1.0] x [0.0, 1.0], Затем я получаю очки (синим цветом) слева (т.е.[-1.0, 0.0] x [0.0, 1.0]) симметрией отражения согласноx = 0.0 (левый край ограничительной рамки). С симметриями отражения в соответствии сx = 1.0, y = 0.0 а такжеy = 1.0 (другие края ограничительной рамки), я получаю все точки (синим цветом), которые мне нужны для выполнения работы.

Тогда я бегуscipy.spatial.Voronoi, Предыдущее изображение изображает полученную диаграмму Вороного (я используюscipy.spatial.voronoi_plot_2d).

Что делать дальше? Просто отфильтруйте точки, ребра или грани в соответствии с ограничивающей рамкой. И получить центр тяжести каждого лица в соответствии с известной формулой для вычисленияцентр тяжести многоугольника, Вот изображение результата (центроиды выделены красным):

Немного веселья перед показом кода

Большой! Вроде работает. Что если после одной итерации я попытаюсь перезапустить алгоритм на центроидах (красным), а не на начальных точках (синим)? Что если я попробую это снова и снова?

Шаг 2

Шаг 10

Шаг 25

Здорово! Вороные клетки имеют тенденцию минимизировать ихэнергия...

Вот код
import matplotlib.pyplot as pl
import numpy as np
import scipy as sp
import scipy.spatial
import sys

eps = sys.float_info.epsilon

n_towers = 100
towers = np.random.rand(n_towers, 2)
bounding_box = np.array([0., 1., 0., 1.]) # [x_min, x_max, y_min, y_max]

def in_box(towers, bounding_box):
    return np.logical_and(np.logical_and(bounding_box[0] <= towers[:, 0],
                                         towers[:, 0] <= bounding_box[1]),
                          np.logical_and(bounding_box[2] <= towers[:, 1],
                                         towers[:, 1] <= bounding_box[3]))


def voronoi(towers, bounding_box):
    # Select towers inside the bounding box
    i = in_box(towers, bounding_box)
    # Mirror points
    points_center = towers[i, :]
    points_left = np.copy(points_center)
    points_left[:, 0] = bounding_box[0] - (points_left[:, 0] - bounding_box[0])
    points_right = np.copy(points_center)
    points_right[:, 0] = bounding_box[1] + (bounding_box[1] - points_right[:, 0])
    points_down = np.copy(points_center)
    points_down[:, 1] = bounding_box[2] - (points_down[:, 1] - bounding_box[2])
    points_up = np.copy(points_center)
    points_up[:, 1] = bounding_box[3] + (bounding_box[3] - points_up[:, 1])
    points = np.append(points_center,
                       np.append(np.append(points_left,
                                           points_right,
                                           axis=0),
                                 np.append(points_down,
                                           points_up,
                                           axis=0),
                                 axis=0),
                       axis=0)
    # Compute Voronoi
    vor = sp.spatial.Voronoi(points)
    # Filter regions
    regions = []
    for region in vor.regions:
        flag = True
        for index in region:
            if index == -1:
                flag = False
                break
            else:
                x = vor.vertices[index, 0]
                y = vor.vertices[index, 1]
                if not(bounding_box[0] - eps <= x and x <= bounding_box[1] + eps and
                       bounding_box[2] - eps <= y and y <= bounding_box[3] + eps):
                    flag = False
                    break
        if region != [] and flag:
            regions.append(region)
    vor.filtered_points = points_center
    vor.filtered_regions = regions
    return vor

def centroid_region(vertices):
    # Polygon's signed area
    A = 0
    # Centroid's x
    C_x = 0
    # Centroid's y
    C_y = 0
    for i in range(0, len(vertices) - 1):
        s = (vertices[i, 0] * vertices[i + 1, 1] - vertices[i + 1, 0] * vertices[i, 1])
        A = A + s
        C_x = C_x + (vertices[i, 0] + vertices[i + 1, 0]) * s
        C_y = C_y + (vertices[i, 1] + vertices[i + 1, 1]) * s
    A = 0.5 * A
    C_x = (1.0 / (6.0 * A)) * C_x
    C_y = (1.0 / (6.0 * A)) * C_y
    return np.array([[C_x, C_y]])

vor = voronoi(towers, bounding_box)

fig = pl.figure()
ax = fig.gca()
# Plot initial points
ax.plot(vor.filtered_points[:, 0], vor.filtered_points[:, 1], 'b.')
# Plot ridges points
for region in vor.filtered_regions:
    vertices = vor.vertices[region, :]
    ax.plot(vertices[:, 0], vertices[:, 1], 'go')
# Plot ridges
for region in vor.filtered_regions:
    vertices = vor.vertices[region + [region[0]], :]
    ax.plot(vertices[:, 0], vertices[:, 1], 'k-')
# Compute and plot centroids
centroids = []
for region in vor.filtered_regions:
    vertices = vor.vertices[region + [region[0]], :]
    centroid = centroid_region(vertices)
    centroids.append(list(centroid[0, :]))
    ax.plot(centroid[:, 0], centroid[:, 1], 'r.')

ax.set_xlim([-0.1, 1.1])
ax.set_ylim([-0.1, 1.1])
pl.savefig("bounded_voronoi.png")

sp.spatial.voronoi_plot_2d(vor)
pl.savefig("voronoi.png")
 Flabetvibes25 сент. 2018 г., 18:17
И вы правы насчет второго вопроса.
 duhaime25 сент. 2018 г., 16:32
почему вы вычитаете эпсилон при проверке, находятся ли точки внутри ограничительной рамки? Чтобы ошибки округления с плавающей запятой не приводили к тому, чтобы точка появлялась за пределами ограничительной рамки, когда она на самом деле внутри?
 Flabetvibes25 сент. 2018 г., 18:17
Чтобы ответить на ваш первый вопрос: да, это вычитание присутствует только для обработки ошибок округления с плавающей запятой. Если я хорошо помню, я начал без, но не смог получить ожидаемый результат. Если вы попробуете код без вычитания, скажите мне, работает ли он правильно.
 duhaime25 сент. 2018 г., 18:02
Кроме того, почему вы умножаете А на 6,0 при получении координат центроида? Любая помощь, которую вы можете предложить по этим вопросам, будет принята с благодарностью!
 duhaime25 сент. 2018 г., 18:04
Ах, это отвечает на мой второй вопрос:en.wikipedia.org/wiki/Centroid#Of_a_polygon
 Energya28 мая 2019 г., 02:22
Большое спасибо за этот код и понимание! У меня есть улучшение для вашей процедуры, хотя: по построению регионы, которые вы хотите сохранить, относятся к первой 1/5 части баллов, которые вы далиscipy.spatial.Voronoi(), Это означает, что вы можете использоватьVoronoi.point_region атрибут, который дает для каждой точки индекс региона, к которому она принадлежит:vor.filtered_regions = np.array(vor.regions)[vor.point_region[:vor.npoints//5]], Это а) экономит кучу проверок вручную и б) гарантирует, что регионы возвращаются в том же порядке, что и их точки совпадения (что было крайне важно для моего варианта использования :))

Ваш ответ на вопрос