Обновление изображения

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

Базовый механизм основан на двух шагах: загрузка ресурса и регистрация имени изображения в стиле.

map.loadImage('/images/marker.png', (error, image) => {
  if (error) throw error;

  map.addImage('custom-marker', image);
});

После добавления изображение становится доступным внутри стиля и может использоваться в слоях через icon-image.

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

Ключевым моментом является то, что изображение становится частью внутреннего реестра стиля и кешируется в WebGL-контексте.


Обновление изображения через перерегистрацию ресурса

Обновление изображения в Mapbox GL JS не выполняется «на месте» в смысле изменения пикселей уже зарегистрированного объекта. Вместо этого применяется перезапись ресурса в стиле.

Начиная с современных версий Mapbox GL JS, поддерживается переопределение изображения через повторный вызов регистрации с флагом перезаписи.

map.loadImage('/images/marker-v2.png', (error, image) => {
  if (error) throw error;

  map.addImage('custom-marker', image, { overwrite: true });
});

Механизм работает следующим образом:

  • существующий ресурс удаляется из внутреннего хранилища стиля
  • новое изображение регистрируется под тем же идентификатором
  • все слои, использующие идентификатор, автоматически начинают отображать новую текстуру

Перерисовка происходит через WebGL без необходимости пересоздания слоя.


Замена изображения без явного удаления

Если требуется частое обновление изображения (например, динамический маркер или состояние объекта), используется повторная загрузка и регистрация без промежуточного removeImage.

function updateMarker(url) {
  map.loadImage(url, (error, image) => {
    if (error) return;

    map.addImage('dynamic-marker', image, { overwrite: true });
  });
}

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


Использование removeImage перед обновлением

В более старых архитектурах или при необходимости строгого контроля состояния применяется явное удаление ресурса перед добавлением нового.

if (map.hasImage('custom-marker')) {
  map.removeImage('custom-marker');
}

map.loadImage('/images/marker-v3.png', (error, image) => {
  if (error) throw error;

  map.addImage('custom-marker', image);
});

Этот подход полезен при отладке и при сложной логике переключения наборов иконок.


Обновление изображения через изменение слоя

Изображение, используемое в слое symbol, определяется не напрямую через слой, а через свойство icon-image. Поэтому изменение изображения часто сводится к изменению этого свойства или имени ресурса.

map.setLayoutProperty('points', 'icon-image', 'custom-marker-new');

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

map.loadImage('/images/marker-new.png', (error, image) => {
  if (error) return;

  map.addImage('custom-marker-new', image);
});

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


Обновление изображений через состояние данных

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

Источник данных содержит признак состояния:

{
  type: 'Feature',
  geometry: {
    type: 'Point',
    coordinates: [30, 50]
  },
  properties: {
    status: 'active'
  }
}

Слой использует выражение выбора изображения:

map.addLayer({
  id: 'devices',
  type: 'symbol',
  source: 'devices-source',
  layout: {
    'icon-image': [
      'match',
      ['get', 'status'],
      'active', 'icon-active',
      'inactive', 'icon-inactive',
      'icon-default'
    ]
  }
});

В этом случае «обновление изображения» фактически становится обновлением данных источника:

source.setData(updatedGeojson);

Изображения остаются неизменными, меняется только привязка.


Динамическая замена изображения через таймер или событие

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

async function refreshIcon() {
  map.loadImage(`/images/icon-${Date.now()}.png`, (error, image) => {
    if (error) return;

    map.addImage('live-icon', image, { overwrite: true });
  });
}

setInterval(refreshIcon, 5000);

Такая модель используется в задачах мониторинга, когда визуальные элементы должны отражать изменяющиеся параметры системы.


Работа с изображениями через спрайты стиля

Внутренний механизм Mapbox GL JS основан на спрайтах. Все изображения компонуются в единый атлас, оптимизированный для WebGL.

При обновлении изображения:

  • атлас пересобирается частично или полностью
  • изменяется текстурная привязка
  • слои автоматически получают доступ к новой версии ресурса

При массовом обновлении изображений предпочтительно минимизировать количество вызовов addImage, так как каждое обновление может вызывать перерасчёт GPU-ресурсов.


Кэширование браузера и обновление изображений

При загрузке изображений по URL важную роль играет HTTP-кэш. Часто обновление не отражается визуально из-за кеширования ресурса браузером.

Распространённый метод обхода — добавление параметра версии:

map.loadImage('/images/marker.png?v=3', (error, image) => {
  if (error) return;

  map.addImage('custom-marker', image, { overwrite: true });
});

Такой подход гарантирует получение нового бинарного содержимого без необходимости ручного сброса кэша.


Обновление изображений в растровых слоях

Для слоёв типа raster изображение не обновляется напрямую через addImage, так как используется внешний тайловый источник. Однако возможно переключение URL источника:

map.getSource('raster-source').setTiles([
  'https://tiles.server.com/new/{z}/{x}/{y}.png'
]);

При этом перезагрузка тайлов приводит к обновлению визуального изображения на карте.


Управление ресурсами изображений в памяти WebGL

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

Стратегия управления включает:

  • проверку существования ресурса через hasImage
  • удаление устаревших изображений через removeImage
  • ограничение количества уникальных идентификаторов
  • переиспользование одного имени ресурса
if (map.hasImage('temp-icon')) {
  map.removeImage('temp-icon');
}

Обновление изображений в контексте анимации

Для псевдоанимированных эффектов применяется последовательная замена текстур под одним идентификатором.

const frames = ['/f1.png', '/f2.png', '/f3.png'];

let index = 0;

function animate() {
  map.loadImage(frames[index], (error, image) => {
    if (error) return;

    map.addImage('animated-icon', image, { overwrite: true });
    index = (index + 1) % frames.length;
  });
}

setInterval(animate, 100);

Такой подход создаёт эффект кадровой анимации без использования canvas или shader-логики.


Особенности повторного использования идентификаторов

Идентификатор изображения является ключевым связующим элементом между стилем и ресурсом. Все обновления должны учитывать, что:

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

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