Поиск центра полигона Листовки?
У меня есть куча листовых многоугольников на карте, которую я создал. Каждый многоугольник представляет собой нечто иное. Конкретный набор информации отображается во всплывающем меню в зависимости от страницы, на которой пользователь включен. Мне нужно найти способ сделать всплывающий пузырь открытым в центре многоугольника, который он представляет.
Каждый многоугольник рисуется с использованием следующего кода:
var L20 = [
[74.0995, -99.92615],
[74.14008, -99.4043],
[74.07691, -99.33838],
[74.03617, -99.86023]
];
var L19 = [
[74.02559, -99.84924],
[74.06636, -99.32739],
[74.0029, -99.26147],
[73.96197, -99.77783]
];
var L18 = [
[73.95142, -99.76684],
[73.99235, -99.25048],
[73.92889, -99.18456],
[73.8878, -99.69543]
];
var set1 = L.polygon([L20, L19, L18], {
color: "#fff",
weight: 1,
stroke: true,
opacity: 0.05,
fillColor: "#346B1F",
}).addTo(map);
Всплывающее окно рисуется с использованием следующего кода:
var popup = L.popup({})
.setLatLng([73.64017, -100.32715])
.setContent(content).openOn(map);
var popup = L.popup();
Итак, мне нужно найти способ для .setLatLang
определить или задать центр многоугольника.
Я придумал 3 решения, которые могут работать, а не уверен, как это сделать.
-
найти способ использования координат многоугольника для определения центра многоугольника, в котором всплывающее окно откроется.
-
вызовите одну точку многоугольника, затем смещайте положение всплывающего окна.
-
Используйте идентификатор для каждого многоугольника, поэтому каждому всплывающему окну известно, что область окна (многоугольник) может быть открыта.
Кто-нибудь может мне помочь?
Ответы
Ответ 1
Существует несколько способов аппроксимировать центроид многоугольника.
Самый простой (но наименее точный метод) - получить центр ограничивающей рамки, содержащий многоугольник, как предложил ярл, используя polygon.getBounds().getCenter();
Я первоначально ответил на вопрос формулой для нахождения центроида точек, который можно найти, усредняя координаты его вершин.
var getCentroid = function (arr) {
return arr.reduce(function (x,y) {
return [x[0] + y[0]/arr.length, x[1] + y[1]/arr.length]
}, [0,0])
}
centerL20 = getCentroid(L20);
Хотя центроид точек является достаточно близким приближением, чтобы обмануть меня, комментатор указал, что это не центр тяжести многоугольника.
Реализация, основанная на формуле для центра несамопересекающегося замкнутого многоугольника, дает правильный результат:
var getCentroid2 = function (arr) {
var twoTimesSignedArea = 0;
var cxTimes6SignedArea = 0;
var cyTimes6SignedArea = 0;
var length = arr.length
var x = function (i) { return arr[i % length][0] };
var y = function (i) { return arr[i % length][1] };
for ( var i = 0; i < arr.length; i++) {
var twoSA = x(i)*y(i+1) - x(i+1)*y(i);
twoTimesSignedArea += twoSA;
cxTimes6SignedArea += (x(i) + x(i+1)) * twoSA;
cyTimes6SignedArea += (y(i) + y(i+1)) * twoSA;
}
var sixSignedArea = 3 * twoTimesSignedArea;
return [ cxTimes6SignedArea / sixSignedArea, cyTimes6SignedArea / sixSignedArea];
}
Ответ 2
С некоторого времени Leaflet имеет встроенный метод getCenter():
polygon.getBounds().getCenter();
Ответ 3
Проблема, которую вы пытаетесь решить, называется проблемой полюса недоступности. Поиск наилучшего места для нанесения метки в многоугольник не полностью решен путем нахождения центра ограничивающей рамки. Рассмотрим многоугольник в форме буквы U. Центр ограничивающей коробки помещает метку за пределы многоугольника. Мне потребовалась вечность, чтобы найти эту замечательную библиотеку: https://github.com/mapbox/polylabel
Из README.MD:
Быстрый алгоритм поиска многоугольника полюса недоступности, самая дальняя внутренняя точка из контура многоугольника (не путать с центроидом), реализованная как библиотека JavaScript. Полезно для оптимального размещения текстовой метки на многоугольнике.
Это итеративный алгоритм сетки, вдохновленный бумагой Гарсиа-Кастелланоса и Ломбардо, 2007. В отличие от той, что в статье, этот алгоритм:
- гарантирует обнаружение глобального оптимума в заданной точности
- во много раз быстрее (10-40x)
Использование:
Учитывая координаты полигона в формате GeoJSON и точности (по умолчанию 1,0), Polylabel возвращает полюс координаты недоступности в формате [x, y].
var p = polylabel(polygon, 1.0);
![Полюс недоступности]()
Как работает алгоритм:
Это итеративный алгоритм на основе сетки, который начинается с покрытия многоугольника большими квадратными ячейками, а затем итеративно раскалывает их в порядке наиболее перспективных, а агрессивно обрезает неинтересные ячейки.
- Генерировать начальные квадратные ячейки, которые полностью покрывают многоугольник (размер ячейки равен ширине или высоте, в зависимости от того, что меньше). Вычислите расстояние от центра каждой ячейки до внешнего многоугольника, используя отрицательное значение, если точка находится за пределами многоугольника (обнаружена лучей).
- Поместите ячейки в очередь приоритетов, отсортированную по максимальному потенциальному расстоянию от точки внутри ячейки, определяемой как сумма расстояния от центра и радиуса ячейки (равная cell_size * sqrt (2)/2).
- Рассчитайте расстояние от центра тяжести многоугольника и выберите его как первое "лучшее до сих пор".
- Вытаскивать ячейки из очереди приоритетов поочередно. Если расстояние между ячейками лучше, чем текущее, сохраните его как таковое. Затем, если ячейка потенциально содержит лучшее решение, которое соответствует текущей лучшей (cell_max - best_dist > точности), разделите ее на 4 дочерние ячейки и поместите их в очередь.
- Остановите алгоритм, когда мы исчерпали очередь и вернем лучший центр ячейки как полюс недоступности. Это гарантировано будет глобальным оптимумом в пределах данной точности.
![Самая удаленная внутренняя точка из контура многоугольника]()
Ответ 4
предполагая, что каждый многоугольник имеет только 4 стороны, он просто
var L20 = [
[74.0995, -99.92615],
[74.14008, -99.4043],
[74.07691, -99.33838],
[74.03617, -99.86023]
];
используя этот пример, получите max и min lat: 74.03617 и 74.14008 соответственно так же долго: -99.92615 и 99.33838 соответственно
Затем получите среднее значение для каждого: (max - min)/2 = 0,051955 и -0,293885, затем добавьте их к минимальному количеству
дает вам центр 74.088125, -99.632265