Самопересечение в контексте картографических данных возникает тогда,
когда граница полигона пересекает саму себя, образуя некорректную или
неоднозначную геометрию. В терминах GeoJSON это означает, что
последовательность координат, описывающих контур Polygon
или MultiPolygon, нарушает правило простоты (simple
geometry), при котором рёбра не должны пересекаться, кроме как в
вершинах.
В Mapbox GL JS такие геометрии могут приводить к непредсказуемому
рендерингу, особенно в слоях типа fill,
fill-extrusion и при использовании кластеризации или
динамической стилизации. Поведение не всегда выражается ошибкой — чаще
оно проявляется визуальными артефактами: «дырками», наложениями,
исчезновением частей полигона или странными заливками.
Ключевой момент: Mapbox GL JS не гарантирует исправление геометрии на стороне клиента. Библиотека предполагает, что входные данные уже валидны.
В основе рендеринга лежит векторный тайловый конвейер Mapbox. На этапе триангуляции полигона используется алгоритм разбиения на треугольники (earcut). Именно здесь самопересечения становятся критическими.
При корректной геометрии:
При самопересечении:
Особенно чувствительны случаи, где:
Часто данные приходят из CAD-систем, GIS-экспорта или пользовательского рисования на карте. На этапе генерации могут возникать:
Алгоритмы вроде Douglas-Peucker при агрессивной настройке допускают пересечения рёбер.
import * as turf from "@turf/turf";
const simplified = turf.simplify(polygon, {
tolerance: 0.01,
highQuality: false
});
При высокой tolerance контур может «переламываться»
внутрь себя, образуя пересечения.
При интерактивном редактировании пользователем:
В слоях типа fill самопересечения чаще всего проявляются
как:
map.addLayer({
id: "areas",
type: "fill",
source: "geo",
paint: {
"fill-color": "#3b82f6",
"fill-opacity": 0.6
}
});
Mapbox GL JS использует правило заполнения, аналогичное графическим API:
nonzero (по умолчанию для большинства случаев)evenodd (в некоторых визуальных сценариях)Хотя напрямую параметр не всегда доступен в стиле, поведение заливки зависит от внутренней триангуляции.
Самопересекающиеся полигоны могут менять интерпретацию «внутренней» и «внешней» области.
import * as turf from "@turf/turf";
const isSelfIntersecting = !turf.kinks(polygon).features.length;
Функция kinks выявляет точки пересечения рёбер.
Если результат содержит геометрии — полигон имеет самопересечения.
Более строгий подход — использование JSTS (JavaScript Topology Suite):
import jsts from "jsts";
const reader = new jsts.io.GeoJSONReader();
const geom = reader.read(polygon);
const validator = new jsts.operation.valid.IsValidOp(geom);
const valid = validator.isValid();
JSTS позволяет не только выявить проблему, но и получить объяснение причины.
Самый практичный способ — привести полигон к валидной форме.
import * as turf from "@turf/turf";
const cleaned = turf.rewind(turf.cleanCoords(polygon));
cleanCoords удаляет дублирующиеся точки, а
rewind корректирует порядок обхода.
Если исходная геометрия сложная, её можно разложить:
const exploded = turf.polygonSplit(polygon);
Или через более общие алгоритмы:
Один из наиболее стабильных способов «починки»:
const fixed = turf.buffer(polygon, 0);
Этот приём заставляет геометрический движок пересчитать топологию.
В MultiPolygon самопересечения могут возникать не только
внутри одного кольца, но и между частями:
Mapbox GL JS рассматривает их как независимые геометрии, но при рендеринге они могут накладываться, создавая визуальные артефакты.
Самопересечения могут нарушать hit-testing:
map.on("mousemove", "areas", (e) => {
map.setFeatureState(
{ source: "geo", id: e.features[0].id },
{ hover: true }
);
});
Если геометрия некорректна:
features[0] может возвращать неожиданную часть
полигона;В 3D-слоях проблема усиливается:
map.addLayer({
id: "buildings",
type: "fill-extrusion",
source: "geo",
paint: {
"fill-extrusion-height": 50,
"fill-extrusion-color": "#aaa"
}
});
Важную роль играет направление обхода вершин:
Нарушение ориентации усиливает эффект самопересечений, особенно при комбинировании с упрощением.
import * as turf from "@turf/turf";
const corrected = turf.rewind(polygon, { reverse: false });
Mapbox GL JS ожидает, что:
При нарушении этих правил триангулятор работает эвристически, а не детерминированно.
Любая пользовательская или внешняя геометрия должна проверяться до попадания в источник Mapbox:
turf.kinks;При интерактивных редакторах:
При генерации тайлов или оптимизации:
Векторные тайлы Mapbox также не защищены от самопересечений. При генерации на сервере (tippecanoe, tilemaker):
Самопересекающиеся полигоны увеличивают стоимость:
На больших наборах данных это приводит к:
Интересная особенность Mapbox GL JS — зависимость артефактов от zoom:
Это связано с тем, что геометрия пересчитывается на уровне тайлов и LOD (Level of Detail), где разные упрощённые версии полигона могут иметь разные топологические ошибки.