Интеграция с Mapbox GL JS

Turf.js используется как вычислительный слой геоаналитики поверх данных GeoJSON, тогда как Mapbox GL JS отвечает за визуализацию, стилизацию и интерактивное отображение карт в браузере. Их интеграция строится на принципе разделения ответственности: Mapbox GL JS управляет рендерингом и источниками данных, Turf.js — геометрическими вычислениями, фильтрацией и пространственной логикой.

Основой связки выступает формат GeoJSON, который одновременно понимается Mapbox GL JS как источник данных и Turf.js как входной набор геометрий для операций. Типовой поток данных выглядит следующим образом:

  • Mapbox GL JS хранит GeoJSON как source
  • Turf.js получает этот же GeoJSON для вычислений
  • результат вычислений возвращается обратно в Mapbox как обновлённый источник или слой

Ключевой момент интеграции — иммутабельность данных на уровне источника. Mapbox GL JS не отслеживает внутренние изменения объектов GeoJSON, поэтому любые результаты Turf.js требуют явного обновления source через setData.

Инициализация карты и подключение GeoJSON источника

Базовая структура включает создание карты и добавление источника типа geojson.

import mapboxgl from "mapbox-gl";
import * as turf from "@turf/turf";

mapboxgl.accessToken = "YOUR_TOKEN";

const map = new mapboxgl.Map({
  container: "map",
  style: "mapbox://styles/mapbox/streets-v12",
  center: [37.6173, 55.7558],
  zoom: 10
});

map.on("load", () => {
  map.addSource("points", {
    type: "geojson",
    data: {
      type: "FeatureCollection",
      features: []
    }
  });

  map.addLayer({
    id: "points-layer",
    type: "circle",
    source: "points",
    paint: {
      "circle-radius": 6,
      "circle-color": "#3b82f6"
    }
  });
});

На этом уровне Mapbox GL JS выступает как контейнер данных, готовый к динамическому обновлению через Turf.js.

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

Turf.js применяется для трансформации данных перед передачей в слой. Один из базовых сценариев — вычисление буферной зоны вокруг точки или полигона.

const point = turf.point([37.6173, 55.7558]);
const buffer = turf.buffer(point, 5, { units: "kilometers" });

map.getSource("points").setData({
  type: "FeatureCollection",
  features: [point, buffer]
});

Буферные зоны часто используются для визуализации радиусов влияния, зон поиска или доступности.

Динамическое обновление данных на основе взаимодействия карты

Интерактивность достигается через обработчики событий Mapbox GL JS. Координаты клика используются как входные данные Turf.js.

map.on("click", (e) => {
  const clickedPoint = turf.point([e.lngLat.lng, e.lngLat.lat]);

  const circle = turf.circle(clickedPoint, 10, {
    steps: 64,
    units: "kilometers"
  });

  map.getSource("points").setData({
    type: "FeatureCollection",
    features: [clickedPoint, circle]
  });
});

Здесь Turf.js генерирует геометрию круга, а Mapbox GL JS немедленно визуализирует результат через обновление source.

Пространственные фильтры и анализ набора точек

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

const area = turf.polygon([[
  [37.0, 55.0],
  [38.0, 55.0],
  [38.0, 56.0],
  [37.0, 56.0],
  [37.0, 55.0]
]]);

const points = turf.featureCollection([
  turf.point([37.2, 55.2]),
  turf.point([37.9, 55.8]),
  turf.point([38.5, 55.5])
]);

const filtered = turf.pointsWithinPolygon(points, area);

map.getSource("points").setData(filtered);

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

Расчёт расстояний и построение зависимых визуализаций

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

const from = turf.point([37.6173, 55.7558]);
const to = turf.point([37.8, 55.9]);

const distance = turf.distance(from, to, { units: "kilometers" });

Далее результат может использоваться для динамической генерации геометрии:

const line = turf.lineString([
  from.geometry.coordinates,
  to.geometry.coordinates
]);

map.getSource("points").setData({
  type: "FeatureCollection",
  features: [from, to, line]
});

Геометрические операции и пересечения

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

const poly1 = turf.polygon([[
  [37.0, 55.0],
  [38.0, 55.0],
  [38.0, 56.0],
  [37.0, 56.0],
  [37.0, 55.0]
]]);

const poly2 = turf.polygon([[
  [37.5, 55.5],
  [38.5, 55.5],
  [38.5, 56.5],
  [37.5, 56.5],
  [37.5, 55.5]
]]);

const intersection = turf.intersect(poly1, poly2);

map.getSource("points").setData({
  type: "FeatureCollection",
  features: intersection ? [intersection] : []
});

Такие операции используются в системах территориального анализа, наложения слоёв и определения конфликтующих зон.

Оптимизация вычислений при работе с большими наборами данных

При интеграции Turf.js с Mapbox GL JS критически важна производительность, особенно при обработке тысяч объектов.

Основные подходы оптимизации:

  • предварительная агрегация данных до передачи в Turf.js
  • использование bbox фильтров перед точными вычислениями
  • минимизация частоты вызовов setData
  • кэширование результатов пространственных операций

Пример предварительного отбора:

const bbox = turf.bboxPolygon([37, 55, 38, 56]);

const filtered = features.filter(f =>
  turf.booleanPointInPolygon(f, bbox)
);

Использование nearestPoint для интерактивных сценариев

Функция поиска ближайшего объекта часто применяется в интерфейсах выбора.

const points = turf.featureCollection([
  turf.point([37.6, 55.7]),
  turf.point([37.7, 55.8]),
  turf.point([37.8, 55.9])
]);

const target = turf.point([37.65, 55.75]);

const nearest = turf.nearestPoint(target, points);

map.getSource("points").setData({
  type: "FeatureCollection",
  features: [target, nearest]
});

Это позволяет реализовать привязку к ближайшим объектам, маршрутизацию и автоматический выбор элементов.

Потоковое обновление данных и реактивная геоаналитика

При постоянном изменении данных Turf.js используется как промежуточный слой трансформации:

  1. Источник событий (клик, API, WebSocket)
  2. Преобразование через Turf.js
  3. Обновление GeoJSON source
  4. Перерисовка Mapbox GL JS слоя
function update(features) {
  const enriched = features.map(f => {
    const buffered = turf.buffer(f, 2, { units: "kilometers" });
    return buffered;
  });

  map.getSource("points").setData({
    type: "FeatureCollection",
    features: enriched
  });
}

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

Mapbox GL JS использует проекцию Web Mercator, тогда как Turf.js выполняет расчёты в геодезической системе координат WGS84. Это приводит к важному принципу:

  • Turf.js всегда работает с [lng, lat]
  • визуализация Mapbox автоматически преобразует координаты
  • нельзя подменять порядок координат без нарушения геометрии

При сложных операциях (например, больших радиусах buffer) необходимо учитывать искажения проекции, особенно в высоких широтах.

Комбинирование слоёв и аналитических представлений

Интеграция позволяет строить многослойные аналитические визуализации:

  • слой исходных точек
  • слой буферных зон
  • слой пересечений
  • слой маршрутов или линий связности

Каждый слой формируется отдельным результатом Turf.js и управляется Mapbox GL JS независимо, но синхронно через общий набор данных или связанные источники.

map.addLayer({
  id: "buffer-layer",
  type: "fill",
  source: "points",
  paint: {
    "fill-color": "#60a5fa",
    "fill-opacity": 0.3
  }
});

Реализация пространственной логики пользовательского интерфейса

Turf.js часто используется как движок логики интерфейса:

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

Mapbox GL JS при этом остаётся слоем представления, не содержащим бизнес-логики, что обеспечивает предсказуемость архитектуры и масштабируемость геоприложений.