Як поєднати складні багатокутники?


83

Дано два багатокутники:

POLYGON((1 0, 1 8, 6 4, 1 0))
POLYGON((4 1, 3 5, 4 9, 9 5, 4 1),(4 5, 5 7, 6 7, 4 4, 4 5))

Як я можу обчислити об’єднання (об’єднаний багатокутник)?

введіть тут опис зображення

Приклад Дейва використовує SQL-сервер для створення об'єднання, але мені потрібно виконати те саме в коді. Я шукаю математичну формулу чи приклад коду будь-якою мовою, яка відображає фактичну математику. Я намагаюся створити карти, які динамічно поєднують країни в регіони. Тут я поставив відповідне запитання: Групування географічних форм

Відповіді:


69

Це дуже гарне запитання. Я реалізував той самий алгоритм на c # деякий час тому. Алгоритм будує загальний контур з двох багатокутників (тобто будує об’єднання без дірок). Ось.


Мета

Крок 1. Створіть графік, що описує багатокутники.

Вхідні дані: перший багатокутник (n очок), другий багатокутник (m очок). Вихід: графік. Вершина - точка багатокутника точки перетину.

Ми повинні знайти перехрестя. Перегляньте всі сторони багатокутника в обох багатокутниках [O (n * m)] і знайдіть будь-які перехрестя.

  • Якщо перетину не знайдено, просто додайте вершини і з’єднайте їх з ребром.

  • Якщо знайдені перехрестя, відсортуйте їх за довжиною до початкової точки, додайте всі вершини (початок, кінець та перехрещення) та з’єднайте (вже в сортованому порядку) з ребром. Графік

Крок 2. Перевірте побудований графік

Якщо ми не знайшли жодної точки перетину під час побудови графіка, ми маємо одну з таких умов:

  1. Багатокутник1 містить багатокутник2 - повернутий багатокутник1
  2. Багатокутник2 містить багатокутник1 - повернутий багатокутник2
  3. Багатокутник1 і багатокутник2 не перетинаються. Повернути багатокутник1 І багатокутник2.

Крок 3. Знайдіть ліву нижню вершину.

Знайдіть мінімальні координати x та y (minx, miny). Потім знайдіть мінімальну відстань між (minx, miny) та точками багатокутника. Ця точка буде лівою нижньою точкою.

Ліва нижня точка

Крок 4. Побудуйте загальний контур.

Ми починаємо обводити графік з лівої нижньої точки і продовжуємо, поки не повернемось до нього. На початку ми позначаємо всі краї як невидимі. На кожній ітерації слід вибрати наступний пункт і позначити його як відвіданий.

Щоб вибрати наступну точку, виберіть ребро з максимальним внутрішнім кутом у напрямку проти годинникової стрілки.

Я обчислюю два вектори: вектор1 для поточного ребра та вектор2 для кожного наступного невідвіданого ребра (як показано на малюнку).

Для векторів я обчислюю:

  1. Скалярний виріб (крапковий виріб). Він повертає значення, пов'язане з кутом між векторами.
  2. Векторний продукт (перехресний продукт). Він повертає новий вектор. Якщо z-координата цього вектора позитивна, скалярний добуток дає мені прямий кут у напрямку проти годинникової стрілки. В іншому випадку (z-координата від'ємна), я обчислюю отримати кут між векторами як 360 - кут від скалярного добутку.

В результаті я отримую ребро (і відповідну йому наступну вершину) з максимальним кутом.

Я додаю до списку результатів кожну передану вершину. Список результатів - багатокутник об'єднання. Вектори

Зауваження

  1. Цей алгоритм дозволяє об’єднати множину багатокутників - ітеративно застосовувати до пар полігонів.
  2. Якщо у вас є шлях, який складається з безлічі кривих і ліній Без'є, вам слід спочатку згладити цей шлях.

2
Думаю, слід зазначити, що для порівняння скалярних добутків вектори слід нормалізувати перед обчисленням їх скалярного добутку (тобто розділити векторні координати на їх довжини). У будь-якому разі, дякую за цю відповідь.
eyalzba

Цей алгоритм має назву чи є вашим власним творінням?
Андрес Ов'єдо,

Десь читав, але зараз не пам’ятаю, де і коли =)
xtmq

ПРИМІТКА. Див. Об’єднання багатокутників без отворів , на якому зображена інша схема: два багатокутники перекриваються, АЛЕ є „дірка”, яку жоден з них не покриває. Відповідно до коментаря @ xtmq, цей алгоритм "заповнює" цю діру (хоча він не є частиною жодного вхідного багатокутника). Якщо замість цього ви хочете "зберегти" ці отвори як отвори, тоді (а) обчисліть отвори і (б) поверніть "набір отворів" [У деяких графічних системах / режимах ці отвори можуть бути включені в набір вихідних багатокутників , і призведе до дірок при намалюванні.] ...
ToolmakerSteve

2
... Щоб зробити «(а) обчислити отвори», знайдіть точки, які ніколи не відвідує «Крок 4. Побудуйте загальний контур». Використовуйте одну з цих точок, щоб «завести» отвір. Виконайте подібний алгоритм "контуру", виключаючи будь-які точки, які вже використовуються основним вихідним багатокутником. Отриманий багатокутник є «діркою». Повторюйте, поки ВСІ точки не будуть включені в якийсь багатокутник або отвір.
ToolmakerSteve

11

Це складна, але добре зрозуміла тема, яка часто називається "регульовані булеві операції над полігонами". Ви можете переглянути цю відповідь MathOverflow , яка включає малюнок нижче (з бібліотеки відсікання Алана Мурти ), з рожевим об’єднанням комбінації OP :


      BooleanOps



2
Цей хлопець буквально написав книгу про це;)
Костянтин

6

Вам потрібно визначити, які точки лежать всередині . Після видалення цих точок ви можете вставити один набір точок "зовні" в інший. Ваші точки вставки (наприклад, там, де у вас стрілка на малюнку праворуч) - це місця, де вам потрібно було видалити точки з наборів вводу.


1
+1 за посилання на Бурка. На тридцять секунд повільніше, і я б вас до цього побив :)
Девід Сейлер

4

Гарне питання! Я ніколи раніше не робив подібних спроб, але зараз розберуся.

По-перше: Ви повинні знати, де ці дві фігури перекриваються. Для цього ви можете переглянути кожен ребро в багатокутнику A і побачити, де він перетинається, і ребро в багатокутнику B. У цьому прикладі має бути дві точки перетину.

Потім: зробіть форму об’єднання. Ви можете взяти всі вершини в А і В, а також точки перетину, а потім виключити вершини, що містяться в кінцевій фігурі. Щоб знайти ці точки, схоже, ви могли б просто знайти будь-яку вершину A, яка знаходиться всередині B, і будь-який вершину B, яка знаходиться всередині A.


Так, справжнє питання полягає в тому, як обчислити дві додані точки перетину ?
Pacerier


2

Рішення я бачив з допомогою BSP дерева описано тут .

По суті, він описує перетин через терміни об'єднання ребер багатокутника A, що знаходяться всередині багатокутника B (включаючи часткові ребра та обчислені за допомогою дерева BSP ). Потім ви можете визначити A  /  B як ~ (~ A  / \ ~ B ), де ~ позначає зворотну обмотку багатокутника, / позначає об'єднання та / \ позначає перетин.


2

Це дуже давнє запитання, але функція Union_ від Boost у мене спрацювала.

Дивіться цей фрагмент нижче:

#include <iostream>
#include <vector>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>

#include <boost/foreach.hpp>


int main()
{
    typedef boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > polygon;

    polygon green, blue;

    boost::geometry::read_wkt(
        "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", green);

    boost::geometry::read_wkt(
        "POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))", blue);

    std::vector<polygon> output;
    boost::geometry::union_(green, blue, output);

    int i = 0;
    std::cout << "green || blue:" << std::endl;
    BOOST_FOREACH(polygon const& p, output)
    {
        std::cout << i++ << ": " << boost::geometry::area(p) << std::endl;

        for (int i = 0; i < p.outer().size(); i++)
        {
            std::cout << p.outer().at(i).x() << " " << p.outer().at(i).y() << std::endl;
        }
    }



    return 0;
}

1
Не забудьте «виправити» свої багатокутники, якщо це необхідно. Див stackoverflow.com/questions/22258784 / ...
anumi

1

Групуючи країни, я сподіваюся, що не буде перекриття - ви можете взяти досить наївний алгоритм, який шукає спільні вершини - простим видом було б перебирати точки на одному багатокутнику, перевіряти, чи є він на будь-якому іншому вашому полігоні , і ділиться тим самим наступним або попереднім пунктом, щоб перевірити, чи є збіг. Потім просто видаліть спільну вершину, щоб створити ваш союз


2
"Групуючи країни, я сподіваюся, що не буде дублювання" ... не всі країни домовляються про власні кордони чи кордони сусідів, хоча було б непогано, якби вони це зробили.
FrustratedWithFormsDesigner

2
Дійсно, @FrustratedWithFormsDesigner, але більшість картографів або призначить спірний регіон своєму політичному союзнику, або як окрему структуру - це також те, чому я описую свій алгоритм як наївний ...
Роуленд Шоу,


1

Я зіткнувся з тією ж проблемою, і вирішив проблему, використовуючи такий спосіб

Обгортка Cython для перекладу на C ++ бібліотеки Angus Johnson's Clipper (версія 6.4.2) https://github.com/fonttools/pyclipper

pc = pyclipper.Pyclipper()
def get_poly_union(polygons):
    pc.AddPaths(polygons, pyclipper.PT_SUBJECT, True)
    solution = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO)
    return solution[0]

print_image = image.copy()
solution = get_poly_union(polygons_array) 
#polygons_array=[polygon,polygon,polygon, ...,polygon] and polygon=[point,point,point...,point]

cv2.drawContours(print_image, [np.asarray(solution)], -1, (0, 255, 0), 2)

plt.imshow(print_image)

Clipper доступний безпосередньо в C ++ тут: angusj.com/delphi/clipper.php
Catskul
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.