Упрощение геометрии

При отображении пространственных данных на веб-карте часто используются объекты с большим количеством вершин: автомобильные дороги, административные границы, береговые линии, маршруты, инженерные сети и другие сложные геометрии. Чем больше координат содержит объект, тем больше вычислительных ресурсов требуется для его хранения, передачи по сети и визуализации.

Упрощение геометрии (Geometry Simplification) — процесс уменьшения количества вершин без существенной потери визуальной формы объекта. Основная цель заключается в сокращении объёма данных и повышении производительности карты.

Для Mapbox GL JS упрощение особенно важно при работе с:

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

Почему большое количество вершин создаёт проблемы

Рассмотрим линию, описывающую границу региона.

{
  "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 выполняет:

  • обработку координат;
  • создание буферов WebGL;
  • расчёт стилей;
  • рендеринг объектов.

Чем больше вершин, тем больше вычислений необходимо выполнить.

Снижение FPS

При масштабировании и перемещении карты браузер вынужден постоянно перерасчитывать положение объектов.

Сложные геометрии способны вызвать:

  • задержки интерфейса;
  • рывки анимации;
  • падение частоты кадров.

Особенности упрощения в Mapbox GL JS

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

Упрощение при создании векторных тайлов

Если данные публикуются как векторные тайлы, сервер обычно автоматически упрощает геометрию для каждого уровня масштаба.

Например:

  • zoom 3 — очень грубая форма;
  • zoom 8 — более детальная;
  • zoom 14 — почти оригинальная геометрия.

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

Упрощение GeoJSON-источников

При работе с GeoJSON библиотека также выполняет внутренние оптимизации во время разбиения данных на тайлы.

Пример источника:

map.addSource('roads', {
    type: 'geojson',
    data: 'roads.geojson'
});

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


Алгоритм Douglas–Peucker

Наиболее распространённым алгоритмом упрощения является Douglas–Peucker.

Его идея состоит в удалении точек, которые мало влияют на общую форму линии.

Исходная линия:

A ----- B ----- C ----- D ----- E

После упрощения:

A ------------------------- E

Если точки B, C и D не создают существенных отклонений, они могут быть удалены.

Принцип работы

Алгоритм:

  1. Берёт первую и последнюю точку.

  2. Строит между ними прямую.

  3. Находит наиболее удалённую промежуточную точку.

  4. Если расстояние превышает порог:

    • точка сохраняется;
    • процесс повторяется рекурсивно.
  5. Если расстояние меньше порога:

    • все промежуточные точки удаляются.

Допуск упрощения

Ключевой параметр любого алгоритма упрощения — tolerance (допуск).

Небольшой допуск:

Исходно: 10000 точек
После:   9500 точек

Геометрия практически не изменится.

Большой допуск:

Исходно: 10000 точек
После:   500 точек

Производительность значительно повысится, но форма объекта может заметно исказиться.


Упрощение данных перед загрузкой в карту

Наиболее эффективный подход — выполнять упрощение до передачи данных в браузер.

Использование simplify-js

Популярная библиотека:

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);

Параметры:

  • массив точек;
  • величина допуска;
  • использование алгоритма Douglas–Peucker.

Упрощение GeoJSON-линий

Пример линии:

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.js

Библиотека Turf предоставляет готовый инструмент упрощения.

Подключение:

import * as turf from '@turf/turf';

Упрощение объекта:

const simplified =
    turf.simplify(feature, {
        tolerance: 0.01,
        highQuality: true
    });

Параметры:

Параметр Описание
tolerance Степень упрощения
highQuality Более точный результат
mutate Изменение исходного объекта

Пример интеграции Turf и Mapbox GL JS

Исходный 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-файлы напрямую.


Параметр tolerance в GeoJSON-VT

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

GPS-треки часто содержат множество лишних точек.

Пример:

Запись каждые 1 секунда
Поездка 2 часа
≈ 7200 точек

При отображении всей траектории большинство вершин оказываются избыточными.

Упрощение позволяет:

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

Практические рекомендации

Для небольших GeoJSON

  • упрощение может не потребоваться вовсе.

Для объектов с тысячами вершин

  • использовать Turf.js или simplify-js перед загрузкой.

Для крупных наборов данных

  • переходить на векторные тайлы.

Для национальных и мировых карт

  • хранить несколько уровней генерализации.

Для мобильных приложений

  • применять более агрессивное упрощение на малых масштабах.

Для высокоточных инженерных данных

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

Грамотно выполненное упрощение геометрии позволяет значительно сократить объём пространственных данных, уменьшить нагрузку на браузер и видеокарту, повысить скорость рендеринга и обеспечить плавную работу картографических приложений на базе Mapbox GL JS даже при отображении сложных наборов геоданных.