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

Загрузка тайлов в Leaflet основана на стандартном HTML-механизме <img>-элементов, где каждый тайл представляет собой отдельный запрос к серверу. Библиотека не выполняет низкоуровневую обработку HTTP-запросов, а делегирует это браузеру, получая уведомления о результате загрузки через события изображения.

Каждый слой L.TileLayer формирует URL тайла на основе шаблона:

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  maxZoom: 19
}).addTo(map);

При каждом перемещении карты вычисляются координаты тайлов (x, y, z), после чего создаются изображения:

<img src=".../z/x/y.png">

Браузер самостоятельно инициирует запросы и кэширование, а Leaflet отслеживает события загрузки через DOM API.

Типы ошибок при загрузке тайлов

Ошибки тайлов не формализованы как единая сущность — они проявляются через поведение <img>:

HTTP-ошибки (404, 403, 500) Сервер возвращает некорректный ответ или отсутствующий ресурс. Браузер триггерит событие error.

Сетевые ошибки Отсутствие соединения, DNS-сбои, обрыв соединения. Также приводят к error у изображения.

CORS-ограничения При попытке доступа к тайлам с ограниченной политикой кросс-доменных запросов изображение может не отобразиться, особенно при использовании canvas-операций поверх тайлов.

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

Событие tileerror

Leaflet предоставляет событие tileerror, которое срабатывает при неудачной загрузке тайла:

const layer = L.tileLayer(urlTemplate);

layer.on('tileerror', function (error, tile) {
  console.log('Ошибка тайла:', error, tile);
});

Объект tile содержит DOM-элемент <img>, а также координаты:

tile.coords; // {x, y, z}

Событие позволяет реализовать централизованную обработку ошибок загрузки.

Замена битых тайлов через errorTileUrl

Leaflet поддерживает встроенный механизм замены изображения при ошибке:

L.tileLayer(urlTemplate, {
  errorTileUrl: '/images/empty-tile.png'
}).addTo(map);

Этот параметр подставляет резервное изображение вместо отсутствующего тайла, предотвращая визуальные «дыры» на карте.

Особенности поведения:

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

Повторные попытки загрузки (retry logic)

Leaflet не реализует встроенный retry-механизм, поэтому повторные попытки организуются вручную:

L.TileLayer.Retry = L.TileLayer.extend({
  initialize: function (url, options) {
    L.TileLayer.prototype.initialize.call(this, url, options);
    this._retryLimit = options.retryLimit || 3;
  },

  createTile: function (coords, done) {
    const tile = L.TileLayer.prototype.createTile.call(this, coords, done);
    tile._retryCount = 0;

    tile.addEventListener('error', () => {
      if (tile._retryCount < this._retryLimit) {
        tile._retryCount++;
        tile.src = tile.src.split('?')[0] + '?retry=' + tile._retryCount;
      } else {
        done(null, tile);
      }
    });

    return tile;
  }
});

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

Проблемы кэширования и повторной загрузки

Браузер активно кэширует тайлы, что может маскировать ошибки:

  • HTTP 304 может скрывать нестабильность сервера
  • агрессивный cache-control приводит к устаревшим данным
  • service worker может перехватывать запросы и возвращать некорректные ответы

Для диагностики часто используется отключение кэша через параметры URL:

L.tileLayer(urlTemplate + '?v=' + Date.now());

Offline-сценарии

При отсутствии сети <img> генерирует ошибку загрузки. В Leaflet это проявляется массовым tileerror.

Практика обработки включает:

  • переключение на локальный тайловый слой
  • использование заранее сохранённых тайлов
  • подмена errorTileUrl на офлайн-заглушку

Пример переключения источника:

map.on('tileerror', function () {
  map.removeLayer(onlineLayer);
  map.addLayer(offlineLayer);
});

Кастомная обработка через createTile

Более низкоуровневый контроль достигается через переопределение createTile:

L.TileLayer.Custom = L.TileLayer.extend({
  createTile: function (coords, done) {
    const img = document.createElement('img');

    img.onl oad = function () {
      done(null, img);
    };

    img.oner ror = function () {
      img.src = '/fallback.png';
      done(null, img);
    };

    img.src = this.getTileUrl(coords);

    return img;
  }
});

Этот метод позволяет полностью контролировать жизненный цикл тайла.

Логирование и диагностика

Для анализа ошибок загрузки применяется сбор событий:

layer.on('tileloadstart', function (e) {
  console.log('Загрузка тайла:', e.coords);
});

layer.on('tileload', function (e) {
  console.log('Загружен тайл:', e.coords);
});

layer.on('tileerror', function (e) {
  console.log('Ошибка тайла:', e.tile.src, e.coords);
});

Такой подход позволяет выявлять:

  • нестабильные серверные регионы
  • проблемные zoom-уровни
  • отсутствующие плитки в генерации

Влияние поддоменов и балансировки

Использование {s} в URL шаблоне распределяет нагрузку:

L.tileLayer('https://{s}.tile.server.com/{z}/{x}/{y}.png', {
  subdomains: ['a', 'b', 'c']
});

Ошибки могут быть связаны с:

  • недоступностью одного поддомена
  • несинхронностью кэша между серверами
  • различиями в генерации тайлов

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

Retina-тайлы и ошибки разрешения

При detectRetina: true запрашиваются тайлы @2x. Ошибки часто возникают из-за отсутствия высокоразрешённых ресурсов:

L.tileLayer(urlTemplate, {
  detectRetina: true
});

Если сервер не поддерживает retina-версии, происходят:

  • 404 на @2x тайлы
  • падение качества при fallback на обычные тайлы
  • визуальные артефакты при масштабировании

Service Worker и перехват запросов

При использовании Service Worker ошибки тайлов могут не соответствовать реальному состоянию сети:

  • подмена ответа кэшем
  • возврат HTML вместо изображения
  • некорректные заголовки Content-Type

В результате tileload может не соответствовать фактическому содержимому.

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

При деградации источника тайлов возникает каскад запросов:

  • повторные попытки браузера
  • рост числа error событий
  • блокировка рендера карты из-за ожидания тайлов

Оптимизация включает ограничение параллельных загрузок:

L.tileLayer(urlTemplate, {
  updateWhenIdle: true,
  keepBuffer: 2
});

Это снижает нагрузку при нестабильных источниках.

Контроль целостности тайлов

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

img.onl oad = function () {
  if (img.width === 0 || img.height === 0) {
    handleInvalidTile(img);
  }
};

Такая проверка позволяет выявлять «пустые» тайлы, которые не вызывают error, но фактически некорректны.