Лучше "центральная точка", чем центроид

Я использую центроид многоугольников, чтобы прикрепить маркер в приложении карты. Это определенно хорошо работает для выпуклых многоугольников и неплохо для многих вогнутых многоугольников.

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

Кто-нибудь знает, как лучше найти подходящую точку в в любой области полигонов (которая может содержать дыры!) Для прикрепления маркера?

Лучше "центральная точка", чем центроид

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

NaN 29.05.2018 17:34

Я думаю, это можно сделать с помощью триангуляции. Например, на первом этапе триангулируйте многоугольник, а затем попытайтесь найти треугольник, который кажется центральным треугольником в желаемых критериях, и верните его центроид.

vahidreza 29.05.2018 17:35

Я бы разделил вогнутый многоугольник на выпуклые части (возможно, он уже так представлен), взял бы «самый толстый» из них (с большим абсолютным диаметром и отношением максимального диаметра к минимальному диаметру, близким к 1), и поместил точку в центр этой части. Однако это может потребовать значительного объема вычислений, поэтому я бы подумал о кешировании результатов или их сохранении вместе с многоугольниками.

9000 29.05.2018 17:41

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

Christophe Roussy 29.05.2018 17:43

@ChristopheRoussy: Совершенно верно!

user2033412 29.05.2018 17:46

У Географические информационные системы могут быть похожие вопросы, поэтому ищите и там.

Toby Speight 29.05.2018 18:28

Я создал аналогичный вопрос о корпусе. Если вы знаете какой-либо подход, вы можете любезно ответить на него. Благодарить.

javaLover 17.10.2019 05:21
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
7
1 123
6

Ответы 6

Я понятия не имею, как решить эту проблему для любой возможной формы (и не выполнять тяжелые вычисления), но, возможно, для более простых форм, подобных тем, которые вы показали:

https://en.wikipedia.org/wiki/Force-directed_graph_drawing

Эвристический: через некоторое время это может сойтись к разумному приближению

  • преобразовать границу формы во множество точек (больше = точнее)
  • начните с множества случайных точек внутри многоугольника
  • толкайте их, пока они не будут дальше всего от пограничных точек, или просто вычислите расстояние ... (можно сделать параллельно)
  • возьми лучшую точку

Другой способ - использовать несколько алгоритмов в зависимости от характера формы (например, другой для пончиков ...). Также, возможно, полагаться в первую очередь на измерение «самых толстых» участков?

ИМХО спросит об этом на математическом форуме.

Аналогично: Вычислить центроид ВНУТРИ / ВНУТРИ пространственного многоугольника

Аналогично: Как найти две самые далекие точки?

for (int i = 0; i < n; /*++i*/) {
    p = RandomPointInsideConvexHull();
    if (IsInsidePolygon(p)) {
        ++i;
        d = DistanceToClosestEdge(p);
        if (d > bestD) {
            bestP = p;
        }
    }
}

После запуска этого цикла вы получите приблизительное решение по bestP. n - параметр для выбора. Если вам нужен более точный результат, вы можете перезапустить поиск, но теперь вместо того, чтобы выбирать точку внутри выпуклой оболочки многоугольника, вы можете выбрать точку в районе bestP, скажем, не дальше, чем bestD / 5 (на этот раз вам не нужно проверять, есть ли случайная точка находится внутри многоугольника).

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

Adam Gawne-Cain 25.05.2019 10:56

@ AdamGawne-Cain, пожалуйста, проверьте условие, при котором увеличивается i. Хотя это не самый эффективный алгоритм для упомянутых вами типов полигонов.

Yola 25.05.2019 13:58

Ваш метод хорош тем, что он найдет интересную точку во многих полигонах. Но OP хотел метод для "любого многоугольника". Ваш код будет работать вечно, если входной многоугольник является вырожденным (например, L-образная форма с площадью = 0, например {0 0, 1 0, 1 1, 1 0, 0 0}).

Adam Gawne-Cain 25.05.2019 17:38

@ AdamGawne-Cain Хм ... верно, но многоугольник с нулевой площадью не имеет точек, которые строго являются в многоугольника;)

Yola 25.05.2019 19:39

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

В библиотеке CGAL есть модуль 2D прямой каркас и смещение многоугольника, или вы могли бы, например, использовать PostGIS.

Итак, если каждому контуру в процессе непрерывной усадки придается увеличивающаяся высота, возьмите наивысшую точку.

Will Ness 29.05.2018 21:11

@Will - это один из подходов (и он даст вам точку, наиболее удаленную от любого края). Для многоугольника в форме гантели вы можете захотеть, чтобы алгоритм выбирал точку на «полосе» - в этом случае вы должны предпочесть (возможно, взвешенное) среднее положение вдоль позвоночника скелета.

Toby Speight 30.05.2018 09:53

Перефразируя комментарий Кристофа Русси, мы можем искать самый большой круг внутри многоугольника.

Самый большой круг - это тот, который больше не может расти, потому что он касается трех вершин или ребер (если он касается только двух, он может стать больше или просто перемещаться, пока не коснется третьего).

Итак, если у вас мало вершин, вы можете просто перечислить все возможные тройки вершин / ребер, найти для каждой из них круг, а затем выбрать самую большую.

Но для этого потребуется создать четыре функции:

  1. Круг (вершина, вершина, вершина)
  2. Круг (вершина, вершина, ребро)
  3. Круг (вершина, ребро, ребро)
  4. Круг (край, край, край)

Все они возможны, но могут потребовать некоторых усилий.

Найдите крайние ординаты и проведите горизонтальную линию посередине. Гарантированно пересечение полигона.

Найдите пересечение со сторонами и отсортируйте их по возрастанию абсцисс. Выберите точку посередине двух пересечений.

Это процесс O (N + K Log K), где K - количество пересечений (обычно очень маленькое четное число). Писать довольно просто.

Чтобы увеличить шансы на удачное размещение, вы можете попробовать три горизонтали вместо одной и выбрать самый длинный сегмент пересечения.

Этот метод может не сработать, если внутренняя часть многоугольника не соединена и пересечение горизонтальной линии и многоугольника лежит на самопересечении многоугольника. Например, многоугольник может быть цифрой 8, а горизонтальная линия может пересекать талию восьмерки.

Adam Gawne-Cain 25.05.2019 11:02

@ AdamGawne-Cain: нет, это работает. Вы получите две перекрывающиеся точки пересечения, определяющие вырожденный сегмент.

Yves Daoust 25.05.2019 12:15

Я согласен, что вы получите точку, но это не будет строго в многоугольник, как запрашивал OP. На самом деле, я думаю, что ваш метод подходит для получения точки для маркера, поскольку маркер, вероятно, больше точки и может быть больше, чем все пространство внутри многоугольника.

Adam Gawne-Cain 25.05.2019 17:28

Чтобы получить точку для маркера, я бы использовал метод Ива Дауста.

Чтобы получить точку, которая надежно находится «внутри любого многоугольника с отверстиями», я бы разделил многоугольник на треугольники с помощью надежной библиотеки (например, OpenGL GLUtessellator), а затем получил бы центроид треугольника с наибольшей площадью.

Если бы у меня было время на разработку и тестирование, и я хотел бы получить хорошую производительность, я бы использовал гибридный метод: сначала используйте метод Ива Дауста, чтобы получить некоторые кандидатские очки, а затем протестируйте кандидатов, чтобы увидеть, находятся ли они в пределах многоугольника. Если все кандидаты терпят неудачу, используйте более медленный и надежный метод (например, GLUtesselator).

Другие вопросы по теме