Пользовательские источники тайлов

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

В основе большинства слоёв тайлов используется класс L.TileLayer, принимающий строку-шаблон:

L.tileLayer('https://{s}.tile.example.com/{z}/{x}/{y}.png', {
  maxZoom: 19,
  attribution: '© Example Maps'
}).addTo(map);

Ключевые плейсхолдеры:

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

Модель XYZ предполагает, что координата Y растёт вниз от северного полюса. Это стандарт де-факто для большинства веб-картографических сервисов.

Подключение собственных тайловых серверов

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

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

Пример подключения:

const customTiles = L.tileLayer('https://tiles.myserver.com/tiles/{z}/{x}/{y}.png', {
  maxZoom: 18,
  minZoom: 2,
  tileSize: 256,
  keepBuffer: 4,
  updateWhenIdle: false
});

Параметры minZoom и maxZoom ограничивают диапазон доступных масштабов, предотвращая лишние запросы за пределами покрытия источника данных.

Настройка tileSize и нестандартные тайлы

Некоторые серверы используют тайлы размером 512×512 пикселей. В этом случае необходимо явно указать размер и, при необходимости, корректировать масштабирование:

L.tileLayer('https://server.com/tiles/{z}/{x}/{y}.png', {
  tileSize: 512,
  zoomOffset: -1
}).addTo(map);

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

Поддержка retina и высоких DPI

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

L.tileLayer('https://server.com/tiles/{z}/{x}/{y}.png', {
  detectRetina: true
});

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

TMS и инвертированная ось Y

Некоторые серверы используют систему TMS (Tile Map Service), где ось Y инвертирована относительно XYZ. Для корректной работы применяется опция:

L.tileLayer('https://server.com/tms/{z}/{x}/{y}.png', {
  tms: true
});

В этом режиме Leaflet автоматически преобразует координату Y.

Динамические источники тайлов

URL может формироваться функцией через L.TileLayer с переопределением метода getTileUrl:

const DynamicLayer = L.TileLayer.extend({
  getTileUrl: function(coords) {
    const { z, x, y } = coords;
    const server = (x + y) % 2 === 0 ? 'a' : 'b';
    return `https://${server}.tiles.com/${z}/${x}/${y}.png`;
  }
});

const layer = new DynamicLayer();
layer.addTo(map);

Такая схема используется для:

  • балансировки нагрузки
  • A/B маршрутизации
  • выбора разных источников данных по координате

Авторизация и защищённые тайлы

Многие API требуют токены доступа. Они передаются через query string:

L.tileLayer('https://api.tiles.com/{z}/{x}/{y}.png?key=YOUR_TOKEN', {
  maxZoom: 18
}).addTo(map);

Для более сложной авторизации можно использовать динамическую генерацию URL:

getTileUrl: function(coords) {
  return `https://api.tiles.com/${coords.z}/${coords.x}/${coords.y}.png?token=${this.options.token}`;
}

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

Каждый тайл генерирует события загрузки:

  • loading
  • load
  • tileloadstart
  • tileload
  • tileerror

Пример обработки ошибок:

const layer = L.tileLayer('https://server.com/{z}/{x}/{y}.png');

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

Можно задать запасной URL:

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

    img.oner ror = function() {
      img.src = '/fallback.png';
    };

    img.src = this.getTileUrl(coords);
    return img;
  }
});

Ограничение области отображения

Для оптимизации запросов применяется ограничение по географическим границам:

L.tileLayer('https://server.com/{z}/{x}/{y}.png', {
  bounds: [[40, -10], [60, 30]]
});

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

Кэширование и производительность

Браузерное кэширование играет ключевую роль в производительности тайловых слоёв. Дополнительно используются:

  • keepBuffer — количество тайлов за пределами экрана
  • updateWhenIdle — обновление только после остановки перемещения
  • updateInterval — минимальный интервал обновления
L.tileLayer('https://server.com/{z}/{x}/{y}.png', {
  keepBuffer: 8,
  updateWhenIdle: true,
  updateInterval: 200
});

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

Кастомные заголовки и Fetch-based тайлы

Стандартный TileLayer ограничен загрузкой через <img>. Для передачи заголовков используется переопределение с fetch:

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

    fetch(this.getTileUrl(coords), {
      headers: {
        'Authorization': 'Bearer TOKEN'
      }
    })
      .then(res => res.blob())
      .then(blob => {
        tile.src = URL.createObjectURL(blob);
        done(null, tile);
      })
      .catch(err => done(err, tile));

    return tile;
  }
});

Такой подход применяется при работе с закрытыми API или корпоративными картографическими системами.

Несколько источников тайлов и переключение слоёв

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

const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
const satellite = L.tileLayer('https://sat.example.com/{z}/{x}/{y}.jpg');

const baseMaps = {
  "OSM": osm,
  "Satellite": satellite
};

L.control.layers(baseMaps).addTo(map);

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

Кастомные GridLayer для полного контроля

Для нестандартных сценариев используется L.GridLayer, предоставляющий полный контроль над созданием тайлов:

const MyGrid = L.GridLayer.extend({
  createTile: function(coords) {
    const tile = document.createElement('div');
    tile.innerHTML = `${coords.z}/${coords.x}/${coords.y}`;
    tile.style.border = '1px solid #ccc';
    return tile;
  }
});

map.addLayer(new MyGrid());

Этот уровень абстракции используется для:

  • векторных рендеров
  • тепловых карт
  • процедурной генерации тайлов
  • отладки сетки тайлов

Управление прозрачностью и порядком слоёв

При наложении нескольких источников важны параметры:

L.tileLayer('https://server.com/{z}/{x}/{y}.png', {
  opacity: 0.7,
  zIndex: 5
}).addTo(map);
  • opacity регулирует смешивание слоёв
  • zIndex определяет порядок наложения

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