Офлайн-режим

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

Базовая архитектура офлайн-карты включает три слоя:

  • слой отображения (Leaflet и его L.Map)
  • слой источников данных (tile provider abstraction)
  • слой хранения (Cache API, IndexedDB, файловые хранилища в гибридных приложениях)

Главное отличие от онлайн-режима заключается в том, что источник тайлов становится предсказуемым и локальным, а не внешним HTTP-сервисом.


Модель тайлов и влияние на офлайн-режим

Leaflet использует классическую XYZ-схему тайлов:

/{z}/{x}/{y}.png

где:

  • z — уровень масштаба
  • x, y — координаты тайла

Офлайн-режим фактически требует полного или частичного предвычисления этого пространства.

Ключевые ограничения:

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

При планировании офлайн-карты критическим параметром становится максимальный zoom. Разница между z=12 и z=16 — это не линейный, а квадратичный рост объёма данных.


Кэширование тайлов через Cache API

Современный офлайн-режим чаще всего опирается на Cache API, который используется внутри Service Worker.

Типовая схема работы:

  1. Перехват запроса тайла
  2. Проверка наличия в кэше
  3. Возврат локальной копии при наличии
  4. Запрос к сети при отсутствии (если режим гибридный)
  5. Сохранение результата

Пример логики 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) => {
        if (cached) return cached;

        return fetch(event.request).then((response) => {
          const clone = response.clone();
          caches.open('tiles-cache').then((cache) => {
            cache.put(event.request, clone);
          });
          return response;
        });
      })
    );
  }
});

Такой подход создаёт прозрачную подмену источника данных без изменения кода Leaflet.


Интеграция Service Worker с Leaflet

Service Worker работает как прокси между браузером и сетью, что позволяет полностью изолировать Leaflet от логики офлайн-хранения.

Leaflet продолжает использовать стандартный L.TileLayer, например:

L.tileLayer('/tiles/{z}/{x}/{y}.png', {
  maxZoom: 18,
  minZoom: 10
}).addTo(map);

Разница заключается в том, что запросы к /tiles/... перехватываются Service Worker.

Особенности интеграции:

  • отсутствие необходимости модифицировать Leaflet
  • возможность динамического переключения online/offline
  • единая точка контроля кэширования

Предзагрузка тайлов (offline priming)

Офлайн-режим невозможен без этапа предварительного наполнения кэша.

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

Географический bounding box

Задаётся прямоугольная область:

  • северная граница
  • южная граница
  • западная граница
  • восточная граница

Далее происходит конвертация координат в tile-range.

Итерация по zoom уровням

Каждый zoom увеличивает количество тайлов:

  • z=10 → небольшая область
  • z=14 → детализированный регион
  • z=18 → локальные улицы

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

  • максимум 2–4 zoom-уровня для офлайн-пакета

Batch-загрузка

Загрузка тайлов часто выполняется пакетами:

  • 50–200 запросов одновременно
  • контроль throttling
  • обработка ошибок и повторные попытки

Локальные источники тайлов

Помимо кэширования, офлайн-режим может использовать локальные хранилища.

Статические директории

В приложениях Electron или мобильных оболочках тайлы хранятся как файлы:

/assets/tiles/12/1204/1532.png

Преимущества:

  • мгновенный доступ
  • отсутствие кэш-эвикции

Недостатки:

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

IndexedDB как хранилище тайлов

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

  • ключ: ${z}/${x}/${y}
  • значение: Blob или ArrayBuffer

Схема использования:

  • запись при первом скачивании
  • чтение при офлайн-запросе
  • контроль версии тайлов

Leaflet TileLayer и кастомные провайдеры

В Leaflet основная точка интеграции офлайн-логики — L.TileLayer.

Расширение слоя:

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

    const key = `${coords.z}/${coords.x}/${coords.y}`;

    caches.open('tiles-cache').then((cache) => {
      cache.match(`/tiles/${key}.png`).then((response) => {
        if (response) {
          response.blob().then((blob) => {
            tile.src = URL.createObjectURL(blob);
            done(null, tile);
          });
        }
      });
    });

    return tile;
  }
});

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


Форматы офлайн-карт

Raster tiles (PNG/JPEG)

Самый распространённый вариант:

  • простота генерации
  • совместимость с Leaflet
  • высокий объём данных

Недостаток — отсутствие векторной гибкости.


Vector tiles

Векторные тайлы позволяют:

  • менять стиль без перезагрузки данных
  • уменьшать размер хранилища
  • повышать масштабируемость

Однако офлайн-режим требует наличия локального рендера стилей.


MBTiles и локальные базы карт

Формат MBTiles представляет собой SQLite-базу с тайлами.

Структура:

  • таблица tiles
  • поля: zoom_level, tile_column, tile_row, tile_data

Преимущества:

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

Использование в веб-контексте:

  • через сервер-прокси
  • или через WASM-ридеры

Синхронизация и обновление офлайн-данных

Офлайн-карты не являются статичными в долгосрочной перспективе.

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

Версионирование кэша

  • tiles-cache-v1
  • tiles-cache-v2

При обновлении происходит полная замена набора.

Инкрементальная загрузка

Загружаются только изменённые области:

  • новые дороги
  • изменённые POI
  • обновлённые стили

TTL-подход

Каждый тайл имеет срок жизни:

  • устаревшие удаляются
  • свежие обновляются при появлении сети

Управление памятью браузера

Офлайн-карты могут занимать сотни мегабайт.

Основные механизмы контроля:

  • CacheStorage quota API
  • очистка старых zoom-уровней
  • ограничение географического покрытия
  • сжатие изображений (WebP вместо PNG)

Производительность офлайн-рендеринга

Основные узкие места:

IO операции

Чтение из Cache API или IndexedDB может быть медленнее сети при холодном кэше.

Декодирование изображений

PNG тайлы требуют CPU-декодирования.

DOM-операции Leaflet

Каждый тайл — отдельный DOM элемент.

Оптимизации:

  • уменьшение числа тайлов через lower maxZoom
  • использование canvas renderer
  • предзагрузка соседних тайлов

Гибридный режим (online + offline)

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

  • при наличии сети — загрузка с сервера
  • при отсутствии — fallback в кэш

Приоритет источников:

  1. свежий сетевой тайл
  2. Cache API
  3. локальные файлы
  4. placeholder тайл

Безопасность офлайн-хранения

Офлайн-тайлы могут содержать чувствительные данные:

  • маршруты
  • пользовательские слои
  • геометки

Меры защиты:

  • ограничение доступа Service Worker scope
  • шифрование IndexedDB (в продвинутых системах)
  • очистка при logout
  • изоляция origin

Типовые ошибки реализации офлайн-карт

  • неконтролируемая загрузка всех zoom-уровней
  • отсутствие eviction политики кэша
  • хранение PNG без оптимизации
  • игнорирование лимитов storage quota
  • отсутствие fallback тайлов при повреждении кэша

Практическая модель офлайн-карты на Leaflet

Типовая структура приложения:

/map
  /leaflet
  /service-worker.js
  /tiles-manifest.json
  /cache-manager.js

Компоненты:

  • Leaflet отвечает за рендеринг
  • Service Worker — за перехват запросов
  • manifest — описывает доступные области
  • cache-manager — управляет загрузкой

Поведение карты при полной потере сети

При отсутствии сети:

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

Главное ограничение — отсутствие новых тайлов вне предзагруженной зоны.


Масштабирование офлайн-карт

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

  • разделение по регионам
  • отдельные offline packs
  • загрузка по требованию
  • CDN-предзагрузка при первом запуске

Такая модель позволяет использовать Leaflet даже в условиях полностью автономных систем навигации.