Полные пользовательские сценарии

Mapbox GL JS представляет собой высокоуровневый слой управления WebGL-рендерингом карт, где пользовательские сценарии строятся вокруг связки источников данных (sources), визуальных слоёв (layers), камеры (camera), состояния приложения и событий взаимодействия. Полные сценарии в таких системах не ограничиваются отображением карты — они включают поток данных, управление состоянием, визуальную агрегацию и реакцию интерфейса на изменения геопространственной информации.

В основе любых сложных сценариев лежит модель:

  • Source (источник данных) — GeoJSON, vector tiles, raster tiles
  • Layer (слой визуализации) — отображение данных источника
  • Camera (камера) — zoom, center, bearing, pitch
  • State (состояние приложения) — фильтры, выбранные объекты, режимы отображения
  • Events (события) — click, mousemove, moveend, load

Типичный сценарий формируется как реактивная система:

  • изменение данных → setData() или обновление tile source
  • изменение состояния → пересборка фильтров layers
  • взаимодействие пользователя → изменение camera и UI state
map.addSource('orders', {
  type: 'geojson',
  data: '/api/orders'
});

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

Сценарий: система доставки в реальном времени

Сценарий доставки строится вокруг постоянного обновления координат объектов (курьеров, заказов, транспорта). Основной механизм — обновление GeoJSON источника.

Ключевые элементы:

  • поток WebSocket или SSE
  • обновление setData
  • анимация перемещения маркеров
  • фильтрация по статусам
const source = map.getSource('couriers');

socket.on('courier:update', (data) => {
  const geojson = {
    type: 'FeatureCollection',
    features: data.map(courier => ({
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: courier.coords
      },
      properties: {
        status: courier.status
      }
    }))
  };

  source.setData(geojson);
});

Для плавности движения применяется интерполяция координат:

  • хранение предыдущей позиции
  • расчет промежуточных точек
  • использование requestAnimationFrame

Слой может изменять стиль в зависимости от состояния:

map.setPaintProperty('couriers-layer', 'circle-color', [
  'match',
  ['get', 'status'],
  'active', '#22c55e',
  'delayed', '#f59e0b',
  '#ef4444'
]);

Сценарий: платформа недвижимости с фильтрацией и кластерами

Геоданные объектов недвижимости обычно имеют высокую плотность, что требует кластеризации.

Основные компоненты:

  • cluster: true
  • динамические фильтры
  • раскрытие кластеров по zoom
map.addSource('real-estate', {
  type: 'geojson',
  data: '/api/houses',
  cluster: true,
  clusterMaxZoom: 14,
  clusterRadius: 50
});

Отображение кластеров:

map.addLayer({
  id: 'clusters',
  type: 'circle',
  source: 'real-estate',
  filter: ['has', 'point_count'],
  paint: {
    'circle-radius': [
      'step',
      ['get', 'point_count'],
      15, 10,
      25, 50,
      35
    ],
    'circle-color': '#6366f1'
  }
});

Логика пользовательского сценария:

  • выбор фильтра (цена, площадь, тип)
  • пересборка GeoJSON
  • обновление источника
  • автоматический пересчет кластеров

Фильтрация реализуется через setFilter:

map.setFilter('unclustered-point', [
  'all',
  ['>=', ['get', 'price'], 100000],
  ['<=', ['get', 'price'], 300000]
]);

Сценарий: аналитическая геовизуализация

Аналитические сценарии включают heatmap и choropleth.

Heatmap используется для плотности событий:

map.addLayer({
  id: 'heatmap',
  type: 'heatmap',
  source: 'events',
  paint: {
    'heatmap-intensity': 1,
    'heatmap-radius': 20,
    'heatmap-color': [
      'interpolate',
      ['linear'],
      ['heatmap-density'],
      0, 'rgba(0,0,255,0)',
      1, 'rgba(255,0,0,1)'
    ]
  }
});

Choropleth-карты строятся на основе полигонов:

  • регионы как FeatureCollection
  • числовые показатели в properties
  • интерполяция цвета
map.addLayer({
  id: 'regions-fill',
  type: 'fill',
  source: 'regions',
  paint: {
    'fill-color': [
      'interpolate',
      ['linear'],
      ['get', 'value'],
      0, '#f3f4f6',
      100, '#60a5fa',
      500, '#1e3a8a'
    ]
  }
});

Сценарий: навигация и маршрутизация

Маршрутизация обычно строится через внешний API, но визуализация полностью ложится на GL JS.

Компоненты:

  • GeoJSON маршрут
  • polyline layer
  • интерактивные точки остановок
map.addSource('route', {
  type: 'geojson',
  data: routeGeoJSON
});

map.addLayer({
  id: 'route-line',
  type: 'line',
  source: 'route',
  paint: {
    'line-width': 4,
    'line-color': '#10b981'
  }
});

Анимация движения вдоль маршрута:

  • вычисление прогресса
  • обновление координаты маркера
  • синхронизация с камерой
map.flyTo({
  center: route.coordinates[0],
  zoom: 14,
  speed: 1.2
});

Сценарий: 3D города и высотная визуализация

3D-сценарии используют extrusion и terrain.

Extrusion зданий:

map.addLayer({
  id: 'buildings',
  type: 'fill-extrusion',
  source: 'composite',
  'source-layer': 'building',
  paint: {
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-color': '#94a3b8',
    'fill-extrusion-opacity': 0.8
  }
});

Terrain добавляет рельеф:

map.setTerrain({
  source: 'mapbox-dem',
  exaggeration: 1.5
});

Камера становится частью сценария:

  • изменение pitch
  • вращение bearing
  • анимация переходов между районами

Сценарий: геозоны и событийные триггеры

Геозоны реализуются на стороне клиента:

  • полигоны зон
  • проверка попадания точки
  • события enter/exit
function isInsideZone(point, polygon) {
  return turf.booleanPointInPolygon(point, polygon);
}

При обновлении координат:

  • проверка принадлежности зоне
  • генерация события
  • изменение UI состояния

Применяется для:

  • складов
  • зон доставки
  • ограниченных территорий

Сценарий: производительность и поток данных

Высоконагруженные сценарии требуют:

  • throttling обновлений
  • батчинг GeoJSON
  • использование vector tiles вместо GeoJSON
  • минимизация repaint

Подход обновления:

  • не вызывать setData чаще 10–15 раз/сек
  • агрегировать изменения
let queue = [];

function pushUpdate(point) {
  queue.push(point);
}

setInterval(() => {
  if (!queue.length) return;

  const data = buildGeoJSON(queue);
  map.getSource('live').setData(data);

  queue = [];
}, 100);

Сценарий: синхронизация состояния UI и карты

Карты в сложных приложениях должны быть частью общего состояния приложения.

Синхронизация включает:

  • zoom → URL
  • center → store
  • выбранный объект → state manager
map.on('moveend', () => {
  const center = map.getCenter();
  const zoom = map.getZoom();

  history.replaceState(null, '', `#z=${zoom}&lat=${center.lat}&lng=${center.lng}`);
});

При восстановлении состояния:

  • парсинг URL
  • установка камеры
  • восстановление слоёв

Сценарий: мультимодальные слои и переключение режимов

Типичные режимы:

  • аналитика
  • навигация
  • мониторинг
  • 3D визуализация

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

  • изменение видимости layers
  • замена sources
  • изменение стиля карты
function setMode(mode) {
  map.setLayoutProperty('heatmap', 'visibility', mode === 'analytics' ? 'visible' : 'none');
  map.setLayoutProperty('route-line', 'visibility', mode === 'navigation' ? 'visible' : 'none');
}

Сценарий: работа с кастомными источниками и динамическими данными

Кастомные источники применяются для:

  • API агрегаторов
  • IoT данных
  • потоков сенсоров

Стратегия:

  • нормализация входных данных
  • преобразование в FeatureCollection
  • обновление источника
function normalize(sensorData) {
  return {
    type: 'FeatureCollection',
    features: sensorData.map(d => ({
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [d.lon, d.lat]
      },
      properties: d
    }))
  };
}

Сценарий: управление жизненным циклом карты в SPA

В SPA архитектуре карта:

  • инициализируется один раз
  • пересоздаёт слои при смене маршрута
  • очищает ресурсы при unmount

Типовой цикл:

  • create map
  • add sources/layers
  • update on route change
  • destroy listeners
map.removeLayer('layer-id');
map.removeSource('source-id');

Сценарии полного цикла формируются вокруг постоянного взаимодействия:

  • данные → визуализация → взаимодействие → новое состояние → обновление данных