Поиск центра полигона Листовки?

У меня есть куча листовых многоугольников на карте, которую я создал. Каждый многоугольник представляет собой нечто иное. Конкретный набор информации отображается во всплывающем меню в зависимости от страницы, на которой пользователь включен. Мне нужно найти способ сделать всплывающий пузырь открытым в центре многоугольника, который он представляет.

Каждый многоугольник рисуется с использованием следующего кода:

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