Работа с мультиполигонами в булевых операциях

В формате GeoJSON объект MultiPolygon представляет набор независимых полигонов, объединённых в одну геометрию. Каждый полигон может содержать внешний контур и произвольное количество внутренних контуров (отверстий).

Типичная структура выглядит следующим образом:

{
  "type": "MultiPolygon",
  "coordinates": [
    [
      [
        [30, 10],
        [40, 40],
        [20, 40],
        [10, 20],
        [30, 10]
      ]
    ],
    [
      [
        [60, 20],
        [80, 20],
        [80, 40],
        [60, 40],
        [60, 20]
      ]
    ]
  ]
}

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

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

В Turf.js мультиполигоны участвуют во всех основных булевых операциях наравне с обычными полигонами.


Создание мультиполигона

Для создания MultiPolygon используется функция multiPolygon.

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

const regions = turf.multiPolygon([
  [
    [
      [0, 0],
      [10, 0],
      [10, 10],
      [0, 10],
      [0, 0]
    ]
  ],
  [
    [
      [20, 0],
      [30, 0],
      [30, 10],
      [20, 10],
      [20, 0]
    ]
  ]
]);

Результатом становится объект GeoJSON Feature:

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

Такой объект можно передавать в любые функции Turf.js, поддерживающие работу с полигонами.


Булевы операции в Turf.js

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

Наиболее востребованы:

Операция Назначение
union Объединение
intersect Пересечение
difference Вычитание
dissolve Слияние смежных областей
booleanIntersects Проверка пересечения
booleanContains Проверка вложенности
booleanOverlap Проверка наложения
booleanWithin Проверка нахождения внутри

При работе с MultiPolygon поведение этих операций имеет ряд особенностей.


Объединение мультиполигонов

Функция union() объединяет все области двух геометрий.

Объединение непересекающихся частей

const area1 = turf.multiPolygon([
  [
    [
      [0, 0],
      [10, 0],
      [10, 10],
      [0, 10],
      [0, 0]
    ]
  ]
]);

const area2 = turf.multiPolygon([
  [
    [
      [20, 0],
      [30, 0],
      [30, 10],
      [20, 10],
      [20, 0]
    ]
  ]
]);

const result = turf.union(
  turf.featureCollection([area1, area2])
);

Результирующая геометрия также будет MultiPolygon.

Структурно:

MultiPolygon
 ├─ Polygon #1
 └─ Polygon #2

Такой сценарий характерен для объединения удалённых территорий.


Объединение пересекающихся частей

Если полигоны пересекаются, Turf.js строит единую область.

const poly1 = turf.polygon([
  [
    [0, 0],
    [20, 0],
    [20, 20],
    [0, 20],
    [0, 0]
  ]
]);

const poly2 = turf.polygon([
  [
    [10, 10],
    [30, 10],
    [30, 30],
    [10, 30],
    [10, 10]
  ]
]);

const merged = turf.union(
  turf.featureCollection([poly1, poly2])
);

Полученная фигура уже может стать обычным Polygon, если после объединения остаётся единый контур.

Это важная особенность: тип результата не всегда совпадает с типом входных данных.


Пересечение мультиполигонов

Функция intersect() вычисляет общую область.

const overlap = turf.intersect(
  turf.featureCollection([
    multiPolyA,
    multiPolyB
  ])
);

Если общих участков нет:

null

Если пересечение существует:

Feature<Polygon>

или

Feature<MultiPolygon>

в зависимости от количества образовавшихся фрагментов.


Пример множественных пересечений

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

A1   A2   A3

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

B

После выполнения:

const result = turf.intersect(
  turf.featureCollection([multiA, multiB])
);

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

Intersection
 ├─ Part #1
 └─ Part #2

Вычитание областей

Функция difference() удаляет из первой геометрии область второй.

const result = turf.difference(
  turf.featureCollection([
    source,
    mask
  ])
);

Логически операция выглядит так:

A - B

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


Образование отверстий

Если вычитаемая область полностью лежит внутри полигона:

██████████
██      ██
██  B   ██
██      ██
██████████

результатом станет полигон с внутренним кольцом.

Polygon
 ├─ outer ring
 └─ hole

GeoJSON автоматически хранит отверстия как дополнительные массивы координат.


Разделение полигона на части

Иногда вычитание разрывает исходную область.

████████████
████ BBB ███
████ BBB ███
████████████

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

███      ███
███      ███

В этом случае Turf.js возвращает MultiPolygon.

{
  type: "MultiPolygon"
}

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

if (result.geometry.type === "MultiPolygon") {
  console.log("Область разделилась на части");
}

Проверка пересечения мультиполигонов

Для быстрой проверки без построения новой геометрии используется функция booleanIntersects().

const intersects = turf.booleanIntersects(
  multiPolygon1,
  multiPolygon2
);

Результат:

true

или

false

Внутри Turf.js анализируются все полигоны, входящие в состав MultiPolygon.

Даже если пересекается только одна часть:

MP1
 ├─ P1
 ├─ P2
 └─ P3

MP2
 └─ пересекает P2

результат будет:

true

Проверка полного включения

Функция booleanContains() определяет, полностью ли одна геометрия содержит другую.

const contains = turf.booleanContains(
  bigArea,
  smallArea
);

Для мультиполигонов проверяются все составляющие области.

const contains = turf.booleanContains(
  country,
  islands
);

Возвращается:

true

только если каждая часть объекта islands находится внутри country.


Проверка нахождения внутри

Обратная операция выполняется функцией booleanWithin().

const within = turf.booleanWithin(
  objectA,
  objectB
);

Логически:

A внутри B

Для MultiPolygon это особенно полезно при анализе административных границ.

Например:

const isInside = turf.booleanWithin(
  district,
  region
);

Проверка частичного наложения

Функция booleanOverlap() используется тогда, когда объекты:

  • пересекаются;
  • имеют общую площадь;
  • не содержат друг друга полностью.
const overlap = turf.booleanOverlap(
  multiPolygon1,
  multiPolygon2
);

Пример:

AAAAAAA
AAABBBB
AAABBBB

Результат:

true

Если один объект полностью вложен в другой:

false

Поскольку это уже отношение включения, а не наложения.


Работа с FeatureCollection мультиполигонов

Во многих проектах данные поступают в виде коллекций.

const collection = turf.featureCollection([
  mp1,
  mp2,
  mp3,
  mp4
]);

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

let current = collection.features[0];

for (let i = 1; i < collection.features.length; i++) {
  current = turf.union(
    turf.featureCollection([
      current,
      collection.features[i]
    ])
  );
}

После обработки получается единая геометрия.

Такой подход часто используется для:

  • объединения муниципалитетов;
  • построения зон покрытия;
  • агрегации кадастровых данных;
  • формирования региональных карт.

Нормализация результата булевых операций

После сложных вычислений тип результирующей геометрии может меняться.

Например:

Polygon

может превратиться в

MultiPolygon

или наоборот.

Без проверки типа дальнейшая обработка может завершиться ошибкой.

Рекомендуемый подход:

const geometryType =
  result.geometry.type;

switch (geometryType) {
  case "Polygon":
    console.log("Один полигон");
    break;

  case "MultiPolygon":
    console.log("Несколько полигонов");
    break;
}

Подобная проверка особенно важна при:

  • экспорте GeoJSON;
  • визуализации на карте;
  • расчёте площади;
  • генерации отчётов;
  • передаче данных в сторонние GIS-системы.

Получение отдельных полигонов из MultiPolygon

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

Для этого используется flatten().

const flattened =
  turf.flatten(multiPolygon);

Результатом будет:

FeatureCollection<Polygon>

Далее можно перебрать все полигоны.

flattened.features.forEach(poly => {
  const area = turf.area(poly);

  console.log(area);
});

Подход широко применяется после операций:

  • union;
  • difference;
  • intersect;
  • dissolve.

Вычисление площади результатов

После булевых операций часто требуется определить площадь итоговой фигуры.

const area = turf.area(result);

Функция одинаково работает для:

Polygon

и

MultiPolygon

Например:

const merged = turf.union(
  turf.featureCollection([
    region1,
    region2
  ])
);

const totalArea = turf.area(merged);

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


Производительность при работе с большими мультиполигонами

Булевы операции относятся к наиболее ресурсоёмким вычислениям в геоинформационных системах.

Основные факторы нагрузки:

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

Проблемы обычно возникают при обработке:

100+
 полигонов

или

десятков тысяч вершин

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

  1. Предварительная фильтрация по bounding box.
  2. Упрощение геометрии через simplify().
  3. Разделение данных на тайлы.
  4. Последовательная пакетная обработка.
  5. Исключение заведомо непересекающихся объектов.

Пример предварительной проверки:

if (
  turf.booleanIntersects(a, b)
) {
  const result = turf.intersect(
    turf.featureCollection([a, b])
  );
}

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


Типичные сценарии использования

Геоинформационные системы

Районы
+
Области
+
Муниципалитеты

Объединяются и пересекаются для анализа административных границ.

Логистика

Зоны доставки
+
Зоны обслуживания

Используются для построения общих территорий покрытия.

Экологический мониторинг

Лесные массивы
-
Запретные территории

Формируются новые области наблюдения.

Кадастровые системы

Участок
-
Отчуждаемая часть

Вычисляется остаточная площадь земельного объекта.

Анализ телекоммуникационных сетей

Покрытие станции A
∩
Покрытие станции B

Определяются зоны перекрытия сигнала.

Во всех подобных задачах MultiPolygon выступает одной из ключевых геометрий, а булевы операции Turf.js позволяют выполнять сложные пространственные преобразования с сохранением структуры данных GeoJSON и автоматическим формированием корректных результирующих полигонов.