Пересечение и объединение

Геометрическая модель данных

В OpenLayers вся работа с векторной графикой строится вокруг геометрий: точек, линий и полигонов. Базовый класс ol/geom/Geometry задаёт общий интерфейс для всех пространственных объектов, а конкретные реализации (Point, LineString, Polygon, MultiPolygon) используются для описания реальных данных на карте.

Каждый объект геометрии содержит координаты в системе координат карты (обычно EPSG:3857 или EPSG:4326) и может быть связан с объектом Feature, который, в свою очередь, отображается через слой VectorLayer.

Важно понимать, что сама библиотека OpenLayers не является полнофункциональной ГИС-системой для булевых операций над полигонами, поэтому операции пересечения и объединения чаще реализуются через внешние геометрические библиотеки, интегрированные в поток обработки данных.


Пересечение геометрий (Intersection)

Смысл операции

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

Для полигонов это может быть:

  • новый полигон меньшего размера,
  • мультиполигон (если пересечений несколько),
  • пустая геометрия (если пересечений нет).

Подход 1: использование Turf.js

Наиболее популярный способ работы с пересечениями в связке с OpenLayers — библиотека Turf.js.

Turf предоставляет функцию intersect, которая принимает GeoJSON-геометрии.

Пример пересечения полигонов

import { Feature } from 'ol';
import Polygon from 'ol/geom/Polygon';
import * as turf from '@turf/turf';

// OpenLayers полигоны
const poly1 = new Polygon([[
  [0, 0],
  [10, 0],
  [10, 10],
  [0, 10],
  [0, 0]
]]);

const poly2 = new Polygon([[
  [5, 5],
  [15, 5],
  [15, 15],
  [5, 15],
  [5, 5]
]]);

// преобразование в GeoJSON
const turfPoly1 = turf.polygon(poly1.getCoordinates());
const turfPoly2 = turf.polygon(poly2.getCoordinates());

// пересечение
const intersect = turf.intersect(turfPoly1, turfPoly2);

if (intersect) {
  const resultGeom = new Polygon(intersect.geometry.coordinates);
}

Подход 2: JSTS (более строгая топологическая модель)

Библиотека JSTS реализует алгоритмы, основанные на JTS (Java Topology Suite), и поддерживает более точные операции пересечения.

Пример пересечения через JSTS

import GeoJSON from 'ol/format/GeoJSON';
import { GeometryFactory } from 'jsts/org/locationtech/jts/geom/GeometryFactory';
import { WKTReader } from 'jsts/org/locationtech/jts/io/WKTReader';
import { GeoJSONReader, GeoJSONWriter } from 'jsts/org/locationtech/jts/io';

const reader = new GeoJSONReader();
const writer = new GeoJSONWriter();

const geom1 = reader.read({
  type: "Polygon",
  coordinates: [[[0,0],[10,0],[10,10],[0,10],[0,0]]]
});

const geom2 = reader.read({
  type: "Polygon",
  coordinates: [[[5,5],[15,5],[15,15],[5,15],[5,5]]]
});

const intersection = geom1.intersection(geom2);

const resultGeoJSON = writer.write(intersection);

Использование результата в OpenLayers

После получения GeoJSON результат легко конвертируется обратно в геометрию OpenLayers:

import GeoJSON from 'ol/format/GeoJSON';

const format = new GeoJSON();

const feature = format.readFeature(resultGeoJSON, {
  featureProjection: 'EPSG:3857'
});

Объединение геометрий (Union)

Смысл операции

Объединение создаёт одну геометрию, охватывающую все входные объекты.

Результаты:

  • один полигон (если объекты соприкасаются),
  • мультиполигон (если объекты раздельны),
  • упрощённая форма без внутренних границ.

Turf.js: union

В Turf.js существует функция union, позволяющая объединять полигоны.

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

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

const poly2 = turf.polygon([[
  [5, 5],
  [15, 5],
  [15, 15],
  [5, 15],
  [5, 5]
]]);

const union = turf.union(poly1, poly2);

JSTS: точное объединение

JSTS обеспечивает более стабильное объединение сложных геометрий:

const geomUnion = geom1.union(geom2);

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

let result = geometries[0];

for (let i = 1; i < geometries.length; i++) {
  result = result.union(geometries[i]);
}

Интеграция с OpenLayers слоями

Операции пересечения и объединения обычно применяются к Feature из VectorSource.

Пример: обработка слоя

import VectorSource from 'ol/source/Vector';
import Feature from 'ol/Feature';

const source = new VectorSource();

const features = source.getFeatures();

let geom = features[0].getGeometry();

for (let i = 1; i < features.length; i++) {
  const g = features[i].getGeometry();

  // здесь применяется JSTS или Turf (через GeoJSON)
  geom = geom.union(g); // псевдокод, зависит от библиотеки
}

Типы пересечений в прикладной аналитике

1. Геометрическое пересечение

Используется для:

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

2. Пространственное пересечение в OpenLayers

Методы:

  • geometry.intersectsExtent(extent) — проверка пересечения с bounding box,
  • feature.getGeometry().intersectsCoordinate() — проверка попадания точки.
const extent = [0, 0, 100, 100];

if (geometry.intersectsExtent(extent)) {
  // объект частично попадает в область
}

Оптимизация вычислений

При работе с большими наборами данных важно учитывать производительность.

Предфильтрация по extent

Перед сложными операциями выполняется быстрый отсев:

if (!geom1.intersectsExtent(geom2.getExtent())) {
  return null;
}

Упрощение геометрий

Для ускорения вычислений:

import { simplify } from 'ol/geom/Geometry';

const simplified = geometry.simplify(10);

Работа с мультигеометриями

При объединении часто возникает переход к MultiPolygon:

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

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


Практический сценарий: анализ зон покрытия

  1. Загружаются полигоны зон обслуживания.
  2. Выполняется попарное объединение для получения общей зоны.
  3. Затем вычисляются пересечения с зонами запроса пользователя.
  4. Результат отображается как отдельный слой.
const coverage = turf.union(zoneA, zoneB);
const overlap = turf.intersect(coverage, requestZone);

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

После вычислений результат добавляется в слой OpenLayers:

const feature = new Feature({
  geometry: new Polygon(resultCoords)
});

vectorSource.addFeature(feature);

Стилизация позволяет выделить:

  • зоны пересечения (например, красным),
  • зоны объединения (синим),
  • исходные объекты (полупрозрачным слоем).
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';

const style = new Style({
  fill: new Fill({
    color: 'rgba(255, 0, 0, 0.3)'
  })
});

Особенности координатных систем

Критически важно, чтобы все операции выполнялись в одной системе координат:

  • OpenLayers часто использует EPSG:3857,
  • GeoJSON и Turf — EPSG:4326.

Перед вычислениями выполняется трансформация:

geometry.transform('EPSG:3857', 'EPSG:4326');

Ошибки и ограничения

Типичные проблемы:

  • самопересекающиеся полигоны,
  • некорректный порядок вершин,
  • потеря точности при трансформации координат,
  • несовместимость типов геометрий (Polygon vs MultiPolygon).

JSTS обычно корректнее обрабатывает топологические ошибки, чем Turf.


Сравнение подходов

Turf.js

  • простота использования,
  • работа с GeoJSON,
  • удобен для веб-приложений,
  • ограниченная топологическая строгость.

JSTS

  • высокая точность,
  • строгая геометрическая модель,
  • лучше для сложных GIS-задач,
  • более тяжёлый и сложный API.

Комбинированный подход

В реальных приложениях часто используется гибрид:

  • OpenLayers — визуализация и управление слоями,
  • Turf.js — быстрые расчёты,
  • JSTS — критически важные операции пересечения и объединения.

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