Fallback стратегии

Проверка поддержки среды выполнения

Ключевой уровень отказоустойчивости в Mapbox GL JS начинается до создания карты — с определения возможности работы WebGL и необходимых браузерных API. При отсутствии поддержки WebGL полноценный рендеринг векторных тайлов невозможен, и требуется переключение на альтернативный режим отображения.

Библиотека предоставляет утилиту проверки:

import mapboxgl from "mapbox-gl";

if (!mapboxgl.supported()) {
  // переход на альтернативную реализацию карты
}

Варианты fallback на этом уровне:

  • статическое изображение карты
  • raster tiles через альтернативную библиотеку (Leaflet)
  • серверный рендеринг (static maps API)
  • упрощённый HTML-контент с координатами

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


Деградация на уровне стиля (Style Fallback)

Mapbox GL JS использует JSON-описание стиля, где определяются источники данных, слои и ресурсы (sprite, glyphs). Поломка любого из этих компонентов может привести к частичной или полной потере визуализации.

Fallback между стилями

Один из устойчивых подходов — переключение между стилями:

map.on("error", () => {
  map.setStyle("mapbox://styles/mapbox/light-v11");
});

Типовые сценарии переключения:

  • кастомный стиль недоступен (ошибка загрузки JSON)
  • отсутствуют ресурсы sprite или glyphs
  • ошибка авторизации токена для конкретного стиля
  • повреждённый стиль (invalid JSON schema)

Деградация внутри одного стиля

Внутри стиля возможна частичная потеря слоёв. Например:

  • удаление пользовательских слоёв
  • отключение 3D extrusion
  • замена сложных fill-extrusion на простые fill

Пример программного удаления проблемных слоёв:

const safeLayers = ["background", "water", "roads"];

safeLayers.forEach(layer => {
  if (map.getLayer(layer)) return;
});

Обработка сетевых сбоев

Mapbox GL JS активно загружает ресурсы по сети: tiles, sprites, glyphs, style JSON. Любой сбой сети требует стратегии повторных попыток и деградации качества.

Retry стратегия

Базовая схема повторных запросов:

function fetchWithRetry(url, retries = 3, delay = 500) {
  return fetch(url).catch(err => {
    if (retries === 0) throw err;
    return new Promise(resolve =>
      setTimeout(() => resolve(fetchWithRetry(url, retries - 1, delay * 2)), delay)
    );
  });
}

Применяется для:

  • style.json
  • vector tiles
  • sprite.png / sprite.json
  • glyph PBF файлов

Exponential backoff

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

  • 500ms
  • 1000ms
  • 2000ms
  • 4000ms

Fallback через transformRequest

Одним из наиболее гибких механизмов является перехват всех сетевых запросов Mapbox GL JS:

const map = new mapboxgl.Map({
  container: "map",
  style: "mapbox://styles/custom/style",
  transformRequest: (url, resourceType) => {
    if (url.includes("tiles")) {
      return {
        url: url.replace("primary-domain.com", "fallback-domain.com")
      };
    }
    return { url };
  }
});

Используется для:

  • переключения CDN
  • подмены домена тайлов
  • добавления auth-заголовков
  • маршрутизации через прокси

Fallback тайловых источников

Vector sources и raster sources могут иметь разные уровни резервирования.

Основной и резервный источник

map.addSource("roads", {
  type: "vector",
  tiles: [
    "https://primary.tiles.com/{z}/{x}/{y}.pbf",
    "https://backup.tiles.com/{z}/{x}/{y}.pbf"
  ],
  minzoom: 0,
  maxzoom: 14
});

Хотя Mapbox GL JS не автоматически балансирует между URL внутри массива в продвинутом режиме, логика может быть реализована через:

  • перехват запросов
  • кастомный tile loader
  • service worker

Service Worker как слой отказоустойчивости

Service Worker обеспечивает перехват сетевых запросов и кэширование геоданных.

Основные стратегии:

  • кэширование style.json
  • хранение vector tiles
  • fallback на устаревшие данные при отсутствии сети

Пример базовой стратегии кэширования:

self.addEventListener("fetch", event => {
  event.respondWith(
    caches.match(event.request).then(cached => {
      if (cached) return cached;
      return fetch(event.request).then(response => {
        return caches.open("map-cache").then(cache => {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

Поведение при отсутствии сети:

  • отображение кэшированных тайлов
  • заморозка состояния карты
  • отключение интерактивных слоёв

Fallback sprite и glyph ресурсов

Sprite (иконки) и glyphs (шрифты) критичны для корректного отображения карты.

Sprite fallback

map.setStyle({
  version: 8,
  sprite: [
    "https://primary.com/sprite",
    "https://backup.com/sprite"
  ]
});

Реализация на практике часто требует:

  • проверки доступности sprite.json
  • подмены URL через transformRequest
  • локального sprite bundle

Glyph fallback

Шрифты особенно чувствительны к сбоям CDN:

  • отсутствие glyph → пустые подписи
  • частичная загрузка → пропущенные символы

Стратегии:

  • ограничение языков (fallback на латиницу)
  • замена шрифтов на system fonts
  • локальное хранение PBF glyphs

Деградация визуальных слоёв

При ошибках рендеринга WebGL или перегрузке GPU требуется упрощение карты.

Упрощение стиля

  • отключение 3D слоёв
map.setLayoutProperty("buildings-3d", "visibility", "none");
  • замена extrusion на 2D fill
  • снижение opacity и blur эффектов
  • отключение heatmap layers

Progressive enhancement

Иерархия визуализации:

  1. 3D здания + анимации
  2. 2.5D стили (экструзия без теней)
  3. 2D векторная карта
  4. растровая карта
  5. статическое изображение

Fallback при ошибках токена и авторизации

Ошибки авторизации приводят к полной невозможности загрузки тайлов.

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

  • истёкший access token
  • превышение лимитов
  • ограничение домена

Обработка:

map.on("error", (e) => {
  if (e.error && e.error.status === 401) {
    map.setStyle("/static/fallback-style.json");
  }
});

Fallback стиль обычно:

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

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

Mapbox GL JS предоставляет события жизненного цикла:

  • load
  • error
  • idle
  • data
  • styledata

Общая стратегия деградации

map.on("error", () => {
  const canvas = map.getCanvas();
  canvas.style.opacity = 0.5;
});

Расширенная стратегия:

  • повторная инициализация карты
  • смена стиля
  • переключение на raster fallback

Fallback через альтернативные рендереры

При полном отказе WebGL возможны альтернативные движки:

  • Leaflet (raster fallback)
  • OpenLayers (canvas rendering)
  • server-side rendering (static map API)

Логика переключения:

if (!mapboxgl.supported()) {
  initLeafletMap();
}

Кэширование как основной механизм устойчивости

Mapbox GL JS сильно зависит от повторного использования данных.

Типы кэширования:

  • browser cache (HTTP cache headers)
  • in-memory tile cache
  • IndexedDB (через service worker)
  • persistent offline cache

Эффекты:

  • снижение сетевой нагрузки
  • ускорение повторных загрузок
  • возможность offline-режима

Offline режим как крайняя форма fallback

Offline режим требует заранее подготовленных данных:

  • pre-bundled tiles (MBTiles)
  • локальные style.json
  • встроенные sprite/glyph ресурсы

Ограничения:

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

Иерархия fallback-стратегий

Структура устойчивости системы отображения:

  1. Проверка WebGL поддержки
  2. Основной Mapbox GL JS рендеринг
  3. Альтернативный стиль
  4. Замена источников тайлов
  5. Service Worker кэш
  6. Упрощение слоёв
  7. Raster fallback
  8. Статическая карта

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