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

Общая модель ошибок загрузки в карте

Leaflet строится вокруг асинхронной загрузки ресурсов: тайлов, GeoJSON-данных, векторных слоёв и внешних API. Большая часть проблем в реальных приложениях связана не с логикой карты, а с сетевыми сбоями, недоступностью тайловых серверов, ошибками CORS и некорректными ответами от источников данных.

Механика обработки ошибок в Leaflet распределена по нескольким уровням:

  • события слоёв (tileerror, loading, load)
  • обработка fetch/XHR при внешних данных
  • fallback-логика для тайлов и изображений
  • контроль состояния карты через события Map
  • пользовательская валидация данных перед добавлением в слой

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


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

Событие tileerror

Наиболее частая проблема — недоступные тайлы в TileLayer. В Leaflet для этого используется событие tileerror, которое срабатывает при невозможности загрузки изображения тайла.

const map = L.map('map').setView([51.505, -0.09], 13);

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

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

Событие предоставляет контекст:

  • URL проблемного тайла
  • DOM-элемент изображения
  • техническую ошибку загрузки

Замена битых тайлов

Распространённая стратегия — подмена изображения при ошибке:

tiles.on('tileerror', function (error, tile) {
  tile.tile.src = '/images/fallback-tile.png';
});

Подход позволяет избежать «дыр» на карте, особенно при нестабильных серверах.


Переопределение errorTileUrl

Leaflet поддерживает встроенный механизм fallback-тайла:

const tiles = L.tileLayer('https://example.com/{z}/{x}/{y}.png', {
  errorTileUrl: '/images/error-tile.png'
});

Особенность:

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

Повторные попытки загрузки тайлов

Нативного retry-механизма нет, но он легко реализуется вручную:

tiles.on('tileerror', function (error, tile) {
  let retries = tile._retryCount || 0;

  if (retries < 3) {
    tile._retryCount = retries + 1;
    setTimeout(() => {
      tile.src = tile.src;
    }, 500);
  } else {
    tile.src = '/images/fallback.png';
  }
});

Такая логика полезна при нестабильных CDN.


Ошибки загрузки GeoJSON

Проблемы при fetch-загрузке

GeoJSON часто подгружается отдельно от карты:

fetch('/data/regions.geojson')
  .then(res => res.json())
  .then(data => {
    L.geoJSON(data).addTo(map);
  })
  .catch(err => {
    console.error('Ошибка загрузки GeoJSON:', err);
  });

Здесь ошибка возникает до передачи данных в Leaflet, поэтому обработка полностью лежит на fetch.


Валидация структуры данных

Даже успешный HTTP-запрос не гарантирует корректность GeoJSON:

function isValidGeoJSON(data) {
  return data &&
    data.type === 'FeatureCollection' &&
    Array.isArray(data.features);
}

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

fetch('/data.geojson')
  .then(r => r.json())
  .then(data => {
    if (!isValidGeoJSON(data)) {
      throw new Error('Некорректный GeoJSON');
    }

    L.geoJSON(data).addTo(map);
  })
  .catch(console.error);

Ошибки внутри L.geoJSON

Leaflet позволяет обрабатывать ошибки на уровне каждого feature через onEachFeature, но прямого error callback нет. Поэтому применяется защитное программирование:

L.geoJSON(data, {
  onEachFeature: function (feature, layer) {
    try {
      if (feature.properties) {
        layer.bindPopup(feature.properties.name);
      }
    } catch (e) {
      console.error('Ошибка обработки feature:', e, feature);
    }
  }
}).addTo(map);

Ошибки инициализации карты

Проверка контейнера

Одна из частых причин сбоев — отсутствие DOM-элемента:

const container = document.getElementById('map');

if (!container) {
  throw new Error('Контейнер карты не найден');
}

const map = L.map(container);

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


Ошибки повторной инициализации

Повторный вызов L.map() на одном элементе приводит к конфликтам:

if (map) {
  map.remove();
}

const map = L.map('map');

Удаление предыдущего экземпляра критично при SPA-навигации.


Сетевые ошибки и CORS

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

При загрузке тайлов и внешних данных возникают:

  • CORS-blocked requests
  • 404 тайлы
  • 500 ошибки сервера
  • тайм-ауты CDN

Leaflet не обрабатывает их автоматически, так как работает поверх браузерных API.


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

tiles.on('loading', () => console.log('Начало загрузки тайлов'));
tiles.on('load', () => console.log('Тайлы загружены'));
tiles.on('tileerror', (err) => console.log('Ошибка тайла', err));

Комбинация событий позволяет локализовать проблему:

  • loading без load → зависание сети
  • частые tileerror → недоступный сервер
  • отсутствие событий → ошибка инициализации слоя

Ошибки пользовательских слоёв

VectorLayer и стилизация

Ошибки часто возникают в style функциях:

L.geoJSON(data, {
  style: function (feature) {
    try {
      return {
        color: feature.properties.color || 'blue'
      };
    } catch (e) {
      return { color: 'gray' };
    }
  }
}).addTo(map);

Leaflet вызывает style для каждого feature, поэтому даже одна ошибка может прервать отрисовку части слоя.


Обработка событий слоя

const layer = L.geoJSON(data);

layer.on('add', () => console.log('Слой добавлен'));
layer.on('remove', () => console.log('Слой удален'));

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


Защита от падения приложения

Глобальный try/catch при инициализации

try {
  const map = L.map('map');
  L.tileLayer(url).addTo(map);
} catch (e) {
  document.getElementById('map').innerHTML =
    'Ошибка инициализации карты';
}

Leaflet редко бросает синхронные ошибки, но защита полезна при динамической конфигурации.


Фолбэк-режим без тайлов

function createOfflineMap() {
  const map = L.map('map');

  L.rectangle([[0,0],[0,0]], {
    color: '#ccc'
  }).addTo(map);

  return map;
}

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


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

Логирование всех сетевых событий

function attachDebug(layer) {
  layer.on('tileerror', e => console.warn('tileerror', e));
  layer.on('loading', () => console.info('loading'));
  layer.on('load', () => console.info('load'));
}

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


Проверка URL тайлов

function validateTileUrl(url) {
  try {
    new URL(url);
    return true;
  } catch {
    return false;
  }
}

Перед созданием TileLayer такая проверка предотвращает множество скрытых ошибок.


Контроль состояния карты

map.whenReady(() => {
  console.log('Карта готова');
});

Этот метод особенно важен при асинхронной загрузке конфигурации.


Частые причины ошибок загрузки

  • неверный шаблон URL тайлов {z}/{x}/{y}
  • блокировка CORS на CDN
  • отсутствие HTTPS при HTTPS-странице
  • неправильный maxZoom/minZoom
  • битые GeoJSON-структуры
  • повторная инициализация карты
  • отсутствие контейнера DOM
  • сетевые тайм-ауты мобильных сетей

Leaflet в этих случаях не скрывает проблему, а передаёт её в браузерный слой, поэтому диагностика всегда требует работы с событиями и сетевыми инструментами.