Большие GeoJSON-файлы становятся узким местом в рендеринге геоданных в браузере: увеличивается время парсинга JSON, возрастает нагрузка на память, замедляется пересчёт стилей и падает FPS при панорамировании и зуме.
В контексте MapLibre GL JS ключевая проблема проявляется на нескольких уровнях:
Особенно критичны наборы данных от десятков тысяч до миллионов
объектов, где даже простые операции вроде setData()
приводят к блокировке main thread.
Первый уровень оптимизации выполняется до попадания данных в клиент.
Сложные полигоны с большим числом вершин создают непропорциональную нагрузку на WebGL-пайплайн.
Типовые методы:
Пример упрощения через Turf.js:
import simplify from '@turf/simplify';
const simplified = simplify(geojson, {
tolerance: 0.01,
highQuality: false,
});
Квантование уменьшает точность координат без заметной потери визуального качества:
Один из ключевых подходов — предварительное разбиение:
Это снижает нагрузку на отрисовку в ранних зумах, где детали не нужны.
Наиболее эффективный подход — отказ от GeoJSON в рантайме и переход к тайловой модели.
Обычно применяется генерация через tippecanoe:
tippecanoe -o data.mbtiles \
--layer=points \
--minimum-zoom=0 \
--maximum-zoom=14 \
data.geojson
Далее тайлы подключаются как источник:
map.addSource('tiles', {
type: 'vector',
url: 'mbtiles://data'
});
Преимущества:
Когда переход на vector tiles невозможен, используется тайлинг на клиенте.
Библиотека geojson-vt разбивает GeoJSON на виртуальные тайлы:
Пример:
import geojsonvt from 'geojson-vt';
const tileIndex = geojsonvt(geojson, {
maxZoom: 14,
tolerance: 3,
extent: 4096,
});
Интеграция с MapLibre:
map.addSource('vt', {
type: 'vector',
tiles: ['http://localhost:8080/{z}/{x}/{y}.pbf']
});
Для больших наборов точек критично включать кластеризацию.
map.addSource('points', {
type: 'geojson',
data: geojson,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
Особенности:
Дополнительные слои:
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'points',
filter: ['has', 'point_count']
});
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'points',
filter: ['has', 'point_count']
});
Один из скрытых источников нагрузки — отрисовка объектов вне области видимости.
Оптимизация:
setFilterПример простого bbox-фильтра:
function filterByBounds(features, bounds) {
return features.filter(f => {
const [lng, lat] = f.geometry.coordinates;
return bounds.contains([lng, lat]);
});
}
Частая ошибка — постоянный вызов setData() при малейших
изменениях.
Оптимизации:
let timeout;
function updateSource(data) {
clearTimeout(timeout);
timeout = setTimeout(() => {
map.getSource('points').setData(data);
}, 200);
}
При работе с интерактивными слоями важно стабилизировать идентификаторы:
map.addSource('geo', {
type: 'geojson',
data: geojson,
promoteId: 'id'
});
Преимущества:
Каждый слой увеличивает стоимость композиции кадра.
Практики оптимизации:
match и
casepaint свойств вместо динамических
вычисленийПример менее затратной структуры:
map.addLayer({
id: 'points',
type: 'circle',
source: 'points',
paint: {
'circle-radius': 4,
'circle-color': '#3b82f6'
}
});
Разделение данных по диапазонам зума снижает количество обрабатываемых объектов:
map.addSource('low', { type: 'geojson', data: lowZoomData });
map.addSource('high', { type: 'geojson', data: highZoomData });
И фильтрация слоями:
'minzoom': 0,
'maxzoom': 10
и
'minzoom': 10,
'maxzoom': 24
Тяжёлые операции необходимо переносить из main thread.
Использование Web Workers:
// worker.js
self.onmess age = function (e) {
const simplified = simplify(e.data);
self.postMessage(simplified);
};
Основной поток:
worker.postMessage(geojson);
worker.onmess age = (e) => {
map.getSource('geo').setData(e.data);
};
При повторной загрузке данных важно избегать повторного парсинга:
Особенно эффективно кеширование уже упрощённых данных вместо исходных.
При высоких зумах количество объектов может становиться избыточным.
Подходы:
Типовая высокопроизводительная схема обработки больших GeoJSON:
Такая комбинация позволяет поддерживать десятки и сотни тысяч объектов без деградации интерактивности даже в браузерной среде с ограниченными ресурсами.