Загрузка и кэширование тайлов

Система отображения тайлов в Leaflet построена вокруг асинхронной подгрузки растровых изображений по координатной сетке. Карта разбивается на квадратные плитки фиксированного размера (обычно 256×256 пикселей), каждая из которых запрашивается по отдельному URL в зависимости от координат x, y и уровня масштабирования z.

Базовый механизм реализуется через класс L.TileLayer, который управляет формированием URL, жизненным циклом тайлов и их хранением в DOM.

Основная структура URL-шаблона:

L.tileLayer('https://{s}.tile.server.com/{z}/{x}/{y}.png')

Параметры:

  • {z} — уровень масштаба
  • {x}, {y} — координаты тайла в глобальной сетке
  • {s} — поддомен для распределения нагрузки

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


Формирование запросов и разбиение карты на тайлы

При изменении viewport Leaflet пересчитывает набор видимых тайлов:

  1. Определяется bounding box текущего окна карты.
  2. Преобразуются географические координаты в пиксельные координаты уровня зума.
  3. Пиксельные координаты нормализуются к сетке тайлов.
  4. Формируется список необходимых тайлов.

Каждый тайл представляется DOM-элементом <img> с привязанным URL. Leaflet не хранит изображение как единый слой — вместо этого используется набор независимых элементов.


Жизненный цикл тайла

Загрузка тайла проходит несколько стадий:

  • создание DOM-элемента
  • установка src
  • начало сетевого запроса
  • событие tileloadstart
  • успешная загрузка или ошибка
  • события tileload или tileerror
  • добавление в контейнер слоя

Событийная модель позволяет синхронизировать внешний код с состоянием рендеринга:

  • loading — начальная фаза загрузки слоя
  • load — все тайлы загружены
  • tileload — отдельный тайл загружен
  • tileerror — ошибка загрузки ресурса

Кэширование на уровне браузера

Основной механизм кэширования основан на стандартном HTTP-кэше. Leaflet не реализует собственный диск-кэш для тайлов, а полагается на заголовки ответа сервера:

  • Cache-Control
  • Expires
  • ETag
  • Last-Modified

Если сервер корректно настроен, повторные запросы к одинаковым тайлам не приводят к сетевым обращениям.

Особенно важно поведение при навигации по зумам: при возврате на ранее посещённый уровень масштабирования браузер может полностью восстановить изображения из локального кэша без повторной загрузки.


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

При изменении масштаба возникает проблема «разрушения» предыдущего набора тайлов. Leaflet использует стратегию частичного сохранения DOM-элементов.

Параметр:

keepBuffer: 2

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

Дополнительные механизмы:

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

Внутри L.TileLayer используется внутренний пул _tiles и _unusedTiles, что позволяет переиспользовать элементы <img> без их пересоздания.


Управление моментом загрузки

Опции, влияющие на стратегию загрузки:

  • updateWhenIdle: true — загрузка тайлов происходит после завершения движения карты
  • updateWhenZooming: false — предотвращает постоянную перезагрузку при анимации zoom
  • updateInterval — интервал обновления при интерактивном движении

Эти параметры напрямую влияют на количество сетевых запросов при интерактивной работе с картой.

При включённом updateWhenIdle Leaflet откладывает вычисление нового набора тайлов до момента, когда пользователь прекращает взаимодействие с картой.


Проблема префетчинга и заполнения буфера

Leaflet применяет предзагрузку тайлов за пределами текущего viewport. Это уменьшает визуальные разрывы при резком перемещении карты.

Буфер формируется следующим образом:

  • вычисляется расширенный bounding box
  • добавляются тайлы соседних областей
  • формируется приоритет загрузки от центра к краям

Центральные тайлы получают более высокий приоритет, чем периферийные.


Управление ошибками загрузки

При невозможности загрузки тайла (404, сетевой сбой, CORS) используется механизм замещения:

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

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

L.tileLayer(url, {
  errorTileUrl: '/img/tile-error.png'
})

Роль поддоменов в распределении нагрузки

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

subdomains: 'abc'

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

Пример генерации:

  • a.tile.server.com
  • b.tile.server.com
  • c.tile.server.com

Тайлы высокого разрешения и Retina-дисплеи

Для экранов с высоким DPI используется параметр:

detectRetina: true

При его включении Leaflet:

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

Это приводит к увеличению объёма трафика, но улучшает резкость изображения.


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

Дополнительный уровень оптимизации реализуется через Service Worker. Он позволяет перехватывать запросы тайлов и сохранять их в IndexedDB или Cache Storage.

Типичная стратегия:

  • intercept fetch запросов к tile endpoint
  • проверка наличия в кеше
  • возврат локальной копии при совпадении
  • асинхронное обновление при необходимости

Это превращает карту в частично офлайн-доступный ресурс.


Управление памятью и удаление тайлов

При выходе тайлов за пределы буфера Leaflet:

  • удаляет DOM-элемент <img>
  • очищает ссылки на события
  • освобождает память JavaScript-объектов
  • сохраняет возможность повторного создания при возвращении в область

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


Приоритет загрузки и порядок отображения

Leaflet использует стратегию progressive rendering:

  1. сначала загружаются центральные тайлы
  2. затем ближайшие соседние
  3. затем периферийные области

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


Влияние сетевых условий на стратегию загрузки

При высокой задержке сети проявляются следующие особенности:

  • рост числа одновременно висящих запросов <img>
  • задержка полной отрисовки слоя
  • частичное отображение карты с «дырками» тайлов

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


Синхронизация тайлов между уровнями зума

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

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