Загрузка внешних изображений

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

В основе загрузки лежит метод map.loadImage, который асинхронно получает изображение по URL и преобразует его в формат, пригодный для использования внутри WebGL-контекста.


Базовый механизм загрузки изображения

Загрузка внешнего изображения выполняется через API карты:

map.loadImage(
  'https://example.com/icon.png',
  (error, image) => {
    if (error) throw error;

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

Ключевые этапы процесса

  • загрузка ресурса по URL;
  • декодирование изображения браузером;
  • преобразование в ImageData/HTMLImageElement;
  • регистрация изображения внутри стиля карты через addImage.

Регистрация изображения в стиле карты

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

map.addImage('custom-marker', image, {
  pixelRatio: 2
});

Параметры addImage

  • id — уникальный идентификатор изображения в стиле;
  • image — объект изображения;
  • options.pixelRatio — масштабирование для Retina-дисплеев;
  • options.sdf — режим Signed Distance Field для окрашиваемых иконок.

Использование изображения в слоях

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

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

Особенности использования

  • имя изображения совпадает с идентификатором в addImage;
  • иконка рендерится через WebGL без дополнительной растеризации на стороне CPU;
  • поддерживается динамическое масштабирование и поворот.

Динамическая загрузка изображений

При работе с изменяющимися данными изображения часто загружаются в момент появления слоя или данных.

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

    if (!map.hasImage(name)) {
      map.addImage(name, image);
    }
  });
}

Проверка существования изображения

if (map.hasImage('custom-marker')) {
  // изображение уже зарегистрировано
}

Использование изображений в заливках (fill patterns)

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

map.addLayer({
  id: 'water-pattern',
  type: 'fill',
  source: 'water',
  paint: {
    'fill-pattern': 'water-texture'
  }
});

Такие изображения должны быть предварительно добавлены через addImage.


Работа с асинхронностью загрузки

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

map.on('load', () => {
  map.loadImage('/icon.png', (error, image) => {
    if (error) return;

    map.addImage('icon', image);
  });
});

Важный момент

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


Кросс-доменные ограничения (CORS)

При загрузке внешних изображений применяется политика CORS.

Типичные требования

  • сервер должен отдавать заголовок Access-Control-Allow-Origin;
  • изображение должно быть доступно без редиректов;
  • HTTPS обязателен при использовании защищённых страниц.

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

Изображение можно заменить без пересоздания слоя:

map.updateImage('custom-marker', newImage);

Если прямое обновление недоступно, используется последовательность:

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

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

Использование SDF-иконок

Signed Distance Field позволяет перекрашивать иконки через стили:

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

Возможности SDF

  • изменение цвета через icon-color;
  • масштабирование без потери качества;
  • единый источник изображения для разных состояний UI.

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

Загруженные изображения хранятся в стиле карты:

  • повторный addImage с тем же ID игнорируется;
  • hasImage предотвращает дублирование;
  • WebGL текстуры кэшируются в памяти GPU.

Работа с удалением изображений

map.removeImage('custom-marker');

Поведение

  • изображение удаляется из стиля;
  • связанные слои продолжают ссылаться на ID до перерисовки;
  • повторное добавление возможно без перезагрузки страницы.

Использование изображений из спрайтов и внешних источников

Mapbox Mapbox предоставляет встроенную систему спрайтов, однако внешние изображения используются в случаях:

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

Оптимизация загрузки

Рекомендации по производительности

  • использовать изображения в формате PNG или WebP;
  • ограничивать разрешение до необходимого размера;
  • применять pixelRatio вместо физического увеличения размеров;
  • избегать частой перерегистрации изображений.

Использование изображений как маркеров

Часто изображения применяются совместно с geojson источниками:

map.addSource('points', {
  type: 'geojson',
  data: {
    type: 'FeatureCollection',
    features: [
      {
        type: 'Feature',
        geometry: {
          type: 'Point',
          coordinates: [30.5, 50.5]
        }
      }
    ]
  }
});
map.addLayer({
  id: 'points-layer',
  type: 'symbol',
  source: 'points',
  layout: {
    'icon-image': 'custom-marker',
    'icon-allow-overlap': true
  }
});

Обработка ошибок загрузки

map.loadImage(url, (error, image) => {
  if (error) {
    console.error('Ошибка загрузки изображения');
    return;
  }

  map.addImage('icon', image);
});

Типичные причины ошибок

  • недоступный URL;
  • CORS-блокировка;
  • некорректный формат изображения;
  • сетевые таймауты.

Использование canvas как источника изображения

Изображение может быть создано динамически:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

canvas.width = 50;
canvas.height = 50;

ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 50, 50);

map.addImage('canvas-icon', ctx.getImageData(0, 0, 50, 50));

Заключительные технические особенности

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