Полигоны с отверстиями в GeoJSON представляются как объект типа
Polygon, где первый контур описывает внешнюю границу, а
последующие контуры — внутренние вырезы. Такая структура широко
используется в геопространственных данных и полностью поддерживается
Turf.js при условии корректного формирования координат.
Структура полигона с отверстиями основана на массиве колец:
Каждое кольцо — это массив координат вида [lng, lat],
где первая и последняя точки совпадают для замыкания контура.
Формат GeoJSON:
{
"type": "Polygon",
"coordinates": [
[ // внешний контур
[30.0, 10.0],
[40.0, 40.0],
[20.0, 40.0],
[10.0, 20.0],
[30.0, 10.0]
],
[ // отверстие
[20.0, 30.0],
[35.0, 35.0],
[30.0, 20.0],
[20.0, 30.0]
]
]
}
В Turf.js такой объект интерпретируется как единая геометрия, где внутренний контур исключается из области покрытия.
Turf.js не требует специальных функций для создания отверстий —
используется стандарт turf.polygon.
import { polygon } from "@turf/turf";
const poly = polygon([
[
[0, 0],
[10, 0],
[10, 10],
[0, 10],
[0, 0]
],
[
[3, 3],
[7, 3],
[7, 7],
[3, 7],
[3, 3]
]
]);
В этом примере внутренняя область автоматически интерпретируется как вырез.
Функция booleanPointInPolygon учитывает внутренние
кольца и возвращает false, если точка попадает в
отверстие.
import { booleanPointInPolygon, point, polygon } from "@turf/turf";
const pt = point([5, 5]);
const poly = polygon([
[
[0, 0],
[10, 0],
[10, 10],
[0, 10],
[0, 0]
],
[
[3, 3],
[7, 3],
[7, 7],
[3, 7],
[3, 3]
]
]);
booleanPointInPolygon(pt, poly); // false
Точка внутри внешнего контура, но попадает в отверстие, поэтому исключается из области.
Хотя GeoJSON RFC 7946 не требует строгого направления обхода, многие геоинструменты придерживаются соглашения:
Turf.js в большинстве операций не зависит строго от ориентации, но при комбинировании с другими библиотеками (например, Mapbox GL, JSTS) несоблюдение направления может приводить к некорректной интерпретации отверстий.
Рекомендуется придерживаться единообразного правила:
В реальных задачах отверстия часто не задаются вручную, а получаются через вычитание геометрий.
differenceФункция turf.difference создаёт новую геометрию, вычитая
одну область из другой.
import { polygon, difference } from "@turf/turf";
const outer = polygon([[
[0, 0],
[20, 0],
[20, 20],
[0, 20],
[0, 0]
]]);
const inner = polygon([[
[5, 5],
[15, 5],
[15, 15],
[5, 15],
[5, 5]
]]);
const result = difference(outer, inner);
Результатом будет полигон с отверстием, эквивалентным внутреннему прямоугольнику.
Особенность: результат difference может вернуть не
только Polygon, но и MultiPolygon, если
вычитание разрывает исходную геометрию.
При использовании turf.union поведение отверстий зависит
от конфигурации геометрий. Если внутренние области перекрываются,
отверстия могут исчезать или перераспределяться.
import { union } from "@turf/turf";
const merged = union(polyA, polyB);
При объединении сложных фигур важно учитывать:
Отверстия в Polygon и MultiPolygon концептуально различаются:
Пример MultiPolygon:
{
type: "MultiPolygon",
coordinates: [
[
[
[0, 0],
[10, 0],
[10, 10],
[0, 10],
[0, 0]
],
[
[2, 2],
[4, 2],
[4, 4],
[2, 4],
[2, 2]
]
],
[
[
[20, 20],
[30, 20],
[30, 30],
[20, 30],
[20, 20]
]
]
]
}
Каждый полигон внутри MultiPolygon имеет собственный набор отверстий.
Каждое кольцо должно быть замкнуто:
// неправильно
[ [0,0], [10,0], [10,10], [0,10] ]
// правильно
[ [0,0], [10,0], [10,10], [0,10], [0,0] ]
Незамкнутые кольца могут приводить к некорректной triangulation и ошибкам в булевых операциях.
Отверстие не должно пересекать внешний контур или выходить за его пределы. Такие случаи приводят к невалидной геометрии, которую Turf.js может интерпретировать непредсказуемо.
Самопересекающиеся контуры нарушают топологическую корректность. В
булевых операциях это часто вызывает null результат.
Polygon может содержать несколько внутренних колец:
const poly = polygon([
outerRing,
hole1,
hole2,
hole3
]);
Правило интерпретации:
Turf.js применяет именно такую модель при пространственных запросах.
В задачах картографии отверстия часто формируются как результат последовательного вычитания:
Композиция операций:
const base = polygon([...]);
const cut1 = polygon([...]);
const cut2 = polygon([...]);
const step1 = difference(base, cut1);
const final = difference(step1, cut2);
Каждая операция может менять структуру геометрии, превращая Polygon в MultiPolygon.
Геометрии с большим количеством колец требуют дополнительных вычислений при:
Особенно затратны операции:
differenceunionintersectРекомендации по снижению нагрузки:
simplify, если используется сторонняя реализация)При визуализации (например, через Mapbox GL или Leaflet) отверстия отображаются автоматически при корректной структуре GeoJSON. Важно, чтобы:
Нарушение этих условий часто приводит к заполнению отверстий или исчезновению внутренних контуров при рендеринге.