Объединение полигонов

Объединение полигонов — одна из наиболее распространённых геопространственных операций в Turf.js. Она позволяет преобразовать несколько пересекающихся или соприкасающихся полигонов в единый объект. Такая операция используется при анализе территорий, работе с административными границами, обработке зон покрытия, объединении земельных участков и подготовке данных для последующих вычислений.

В Turf.js для выполнения этой задачи применяется функция union.

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


Что такое объединение полигонов

Рассмотрим два полигона:

  • первый описывает территорию парка;
  • второй описывает прилегающую рекреационную зону.

Если полигоны пересекаются, после объединения получится единая территория, включающая обе области.

До объединения:

+---------+
|         |
|    +---------+
|    |    |    |
+----|----+    |
     |         |
     +---------+

После объединения:

+--------------+
|              |
|              |
|              |
|              |
+--------------+

Пересекающаяся часть становится частью общей геометрии и не учитывается дважды.


Подключение Turf.js

Установка через npm:

npm install @turf/turf

Импорт всей библиотеки:

import * as turf from "@turf/turf";

Импорт только необходимой функции:

import { union } from "@turf/turf";

Создание исходных полигонов

Для объединения понадобятся как минимум два полигона.

import * as turf from "@turf/turf";

const polygon1 = turf.polygon([
    [
        [0, 0],
        [4, 0],
        [4, 4],
        [0, 4],
        [0, 0]
    ]
]);

const polygon2 = turf.polygon([
    [
        [2, 2],
        [6, 2],
        [6, 6],
        [2, 6],
        [2, 2]
    ]
]);

Полученные объекты являются GeoJSON-полигонами:

console.log(polygon1);
console.log(polygon2);

Простое объединение двух полигонов

Современные версии Turf.js используют объект FeatureCollection.

const collection = turf.featureCollection([
    polygon1,
    polygon2
]);

const result = turf.union(collection);

Результат:

console.log(result);

Вывод будет содержать новый полигон, охватывающий обе области.


Визуализация результата

Исходные полигоны:

const polygon1 = turf.polygon([
    [
        [0, 0],
        [4, 0],
        [4, 4],
        [0, 4],
        [0, 0]
    ]
]);

const polygon2 = turf.polygon([
    [
        [2, 2],
        [6, 2],
        [6, 6],
        [2, 6],
        [2, 2]
    ]
]);

После объединения координаты могут выглядеть следующим образом:

{
    "type": "Feature",
    "geometry": {
        "type": "Polygon",
        "coordinates": [...]
    }
}

Точный набор координат зависит от формы исходных объектов.


Объединение непересекающихся полигонов

Необязательно, чтобы полигоны пересекались.

const polygon1 = turf.polygon([
    [
        [0, 0],
        [2, 0],
        [2, 2],
        [0, 2],
        [0, 0]
    ]
]);

const polygon2 = turf.polygon([
    [
        [5, 0],
        [7, 0],
        [7, 2],
        [5, 2],
        [5, 0]
    ]
]);

Объединение:

const result = turf.union(
    turf.featureCollection([
        polygon1,
        polygon2
    ])
);

В этом случае результатом обычно становится объект типа MultiPolygon.

console.log(result.geometry.type);

Результат:

MultiPolygon

Это связано с тем, что между областями отсутствует общая граница.


Разница между Polygon и MultiPolygon

После объединения результат может иметь разные типы геометрии.

Polygon

Возвращается, если области образуют единую непрерывную фигуру.

{
    "type": "Polygon"
}

MultiPolygon

Возвращается, если области остаются раздельными.

{
    "type": "MultiPolygon"
}

Поэтому после вызова union рекомендуется проверять тип результата.

if (result.geometry.type === "Polygon") {
    console.log("Один полигон");
}

if (result.geometry.type === "MultiPolygon") {
    console.log("Несколько независимых полигонов");
}

Объединение большого количества полигонов

На практике часто требуется объединить десятки или сотни объектов.

const collection = turf.featureCollection([
    polygon1,
    polygon2,
    polygon3,
    polygon4,
    polygon5
]);

const merged = turf.union(collection);

Такой подход значительно удобнее последовательного объединения каждого объекта вручную.


Использование свойств объектов

Каждый полигон может содержать атрибуты.

const polygon1 = turf.polygon(
    [[
        [0,0],
        [4,0],
        [4,4],
        [0,4],
        [0,0]
    ]],
    {
        name: "Zone A"
    }
);

const polygon2 = turf.polygon(
    [[
        [2,2],
        [6,2],
        [6,6],
        [2,6],
        [2,2]
    ]],
    {
        name: "Zone B"
    }
);

После объединения свойства автоматически не комбинируются логически.

const merged = turf.union(
    turf.featureCollection([
        polygon1,
        polygon2
    ])
);

При необходимости итоговые свойства задаются вручную:

merged.properties = {
    name: "Combined Zone"
};

Проверка площади до и после объединения

Часто требуется убедиться, что объединение прошло корректно.

Для этого можно использовать функцию area.

const area1 = turf.area(polygon1);
const area2 = turf.area(polygon2);

Площадь объединённой фигуры:

const merged = turf.union(
    turf.featureCollection([
        polygon1,
        polygon2
    ])
);

const mergedArea = turf.area(merged);

Сравнение:

console.log(area1);
console.log(area2);
console.log(mergedArea);

Если полигоны пересекаются, то:

mergedArea < area1 + area2

Пересекающаяся область учитывается только один раз.


Работа с административными территориями

Один из наиболее частых сценариев — объединение нескольких районов в единый регион.

Исходные данные:

const districts = turf.featureCollection([
    districtA,
    districtB,
    districtC
]);

Объединение:

const region = turf.union(districts);

Полученный объект можно использовать для:

  • вычисления общей площади;
  • отображения на карте;
  • поиска пересечений;
  • анализа населения;
  • расчёта транспортной доступности.

Объединение зон покрытия

Предположим, имеются несколько зон действия базовых станций.

const coverage = turf.featureCollection([
    tower1,
    tower2,
    tower3
]);

Объединение:

const totalCoverage = turf.union(coverage);

После этого становится возможным вычисление общей зоны обслуживания.

const totalArea = turf.area(totalCoverage);

Объединение буферных зон

Буферизация и объединение часто используются совместно.

Создание буферов:

const buffer1 = turf.buffer(point1, 5);
const buffer2 = turf.buffer(point2, 5);
const buffer3 = turf.buffer(point3, 5);

Объединение буферов:

const mergedBuffer = turf.union(
    turf.featureCollection([
        buffer1,
        buffer2,
        buffer3
    ])
);

Такой подход позволяет получить единую зону влияния нескольких объектов.


Обработка результата объединения

После выполнения операции рекомендуется проверить результат.

const merged = turf.union(collection);

Проверка:

if (!merged) {
    console.log("Объединение невозможно");
}

Далее можно обращаться к геометрии:

console.log(merged.geometry);

Или к координатам:

console.log(
    merged.geometry.coordinates
);

Производительность при больших объёмах данных

При работе с тысячами полигонов операция объединения может занимать заметное время.

Основные причины:

  • сложная геометрия объектов;
  • большое число вершин;
  • многочисленные пересечения;
  • вложенные контуры.

Для повышения производительности применяются следующие подходы:

  1. Предварительное упрощение геометрии.
const simplified = turf.simplify(
    polygon,
    {
        tolerance: 0.001,
        highQuality: false
    }
);
  1. Группировка объектов по регионам.

  2. Выполнение обработки пакетами.

  3. Исключение заведомо удалённых объектов.


Типичные ошибки

Незамкнутый контур

Неверно:

[
    [0,0],
    [5,0],
    [5,5],
    [0,5]
]

Верно:

[
    [0,0],
    [5,0],
    [5,5],
    [0,5],
    [0,0]
]

Первая и последняя координаты должны совпадать.


Неверный порядок координат

Формат GeoJSON использует порядок:

[longitude, latitude]

Правильно:

[37.6176, 55.7558]

Неправильно:

[55.7558, 37.6176]

Повреждённая геометрия

Самопересекающиеся полигоны способны привести к ошибкам геометрических операций.

Пример проблемного контура:

[
    [0,0],
    [5,5],
    [0,5],
    [5,0],
    [0,0]
]

Подобные фигуры рекомендуется исправлять до выполнения объединения.


Сравнение union и других операций

union

Объединяет области.

const result = turf.union(collection);

Результат содержит всю площадь всех объектов.


intersect

Находит только общую часть.

const result = turf.intersect(collection);

Возвращается пересечение фигур.


difference

Вычитает один полигон из другого.

const result = turf.difference(collection);

Остаётся часть первой фигуры без второй.


dissolve

Используется для объединения множества объектов по общему признаку и часто применяется при агрегации геоданных.


Практический пример

Пусть имеются три пересекающиеся территории.

const zones = turf.featureCollection([
    turf.polygon([
        [
            [0,0],
            [4,0],
            [4,4],
            [0,4],
            [0,0]
        ]
    ]),
    turf.polygon([
        [
            [2,2],
            [6,2],
            [6,6],
            [2,6],
            [2,2]
        ]
    ]),
    turf.polygon([
        [
            [5,1],
            [8,1],
            [8,4],
            [5,4],
            [5,1]
        ]
    ])
]);

Выполнение объединения:

const merged = turf.union(zones);

Расчёт площади:

const area = turf.area(merged);

console.log(area);

Проверка типа результата:

console.log(
    merged.geometry.type
);

Полученный объект представляет единую геометрию, пригодную для дальнейшего пространственного анализа, визуализации на картах и выполнения других операций Turf.js.