Удаление неиспользуемых ресурсов

В Mapbox GL JS карта рассматривается как контейнер для набора взаимосвязанных ресурсов: источников данных (sources), слоёв (layers), изображений (images), шрифтовых глифов (glyphs), тайлов и стилей. Каждый из этих компонентов существует в рамках состояния карты и может динамически добавляться, обновляться или удаляться.

Ключевой особенностью архитектуры является строгая связность слоёв и источников: слой не может существовать без источника, а удаление источника при наличии зависимых слоёв приводит к ошибкам выполнения. Поэтому управление удалением ресурсов требует соблюдения порядка операций и понимания зависимостей внутри стиля.


Источники данных (sources)

Источники данных представляют собой фундаментальный уровень абстракции. Они определяют, откуда поступают геоданные:

  • GeoJSON
  • vector tiles
  • raster tiles
  • image sources
  • video sources

Каждый источник регистрируется в стиле карты и получает уникальный идентификатор.

Основные операции:

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

Удаление источника выполняется через:

map.removeSource('cities');

Ограничения удаления источников

Удаление источника возможно только при отсутствии зависимых слоёв. При нарушении этого условия возникает ошибка, поскольку слои продолжают ссылаться на удалённый объект.

Типичная последовательность корректного удаления:

  1. Удаление всех слоёв, использующих источник
  2. Удаление источника
if (map.getLayer('cities-layer')) {
  map.removeLayer('cities-layer');
}

if (map.getSource('cities')) {
  map.removeSource('cities');
}

Слои (layers)

Слои представляют визуальное отображение данных источника. Каждый слой связан с конкретным источником и определяет:

  • тип визуализации (circle, line, fill, symbol, raster)
  • стиль отображения
  • фильтры данных
  • порядок отрисовки

Добавление слоя:

map.addLayer({
  id: 'cities-layer',
  type: 'circle',
  source: 'cities',
  paint: {
    'circle-radius': 6,
    'circle-color': '#ff0000'
  }
});

Удаление слоя:

map.removeLayer('cities-layer');

Зависимости слоёв и порядок удаления

Слои в стиле формируют стек отрисовки. Удаление должно учитывать:

  • наличие зависимых слоёв (layout dependencies)
  • использование одного источника несколькими слоями
  • порядок удаления от верхних слоёв к нижним

При наличии нескольких слоёв на одном источнике удаление источника возможно только после удаления всех слоёв.


Загруженные изображения и спрайты

Mapbox GL JS поддерживает использование пользовательских изображений в символических слоях. Эти изображения добавляются в глобальный реестр стиля:

map.addImage('marker-icon', imageData);

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

map.addLayer({
  id: 'points',
  type: 'symbol',
  source: 'points',
  layout: {
    'icon-image': 'marker-icon'
  }
});

Удаление изображения:

map.removeImage('marker-icon');

Особенности удаления изображений

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

Практика безопасного удаления включает:

  • предварительный анализ всех symbol layers
  • удаление или обновление layout свойств
  • только затем вызов removeImage

Шрифтовые глифы (glyphs)

Глифы используются для рендеринга текста в symbol слоях. Они загружаются из font stacks, указанных в стиле карты.

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

"glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf"

Удаление глифов напрямую не выполняется через API Mapbox GL JS, поскольку они управляются стилем. Управление осуществляется через:

  • смену стиля карты
  • изменение конфигурации glyphs в style.json

Очистка при смене стиля

Метод setStyle полностью заменяет текущий стиль карты, включая:

  • источники
  • слои
  • изображения
  • фильтры
  • layout и paint состояния
map.setStyle('mapbox://styles/mapbox/light-v11');

Поведение при смене стиля

При вызове setStyle происходит:

  • уничтожение текущего дерева слоёв
  • освобождение источников
  • очистка изображений
  • повторная инициализация стиля

Важно учитывать, что асинхронная природа загрузки стиля требует повторной регистрации пользовательских ресурсов после события style.load.

map.on('style.load', () => {
  map.addSource('custom', {...});
  map.addLayer({...});
});

Жизненный цикл карты и удаление ресурсов

Жизненный цикл карты включает несколько стадий:

  1. Инициализация
  2. Загрузка стиля
  3. Добавление пользовательских ресурсов
  4. Динамическое обновление
  5. Очистка и уничтожение

При уничтожении карты:

map.remove();

Происходит:

  • удаление WebGL контекста
  • освобождение всех слоёв
  • очистка источников
  • остановка таймеров и событий

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


Предотвращение утечек памяти

Утечки памяти в Mapbox GL JS чаще всего возникают не внутри карты, а на уровне приложения.

Основные источники:

  • неотписанные event listeners
  • сохранённые ссылки на map instance
  • неочищенные таймеры обновления данных
  • динамически добавленные источники без удаления

Управление событиями

Подписки на события должны сопровождаться обязательным удалением:

function onMove() {
  // обработка
}

map.on('move', onMove);
map.off('move', onMove);

Особенно критично это при частом создании и уничтожении карты (SPA).


Динамические данные и потоковое обновление

При работе с realtime данными часто используются:

  • setData для GeoJSON источников
  • обновление paint properties
  • временные слои
map.getSource('track').setData(newData);

При остановке потока данных требуется:

  • прекращение интервалов
  • удаление источника
  • удаление слоёв визуализации
clearInterval(updateTimer);

if (map.getLayer('track-layer')) {
  map.removeLayer('track-layer');
}

if (map.getSource('track')) {
  map.removeSource('track');
}

Удаление ресурсов в компонентах приложений

В архитектурах на React, Vue или аналогичных системах критично учитывать повторное создание карты.

Типичный сценарий:

  • создание карты при монтировании компонента
  • удаление карты при размонтировании
useEffect(() => {
  const map = new mapboxgl.Map({ ... });

  return () => {
    map.remove();
  };
}, []);

При отсутствии корректного удаления возникают:

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

Управление порядком удаления ресурсов

Корректный порядок удаления обеспечивает отсутствие ошибок:

  1. Остановка внешних процессов (таймеры, подписки)
  2. Удаление слоёв
  3. Удаление изображений
  4. Удаление источников
  5. Удаление карты

Нарушение порядка приводит к каскадным ошибкам зависимостей, особенно при сложных стилях с множеством слоёв.


Практики безопасной очистки

В устойчивых приложениях используется централизованное управление ресурсами:

  • хранение идентификаторов слоёв и источников
  • регистрация добавленных изображений
  • контроль зависимостей между компонентами
  • единая функция teardown

Типовой подход включает реестр ресурсов:

const registry = {
  layers: [],
  sources: [],
  images: []
};

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


Особенности удаления векторных и растровых источников

Векторные источники чаще имеют несколько слоёв-потребителей, что усложняет удаление.

Растровые источники могут использоваться в фонах и требуют проверки на:

  • overlay слои
  • terrain конфигурации
  • custom raster rendering

Удаление таких источников выполняется только после полной проверки всех зависимостей в стиле.


Долгоживущие карты и деградация ресурсов

В длительно работающих приложениях (дашборды, мониторинг, трекинг) наблюдается накопление:

  • временных слоёв
  • устаревших источников
  • неиспользуемых изображений

Периодическая очистка выполняется через:

  • пересборку стиля
  • пересоздание карты
  • удаление временных сущностей по TTL

Такой подход предотвращает деградацию производительности WebGL и утечки памяти на уровне браузера.