Архитектура
рендеринга и ключевые узкие места
MapLibre GL JS использует WebGL-рендеринг и многопоточную архитектуру
с вынесением части вычислений в воркеры. Производительность карты в
продакшене определяется тремя основными слоями:
- Сетевой уровень (загрузка тайлов, стилей, шрифтов,
спрайтов)
- Данные и стиль (GeoJSON, векторные тайлы, фильтры,
выражения)
- GPU-рендеринг (дорожки отрисовки, символы,
растровые слои)
Наибольшие проблемы обычно возникают не в отрисовке как таковой, а в
перегрузке источников данных и избыточной сложности стилей.
Оптимизация источников
данных
Переход от GeoJSON к
векторным тайлам
GeoJSON подходит для небольших наборов данных, но становится
критическим узким местом при масштабировании.
Проблемы GeoJSON:
- Полная пересылка данных при каждом обновлении
- Отсутствие тайловой структуры
- Высокая нагрузка на CPU при парсинге
Рекомендуемый подход — использование векторных тайлов (Vector
Tiles).
Подготовка данных обычно выполняется через инструменты типа
tippecanoe:
- генерация MBTiles
- предагрегация геометрии
- упрощение геометрий по zoom level
Оптимизация
GeoJSON при неизбежном использовании
Если GeoJSON остаётся частью архитектуры:
- уменьшение количества координат (simplification)
- удаление лишних properties
- разбиение на чанки
- использование
generateId: true для стабильного
diff
Пример:
map.addSource('points', {
type: 'geojson',
data: '/data/points.json',
generateId: true
});
Упрощение геометрии и
уровни детализации
Simplify по zoom уровням
Чем выше zoom — тем больше деталей можно позволить, но базовые уровни
должны быть максимально облегчены.
Ключевой принцип:
- z0–z5: сильно упрощённые геометрии
- z6–z10: средняя детализация
- z11+: полная детализация
Оптимизация стилей MapLibre
Минимизация количества слоёв
Каждый слой — это отдельный проход рендеринга. Избыточное количество
слоёв резко снижает FPS.
Рекомендации:
- объединение слоёв с одинаковыми стилями
- использование
filter вместо дублирования слоёв
- отказ от дробления символов на множество категорий
Оптимизация filter и
expressions
Сложные выражения вычисляются на GPU/CPU в зависимости от типа
слоя.
Проблемные конструкции:
- глубокие
case-цепочки
- вложенные
match с десятками значений
- динамические вычисления на каждом кадре
Более эффективно:
- перенос логики в данные (preprocessing)
- минимизация вычислений в style runtime
Layout vs Paint:
правильное распределение
- layout properties пересчитываются реже, но
тяжелее
- paint properties обновляются чаще, но дешевле
Оптимально:
- статические параметры → layout
- анимации и визуальные эффекты → paint
Оптимизация символов (labels,
icons)
Конфликты и collision
detection
Символические слои — одна из самых дорогих частей рендеринга.
Критические настройки:
text-allow-overlap: false
icon-allow-overlap: false
- ограничение
symbol-placement
Пример:
{
id: 'labels',
type: 'symbol',
source: 'points',
layout: {
'text-field': ['get', 'name'],
'text-size': 12,
'text-allow-overlap': false
}
}
Уменьшение количества
символов
Практики:
- кластеризация точек
- агрегация на сервере
- отображение только на определённых zoom уровнях
Кластеризация данных
Для точечных данных критично использовать кластеризацию:
map.addSource('points', {
type: 'geojson',
data: '/points.json',
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
Эффект:
- резкое снижение количества rendered features
- уменьшение нагрузки на layout и collision engine
Управление слоями и
видимостью
Использование minzoom и
maxzoom
Каждый слой должен иметь ограничение видимости:
paint: {},
minzoom: 6,
maxzoom: 14
Это снижает:
- количество отрисовываемых объектов
- нагрузку на GPU
- стоимость фильтрации
Динамическое скрытие слоёв
Слои, не попадающие в viewport, должны отключаться:
map.setLayoutProperty('layer-id', 'visibility', 'none');
Оптимизация загрузки
ресурсов
Кэширование тайлов
MapLibre активно использует HTTP-кэш, поэтому важно:
- использовать стабильные URL
- избегать query string с timestamp без необходимости
- включать CDN
Позволяет внедрить авторизацию и кеширование:
transformRequest: (url, resourceType) => {
return {
url,
headers: {
'Authorization': 'Bearer TOKEN'
}
};
}
Сжатие и протоколы
Критично включать:
- gzip / brotli для tiles
- HTTP/2 или HTTP/3
- CDN с edge caching
Оптимизация рендеринга WebGL
Управление FPS и repaint
MapLibre перерисовывает сцену при изменениях состояния. Лишние
вызовы:
setData
setLayoutProperty
setPaintProperty
следует минимизировать.
Отключение ненужных опций
карты
При статичных или полустатичных картах:
new maplibregl.Map({
container: 'map',
interactive: false,
attributionControl: false
});
Оптимизация антисериализации
- избегать
preserveDrawingBuffer: true
- использовать
antialias: false для больших слоёв
- отключать
fadeDuration при ненужных переходах
Управление памятью
Утечки через Map instance
Карта должна корректно уничтожаться:
map.remove();
Игнорирование приводит к:
- росту WebGL контекста
- утечкам текстур
- деградации FPS
Переиспользование источников
Лучше обновлять данные, чем пересоздавать источник:
source.setData(newData);
Оптимизация шрифтов и
спрайтов
Glyphs
Шрифты загружаются как SDF-тайлы. Оптимизация:
- ограничение используемых символов
- генерация subset fonts
- использование одного font stack
Sprites
Спрайты должны быть:
- атласированы
- сжатые (WebP/PNG оптимизация)
- кэшируемые через CDN
Ограничение области карты
Max bounds
Ограничение области снижает количество тайлов:
map.setMaxBounds([
[longitudeMin, latitudeMin],
[longitudeMax, latitudeMax]
]);
Оптимизация обновлений
состояния
Батчинг изменений
Множественные изменения стилей должны быть сгруппированы:
Плохо:
- 10 вызовов
setLayoutProperty
Лучше:
- единое обновление конфигурации слоя
Debounce событий
Для событий движения:
следует использовать debounce или throttle
Производственные паттерны
архитектуры
Разделение слоёв
ответственности
- data layer (источники)
- style layer (визуализация)
- interaction layer (события)
Такое разделение снижает связность и облегчает оптимизацию.
Lazy loading слоёв
Слои должны загружаться:
- только при достижении zoom
- только при попадании в viewport
- только по требованию пользователя
Профилирование
производительности
Используется для анализа:
- frame time
- long tasks
- layout shifts
map.showTileBoundaries = true;
map.showCollisionBoxes = true;
Позволяет выявить:
- перегруженные тайлы
- проблемные символы
- лишние рендер-проходы
Метрики
Ключевые показатели:
- FPS (целевой минимум 30–60)
- tile loading time
- number of rendered layers
- number of visible features
Стратегии масштабирования
Серверная агрегация
Чем больше данных, тем важнее:
- pre-aggregation
- spatial indexing (H3, S2)
- генерация тайлов на сервере
Progressive rendering
- сначала упрощённые тайлы
- затем детализация
- затем символы
Итоговые
принципы производственного использования
- минимизация GeoJSON
- строгая тайловая архитектура
- контроль количества слоёв
- агрессивная кластеризация
- ограничение символов
- предобработка данных на сервере
- кэширование всего, что возможно
- контроль repaint и событий
- освобождение ресурсов через destroy/remove