У меня есть карта Google с кучей полигонов.
Вот проблема, которая меня интересует: учитывая точку широты, как лучше всего определить все полигоны, в которых эта точка лежит?
Очевидный способ - итеративный запуск алгоритма «точка в многоугольнике» для каждого многоугольника, но мне было интересно, существует ли эффективный алгоритм для ответа на такие запросы, особенно если у вас есть тысячи многоугольников.
Ответы:
Как и почти во всех таких вопросах, оптимальный подход зависит от «вариантов использования» и того, как представлены функции. Варианты использования обычно различаются тем, что (а) имеется ли много или мало объектов на каждом уровне, и (б) допускает ли один (или оба) уровня предварительный расчет некоторых структур данных; то есть, является ли один или оба из них достаточно статичными и неизменными, чтобы оправдать инвестиции в предварительные вычисления.
В данном случае это приводит к следующим сценариям. Обычно точки являются динамическими, то есть они не даны заранее. (Если они доступны заранее или в очень больших группах, будут доступны некоторые оптимизации, основанные на их сортировке.) Пусть Q - количество точек запроса, а P - число вершин многоугольника .
Данные векторного многоугольника
(1) Несколько точек, несколько вершин многоугольника в целом . Используйте метод грубой силы, такой как классический алгоритм линейного удара . Для любого приличного метода стоимость O (P * Q), потому что для сравнения точки с ребром многоугольника требуется O (1) время, и все такие сравнения должны быть сделаны.
(2) Возможно много вершин многоугольника, но они являются динамическими: каждый раз, когда точка используется в запросе, все многоугольники могли измениться. Снова используйте алгоритм перебора. Стоимость по-прежнему O (P * Q), которая будет большой, потому что P будет большим, но это не поможет. Если изменения невелики или контролируются ( например , полигоны слегка меняют форму или просто медленно перемещаются), вы можете использовать версию следующего решения и найти эффективный способ обновления структур данных при изменении полигонов. Скорее всего, это вопрос оригинального исследования.
(3) Многие вершины многоугольника и статические многоугольники (то есть слой многоугольника будет редко меняться). Предвычисления структуру данных для поддержки поиска (которые могут быть основаны на линии развертки или дерева квадрантов алгоритма). Стоимость предварительного вычисления для этих алгоритмов составляет O (P * log (P)), но стоимость запросов становится O (Q * log (P)), поэтому общая стоимость составляет O ((P + Q) * log ( П)).
Некоторые улучшения доступны в особых случаях , таких как
(а) Все многоугольники являются выпуклыми ( предварительная обработка многоугольников может быть выполнена быстрее ),
(b) Все внутренности многоугольников не пересекаются , и в этом случае вы можете думать об их объединении как о едином многоугольнике (который допускает прямые эффективные алгоритмы, такие как основанные на триангуляции, и
(c) Большинство полигонов не очень извилистые - то есть они занимают большие части своих ограничивающих прямоугольников - в этом случае вы можете выполнить первоначальный тест, основанный только на ограничивающих прямоугольниках, а затем уточнить это решение. Это популярная оптимизация.
(d) Количество очков большое. Сортировка их может улучшить сроки. Например, при реализации алгоритма «точка-в-многоугольнике» развертки линии слева направо вы должны отсортировать точки по их первой координате, что позволит вам проходить по точкам в то же время, когда вы проходите по краям многоугольника. Я не в курсе, что такая оптимизация была опубликована. Тем не менее, одна из опубликованных публикаций - выполнить ограниченную триангуляцию объединения всех точек и вершин многоугольника: как только эта триангуляция завершена, идентификация внутренних точек должна быть быстрой. Вычислительные затраты будут масштабироваться как O (Q * log (Q) + (P + Q) * log (P + Q)).
Растровые данные многоугольника
Это невероятно просто: рассматривать слой многоугольника как растр двоичного индикатора (1 = внутри многоугольника, 0 = снаружи). (Для этого может потребоваться таблица поиска для преобразования растровых значений во внутренние / внешние индикаторы.) Теперь для каждого точечного зонда требуется усилие O (1) для индексации растровой ячейки и считывания ее значения. Общее усилие составляет O (Q).
В общем
Хорошее гибридное решениев случае многих статических векторных многоугольников (векторный случай 3 выше) первоначально необходимо растеризовать многоугольники, возможно даже с грубым разрешением, на этот раз различая любые ячейки, пересекающие любую часть границы многоугольника (например, дайте им значение 2) , Использование растрового зонда (стоимость: O (1)) обычно приводит к определенному ответу (известно, что точка находится внутри или снаружи), но иногда приводит к неопределенному ответу (точка попадает в ячейку, через которую по крайней мере один край проходит), и в этом случае выполняется более дорогой векторный запрос O (log (P)). Этот метод требует дополнительных затрат на хранение растра, но во многих случаях даже небольшой растр (один МБ позволит использовать растр 2000 на 2000, который хранит значения {0,1,2, ноль}) может дать огромные преимущества в вычислительном времени. , Асимптотически,
источник
Если у вас были ограничивающие прямоугольники, хранящиеся в чем-то вроде четырехугольного дерева, вы могли бы использовать это, чтобы быстро определить, какие полигоны проверять. Как минимум, вы можете просто увидеть, находится ли точка внутри каждого ограничивающего прямоугольника, в отличие от создания полной точки в многоугольнике для каждого многоугольника. Лично я бы настроил веб-сервис, который бы кэшировал полигоны в памяти и использовал что-то вроде JTS или пакета NetTopology для выполнения запроса на пересечение.
источник
В postgis ST_Intersects использует индексы, чтобы сначала найти, находится ли точка внутри ограничительной рамки многоугольника, а затем повторно проверяет, находится ли она внутри многоугольника. Это быстро, часто очень быстро.
Если вы сохранили свои данные в PostGIS, не должно быть никаких сомнений в том, что база данных является подходящим местом для проведения вычислений. В других случаях вам придется отправлять свои полигоны в какую-то среднюю или клиентскую программу. Это само по себе займет гораздо больше времени, чем выполнение вычислений и просто получение соответствующих полигонов.
/ Никлас
источник