Обработка offline состояния

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

Ключевая проблема заключается в том, что рендеринг карты зависит от внешних ресурсов:

  • style JSON (описание слоёв и источников)
  • vector tiles (MVT)
  • raster tiles (PNG/JPEG)
  • glyphs (шрифты)
  • sprites (иконки и атласы)

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


Контроль сетевых запросов через transformRequest

Основной механизм управления загрузками в Mapbox GL JS — функция transformRequest, передаваемая в конфигурации карты.

Она позволяет перехватывать каждый HTTP-запрос:

  • тайлы
  • стиль
  • шрифты
  • спрайты

Типовая логика офлайн-режима строится вокруг подмены URL:

const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/example/style.json',
  transformRequest: (url, resourceType) => {
    if (!navigator.onLine) {
      return {
        url: `/offline-cache${new URL(url).pathname}`
      };
    }
    return { url };
  }
});

Ключевые сценарии использования transformRequest:

  • перенаправление на локальный сервер
  • доступ к IndexedDB-кэшу через промежуточный API
  • подмена Mapbox-URL на self-hosted tiles
  • логирование и контроль сетевых ошибок

Обнаружение offline-состояния

Браузер предоставляет базовые механизмы определения состояния сети:

window.addEventListener('online', () => {
  // восстановление синхронизации
});

window.addEventListener('offline', () => {
  // переключение в режим локального кэша
});

Однако этого недостаточно, так как:

  • navigator.onLine не гарантирует доступность CDN
  • возможны частичные сетевые сбои
  • DNS может быть доступен, но тайлы — нет

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

  • проверка heartbeat-запросов к тайл-серверу
  • отслеживание ошибок загрузки источников
  • таймауты fetch-запросов
  • fallback на локальные источники

Кэширование тайлов через Service Worker

Наиболее устойчивый способ офлайн-поддержки — Service Worker, который перехватывает сетевые запросы на уровне браузера.

Базовая стратегия:

self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);

  if (url.pathname.includes('/tiles/')) {
    event.respondWith(
      caches.match(event.request).then((cached) => {
        return cached || fetch(event.request).then((response) => {
          const copy = response.clone();
          caches.open('tiles-cache').then((cache) => {
            cache.put(event.request, copy);
          });
          return response;
        });
      })
    );
  }
});

Особенности работы с картографическими тайлами:

  • большие объёмы данных (сотни мегабайт и более)
  • необходимость LRU-очистки кэша
  • разделение по zoom/xyz координатам
  • отдельные политики для raster и vector tiles

Структура offline-хранилища

Для стабильного офлайн-режима используется многоуровневая система хранения:

1. IndexedDB

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

  • метаданных тайлов
  • style.json
  • sprite-атласов
  • glyph-кэша

2. Cache Storage

Используется Service Worker’ом:

  • бинарные тайлы
  • HTTP-ответы
  • шрифты

3. LocalStorage (ограниченно)

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

  • флагов состояния
  • конфигурации offline-режима

Работа со стилями в offline-режиме

Style JSON — центральный элемент конфигурации карты. В офлайн-режиме он должен быть полностью локализован.

Типичная проблема — абсолютные URL:

"glyphs": "https://api.mapbox.com/fonts/..."
"sprites": "https://api.mapbox.com/sprites/..."

Решение заключается в подмене на локальные ресурсы:

"glyphs": "/offline/glyphs/{fontstack}/{range}.pbf",
"sprites": "/offline/sprites/sprite"

При этом важно соблюдать:

  • идентичность структуры слоёв
  • совместимость версий стилей
  • локальную генерацию sprite atlas

Тайлы: vector и raster стратегии

Vector tiles

Vector tiles (MVT) обеспечивают гибкость рендеринга, но требуют сложной офлайн-подготовки:

  • генерация через tippecanoe
  • хранение по схеме z/x/y
  • сжатие gzip или pbf

Raster tiles

Raster tiles проще в офлайн-режиме:

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

Выбор зависит от сценария:

  • навигация и интерактивность → vector
  • статические карты → raster

Проблема токенов и API-доступа

Mapbox GL JS в онлайн-режиме использует access token для доступа к API. В офлайн-среде возникают ограничения:

  • невозможность верификации токена без сети
  • блокировка CDN-запросов
  • ограничение лицензии на офлайн-тайлы

Типовая архитектура решения:

  • предварительная загрузка тайлов при наличии сети
  • локальный сервер тайлов
  • отказ от Mapbox API в runtime
  • использование self-hosted стека

Перехват загрузки ресурсов через load event pipeline

Mapbox GL JS использует внутренний pipeline загрузки ресурсов. Ошибки офлайн-режима проявляются в событиях:

  • error
  • data
  • styledata
  • sourcedata

Обработка ошибок позволяет переключать режимы:

map.on('error', (e) => {
  if (!navigator.onLine) {
    // переключение на offline fallback
  }
});

Важно учитывать, что ошибки могут возникать не только из-за отсутствия сети, но и из-за:

  • битых тайлов
  • несовместимых версий style
  • отсутствующих glyph ranges

Локальные tile server архитектуры

Наиболее стабильный подход — развёртывание локального сервера:

  • Node.js + express
  • Nginx static tile hosting
  • mbtiles backend (SQLite)

Пример структуры:

/tiles/{z}/{x}/{y}.pbf
/styles/basic/style.json
/sprites/sprite.png
/fonts/{fontstack}/{range}.pbf

В этом случае Mapbox GL JS работает без сетевых зависимостей, а transformRequest выполняет только маршрутизацию на локальный origin.


Preloading стратегии

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

Используются подходы:

  • загрузка bounding box (bbox)
  • фиксированные zoom levels
  • ограничение географической области
  • progressive tile caching

Пример логики:

  • zoom 0–10: базовые слои мира
  • zoom 11–14: город
  • zoom 15+: критические зоны

Обработка деградации функциональности

В офлайн-режиме часть функций становится недоступной:

  • геокодинг
  • routing API
  • traffic data
  • real-time updates

Архитектура должна учитывать деградацию:

  • отключение интерактивных источников
  • переход на статические слои
  • замена динамических данных локальными снапшотами

Безопасность и контроль целостности кэша

При офлайн-хранении критично проверять:

  • контрольные суммы тайлов
  • версионность style.json
  • несовместимость старых sprite atlas
  • повреждение IndexedDB

Типовой подход:

  • hash-based validation
  • versioned cache buckets
  • atomic update strategy (новый кэш → переключение)

Итоговая модель офлайн-архитектуры

Офлайн-режим в Mapbox GL JS строится как совокупность:

  • перехвата сетевых запросов (transformRequest)
  • локального HTTP-кэша (Service Worker)
  • структурированного хранилища (IndexedDB + Cache Storage)
  • предварительно загруженных тайлов и стилей
  • fallback-логики при ошибках загрузки

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