Оптимизация больших датасетов

Ограничения рендеринга и модель данных

Mapbox GL JS опирается на WebGL и векторный пайплайн, где каждый кадр строится на основе слоёв и источников данных. При работе с большими датасетами ключевым ограничением становится не объём данных как таковой, а количество одновременно отрисовываемых фич (features) и их сложность.

Основные узкие места:

  • количество геометрий, попадающих в текущий viewport;
  • сложность геометрий (число вершин);
  • количество символов в symbol layers;
  • частота пересчёта стилей и фильтров;
  • размер GeoJSON, обрабатываемого на клиенте.

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


Векторные тайлы вместо GeoJSON

GeoJSON является удобным форматом, но критически неэффективен при масштабировании. При больших наборах данных он приводит к:

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

Оптимальное решение — переход на векторные тайлы (Vector Tiles).

Ключевые преимущества:

  • данные разбиваются по тайлам и загружаются лениво;
  • рендерится только то, что попадает в текущий viewport;
  • возможно многоуровневое упрощение геометрии по zoom level.

Типичная схема:

PostGIS → tile server (или Tippecanoe) → vector tiles → Mapbox GL JS

Использование генерации тайлов (Tippecanoe)

При подготовке больших датасетов применяется генерация MVT-тайлов с разными уровнями детализации.

Важные параметры:

  • --simplify-only-low-zooms — упрощение геометрии на низких zoom;
  • --drop-densest-as-needed — удаление избыточных точек;
  • --generate-ids — стабилизация идентификаторов;
  • --coalesce — объединение мелких объектов.

Результат:

  • снижение количества вершин;
  • уменьшение веса тайлов;
  • ускорение декодирования на клиенте.

Упрощение геометрии (geometry simplification)

Даже при использовании векторных тайлов геометрия может быть избыточной.

Стратегии:

  • упрощение линий и полигонов по алгоритму Douglas-Peucker;
  • хранение нескольких уровней детализации;
  • предрасчёт упрощённых версий на сервере.

Важно учитывать:

  • избыточное упрощение ухудшает визуальную точность;
  • недостаточное — перегружает GPU.

Кластеризация точек

Для больших наборов точечных данных (десятки и сотни тысяч объектов) используется clustering.

В Mapbox GL JS встроенная кластеризация работает на уровне источника:

map.addSource('points', {
  type: 'geojson',
  data: geojsonData,
  cluster: true,
  clusterMaxZoom: 14,
  clusterRadius: 50
});

Преимущества:

  • резкое снижение количества отрисовываемых объектов;
  • агрегация данных по zoom;
  • автоматическая генерация cluster layers.

Типичные слои:

  • слой кластеров;
  • слой числа объектов в кластере;
  • слой одиночных точек.

Разделение данных по уровням zoom

Эффективная стратегия — разделение источников по масштабам:

  • низкий zoom: агрегированные данные;
  • средний zoom: упрощённые геометрии;
  • высокий zoom: полные объекты.

Пример конфигурации:

map.addSource('buildings-low', {
  type: 'vector',
  url: 'mapbox://tiles/buildings_low'
});

map.addSource('buildings-high', {
  type: 'vector',
  url: 'mapbox://tiles/buildings_high'
});

И управление видимостью:

paint: {
  'fill-opacity': [
    'interpolate',
    ['linear'],
    ['zoom'],
    10, 0.2,
    15, 1
  ]
}

Использование фильтров вместо подгрузки новых данных

Фильтрация на стороне клиента может быть дорогой при больших наборах данных.

Оптимизация:

  • перенос фильтрации на уровень тайлов;
  • использование filter только для ограниченных наборов;
  • использование свойств feature properties, индексированных заранее.

Пример:

filter: ['==', ['get', 'type'], 'road']

При больших данных предпочтительно:

  • заранее разделять слои по типам;
  • избегать сложных выражений в фильтрах.

Оптимизация symbol layers

Symbol layers являются наиболее дорогими по производительности.

Основные проблемы:

  • текстовый рендеринг;
  • collision detection (разрешение конфликтов подписи);
  • большое количество иконок.

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

Ограничение количества символов

layout: {
  'symbol-max-angle': 30,
  'text-allow-overlap': false,
  'icon-allow-overlap': false
}

Приоритизация через text-field и zoom

layout: {
  'text-size': [
    'interpolate',
    ['linear'],
    ['zoom'],
    10, 10,
    16, 14
  ]
}

Использование minzoom / maxzoom

Разделение подписей по уровням:

  • дороги появляются на среднем zoom;
  • здания — только на высоком;
  • мелкие POI — ограниченно.

Использование feature-state вместо перерасчёта данных

feature-state позволяет изменять визуальное состояние объекта без перезагрузки источника данных.

Преимущество:

  • обновление только GPU state;
  • отсутствие пересборки источника.

Пример:

map.setFeatureState(
  { source: 'points', id: 123 },
  { selected: true }
);

Использование в стиле:

paint: {
  'circle-color': [
    'case',
    ['boolean', ['feature-state', 'selected'], false],
    '#ff0000',
    '#3388ff'
  ]
}

Использование promoteId и стабильных идентификаторов

Для корректной работы feature-state требуется стабильный идентификатор.

map.addSource('data', {
  type: 'geojson',
  data: geojsonData,
  promoteId: 'id'
});

Это снижает необходимость пересоздания объектов и ускоряет взаимодействие.


Снижение нагрузки на стиль (style complexity)

Каждое выражение в стиле выполняется на GPU и CPU одновременно.

Дорогие конструкции:

  • вложенные case выражения;
  • сложные арифметические интерполяции;
  • частые match по строкам.

Оптимизация:

  • предрасчёт свойств в данных;
  • упрощение выражений;
  • замена динамики на готовые поля.

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

Для масштабных систем используется серверная генерация тайлов:

  • PostGIS + ST_AsMVT;
  • tilelive;
  • Tegola;
  • custom tile pipelines.

Преимущества:

  • минимальная нагрузка на клиент;
  • возможность агрегации;
  • контроль уровня детализации.

Управление количеством draw calls

WebGL производительность напрямую зависит от числа draw calls.

Методы оптимизации:

  • объединение слоёв;
  • уменьшение числа источников;
  • использование atlas-иконок;
  • отказ от частых переключений style layers.

Работа с растром как альтернатива вектору

При экстремально больших датасетах иногда эффективнее использовать raster tiles:

  • отсутствие feature-level интерактивности;
  • минимальная нагрузка на GPU;
  • высокая скорость отображения.

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

  • heatmaps;
  • плотностей;
  • агрегаций.

Использование worker pipeline и предварительной обработки

На клиенте:

  • предварительное упрощение данных в Web Worker;
  • разбиение GeoJSON на чанки;
  • кеширование результатов.

Это снижает:

  • блокировку UI;
  • лаги при загрузке;
  • скачки FPS при рендере.

Минимизация пересчётов источников

Любое изменение setData() вызывает:

  • повторный парсинг GeoJSON;
  • пересборку буферов;
  • обновление GPU buffers.

Оптимизация:

  • обновление только изменённых частей;
  • использование feature-state вместо setData;
  • батчинг обновлений.

Управление плотностью объектов на экране

При высоком zoom уровне перегрузка сцены становится критической.

Методы:

  • ограничение maxzoom для источников;
  • динамическое скрытие слоёв;
  • пространственная агрегация.

Пример:

layout: {
  visibility: [
    'step',
    ['zoom'],
    'none',
    12, 'visible'
  ]
}

Кеширование тайлов и браузерные особенности

Mapbox GL JS использует встроенный кеш тайлов.

Факторы влияния:

  • размер тайла;
  • TTL кеша;
  • повторяемость viewport.

Оптимизация:

  • стабильные URL тайлов;
  • избегание частых изменений источников;
  • использование CDN.

Итоговые практики производительности

Ключевые принципы:

  • перенос вычислений на этап генерации тайлов;
  • минимизация GeoJSON на клиенте;
  • агрегация и кластеризация;
  • снижение сложности стилей;
  • контроль количества символов;
  • использование feature-state вместо перерасчёта данных.