При отображении пространственных данных на веб-карте часто используются объекты с большим количеством вершин: автомобильные дороги, административные границы, береговые линии, маршруты, инженерные сети и другие сложные геометрии. Чем больше координат содержит объект, тем больше вычислительных ресурсов требуется для его хранения, передачи по сети и визуализации.
Упрощение геометрии (Geometry Simplification) — процесс уменьшения количества вершин без существенной потери визуальной формы объекта. Основная цель заключается в сокращении объёма данных и повышении производительности карты.
Для Mapbox GL JS упрощение особенно важно при работе с:
Рассмотрим линию, описывающую границу региона.
{
"type": "LineString",
"coordinates": [
[37.6176, 55.7558],
[37.6177, 55.7559],
[37.6178, 55.7560],
...
]
}
Если линия содержит десятки тысяч точек, возникают следующие проблемы:
Каждая вершина хранит минимум две координаты.
Например:
| Количество вершин | Размер данных |
|---|---|
| 100 | Очень маленький |
| 1 000 | Небольшой |
| 10 000 | Значительный |
| 100 000 | Очень большой |
Передача таких данных через интернет может заметно замедлить загрузку карты.
Mapbox GL JS выполняет:
Чем больше вершин, тем больше вычислений необходимо выполнить.
При масштабировании и перемещении карты браузер вынужден постоянно перерасчитывать положение объектов.
Сложные геометрии способны вызвать:
Mapbox GL JS использует несколько уровней оптимизации.
Если данные публикуются как векторные тайлы, сервер обычно автоматически упрощает геометрию для каждого уровня масштаба.
Например:
Это позволяет отображать только необходимый объём данных.
При работе с GeoJSON библиотека также выполняет внутренние оптимизации во время разбиения данных на тайлы.
Пример источника:
map.addSource('roads', {
type: 'geojson',
data: 'roads.geojson'
});
После загрузки данные преобразуются во внутреннее представление, где используются механизмы генерализации геометрии для разных масштабов.
Наиболее распространённым алгоритмом упрощения является Douglas–Peucker.
Его идея состоит в удалении точек, которые мало влияют на общую форму линии.
Исходная линия:
A ----- B ----- C ----- D ----- E
После упрощения:
A ------------------------- E
Если точки B, C и D не создают существенных отклонений, они могут быть удалены.
Алгоритм:
Берёт первую и последнюю точку.
Строит между ними прямую.
Находит наиболее удалённую промежуточную точку.
Если расстояние превышает порог:
Если расстояние меньше порога:
Ключевой параметр любого алгоритма упрощения — tolerance (допуск).
Небольшой допуск:
Исходно: 10000 точек
После: 9500 точек
Геометрия практически не изменится.
Большой допуск:
Исходно: 10000 точек
После: 500 точек
Производительность значительно повысится, но форма объекта может заметно исказиться.
Наиболее эффективный подход — выполнять упрощение до передачи данных в браузер.
Популярная библиотека:
import simplify from 'simplify-js';
Подготовка массива точек:
const points = [
{ x: 10, y: 20 },
{ x: 12, y: 25 },
{ x: 15, y: 27 },
{ x: 18, y: 30 }
];
Упрощение:
const simplified = simplify(points, 5, true);
Параметры:
Пример линии:
const route = {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [...]
}
};
Перед отображением можно обработать координаты:
const simplifiedCoordinates =
simplify(convertedPoints, 10, true);
После этого создаётся новый GeoJSON-объект.
route.geometry.coordinates =
simplifiedCoordinates;
Затем источник обновляется:
map.getSource('route').setData(route);
Библиотека Turf предоставляет готовый инструмент упрощения.
Подключение:
import * as turf from '@turf/turf';
Упрощение объекта:
const simplified =
turf.simplify(feature, {
tolerance: 0.01,
highQuality: true
});
Параметры:
| Параметр | Описание |
|---|---|
| tolerance | Степень упрощения |
| highQuality | Более точный результат |
| mutate | Изменение исходного объекта |
Исходный GeoJSON:
fetch('route.geojson')
.then(response => response.json())
.then(data => {
const simplified =
turf.simplify(data, {
tolerance: 0.005,
highQuality: true
});
map.addSource('route', {
type: 'geojson',
data: simplified
});
});
В результате на карту попадёт уже облегчённая геометрия.
Упрощение применяется не только к линиям.
Полигон:
{
"type": "Polygon",
"coordinates": [...]
}
Может содержать тысячи точек по периметру.
После генерализации:
Пример:
const simplifiedPolygon =
turf.simplify(polygon, {
tolerance: 0.001
});
Чрезмерное упрощение способно привести к ошибкам.
Например:
До:
______
/ \
| |
\______/
После:
______
| |
|______|
Теряются важные детали объекта.
Возможны проблемы:
Поэтому величину допуска необходимо подбирать экспериментально.
Детализация карты должна зависеть от zoom.
Типичная стратегия:
| Zoom | Детализация |
|---|---|
| 0–5 | Максимальное упрощение |
| 6–10 | Среднее упрощение |
| 11–15 | Высокая детализация |
| 16+ | Полная геометрия |
Это позволяет не отображать лишние данные на дальних масштабах.
При использовании векторных тайлов генерализация выполняется автоматически.
Преимущества:
По этой причине крупные картографические проекты редко используют огромные GeoJSON-файлы напрямую.
Mapbox GL JS использует библиотеку GeoJSON-VT для разбиения GeoJSON на тайлы.
Источник можно настроить следующим образом:
map.addSource('regions', {
type: 'geojson',
data: regions
});
Внутри GeoJSON-VT применяется собственный механизм генерализации, основанный на допуске, зависящем от масштаба тайла.
Это позволяет автоматически уменьшать количество вершин при отдалении карты.
До оптимизации полезно подсчитать число координат.
Пример:
const count =
feature.geometry.coordinates.length;
console.log(count);
После обработки:
console.log(
simplified.geometry.coordinates.length
);
Результат может выглядеть так:
До: 18452 точки
После: 2134 точки
Снижение объёма более чем в восемь раз часто практически незаметно визуально.
| Степень упрощения | Производительность | Точность |
|---|---|---|
| Минимальная | Средняя | Очень высокая |
| Умеренная | Высокая | Высокая |
| Сильная | Очень высокая | Средняя |
| Агрессивная | Максимальная | Низкая |
Оптимальный вариант обычно находится между высокой и очень высокой точностью.
В некоторых приложениях геометрия может упрощаться в зависимости от текущего масштаба карты.
Пример обработчика:
map.on('zoomend', () => {
const zoom = map.getZoom();
let tolerance;
if (zoom < 5) {
tolerance = 0.05;
} else if (zoom < 10) {
tolerance = 0.01;
} else {
tolerance = 0.001;
}
});
После определения допуска выполняется повторная генерализация данных.
Подобный подход полезен для специализированных ГИС-приложений, работающих с чрезвычайно сложными объектами.
GPS-треки часто содержат множество лишних точек.
Пример:
Запись каждые 1 секунда
Поездка 2 часа
≈ 7200 точек
При отображении всей траектории большинство вершин оказываются избыточными.
Упрощение позволяет:
Для небольших GeoJSON
Для объектов с тысячами вершин
Для крупных наборов данных
Для национальных и мировых карт
Для мобильных приложений
Для высокоточных инженерных данных
Грамотно выполненное упрощение геометрии позволяет значительно сократить объём пространственных данных, уменьшить нагрузку на браузер и видеокарту, повысить скорость рендеринга и обеспечить плавную работу картографических приложений на базе Mapbox GL JS даже при отображении сложных наборов геоданных.