Подложка карты: тайлы, поставщик и атрибуция
Оглавление серии «Карты для веба» · Урок 2 из 12.
Поверхность карты из предыдущего урока уже знает центр и масштаб, но не показывает улицы. Теперь добавим подложку. При этом не будем считать URL с картинками частью самой библиотеки: Leaflet отображает тайлы, а отдельный поставщик создаёт, хранит и обслуживает их. Такое разделение определяет и устройство кода, и условия будущего выпуска.
В «Рядом» используем стандартную подложку OpenStreetMap как учебный вариант для обычного ручного просмотра. Она подключится после нажатия кнопки. Пользователь заранее прочитает, что поставщик получает запросы по видимой области. Для приложения с заметной аудиторией источник нужно выбрать заново с учётом его политики, квот, стоимости и договора обслуживания.
Почему карта загружается квадратами
Подложка разбита на небольшие изображения — тайлы. Масштаб обозначается уровнем z, а положение изображения на этом уровне — индексами x и y. Когда пользователь перемещает камеру, библиотека вычисляет нужные индексы и запрашивает соответствующие файлы. Мы не получаем одну гигантскую картинку всей Земли и не скачиваем заранее каждый возможный участок.
Число доступных квадратов растёт с уровнем масштаба, но браузеру нужна только небольшая часть вокруг экрана. Поэтому нагрузка зависит не только от числа пользователей, но и от размеров карты, перемещений, частоты посещений и поведения кэша. Несколько записей в нашем каталоге не гарантируют малое число тайловых запросов: человек может долго перемещать подложку, не выбирая ни одного места.
Шаблон адреса сообщает Leaflet, как подставлять эти индексы. Это технический договор конкретного поставщика, а не универсальный стандарт всех картографических сервисов. Другой сервис может требовать токен, иначе задавать размер изображения или использовать дополнительные параметры. При смене источника сверяйте также максимальный масштаб, атрибуцию и право показывать полученные изображения на вашем домене.
Вынесем поставщика в отдельный файл
Создайте src/provider.js. Это полный файл конфигурации учебной подложки:
export default {
url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
maxZoom: 19,
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
};
Адрес использует HTTPS и прямой домен стандартного сервиса. Не добавляем случайное число или время к каждому URL: одинаковый тайл должен оставаться одинаковым ресурсом, чтобы штатный браузерный кэш работал. В атрибуции сохраняем ссылку на авторов данных OpenStreetMap. Она появится на карте через слой и останется видимой, когда слой подключён.
Конфигурация содержит HTML в поле атрибуции, но этот текст написан разработчиком и входит в исходники. Это отличается от будущего описания места, которое может прийти из импортированного файла. Для описаний мы будем создавать DOM и назначать textContent. Не превращайте настройки поставщика в произвольную пользовательскую строку: адрес и атрибуция должны проходить отдельное согласование на стороне владельца сайта.
Кнопка #tiles добавлена в index.html второго шага. Ниже полный контроллер этого небольшого состояния, включая создание слоя и обработку отказа:
import { map } from './map.js';
const status = document.querySelector('#status');
import provider from './provider.js';
let tiles = null;
document.querySelector('#tiles').addEventListener('click', () => {
if (!map) { status.textContent = "Карта недоступна."; return; }
if (tiles) return;
tiles = L.tileLayer(provider.url, {
maxZoom: provider.maxZoom, attribution: provider.attribution
});
tiles.on('tileerror', () => {
status.textContent = 'Часть подложки недоступна. Каталог можно читать без неё.';
});
tiles.addTo(map);
document.querySelector('#tiles').disabled = true;
});
Переменная tiles начинается с null. При первом нажатии создаём слой, добавляем его к карте и запрещаем повторное нажатие. Если всегда создавать новый слой, несколько одинаковых наборов изображений смогут занять один экран, а отладка количества запросов станет сложнее. Один экземпляр достаточен, пока пользователь не переключает источник; переключение потребует удаления прежнего слоя и отдельного управления состоянием.
Событие tileerror сообщает о проблеме загрузки изображения. Оно не доказывает, что весь сервис полностью недоступен: отказ мог затронуть один квадрат, сеть устройства или отдельный ответ. Поэтому сообщение говорит о части подложки. Список мест позднее продолжит работать независимо. Здесь нет автоматического цикла бесконечных повторов и переключения на случайный чужой сервер.
Атрибуция — часть интерфейса
Частая ошибка оформления — спрятать нижний угол карты ради чистой картинки. Вместе с углом исчезает информация о происхождении данных. В нашем CSS атрибуция сохраняется, а её размер остаётся читаемым. Не перекрывайте её кнопками приложения, не удаляйте через attributionControl: false и не переносите всю ответственность на общий футер, если условия источника требуют подпись непосредственно на карте.
Размер контейнера тоже влияет на читаемость. На узком экране длинная подпись может занимать несколько строк. Это повод проверить размещение собственных элементов, а не уменьшить текст до неразличимого. Если поставщик требует несколько подписей, их нужно учесть при проектировании карты вместе с элементами управления и карточками.
Данные OpenStreetMap, стандартный тайловый сервер и библиотека Leaflet имеют разные роли. Свободная лицензия набора данных не означает право использовать общий сервер без ограничений. Можно построить собственную инфраструктуру или заключить договор с другим поставщиком, но это самостоятельная задача. Наш файл позволяет заменить источник явно, не менять каждую функцию приложения.
Какие действия не добавляем в пример
Политика стандартных тайлов OSM регулирует работу общего сервиса. В нашем коде нет массовой предварительной загрузки, обхода областей невидимым браузером, сохранения карты для офлайн-пакета и принудительного обхода кэша. Мы оставляем обычное поведение браузера и загружаем подложку для интерактивного просмотра. Такая дисциплина относится к сетевому сервису; она не запрещает изучать алгоритм отображения на собственных тестовых изображениях.
Для публичного сайта заранее оцените реальную аудиторию и условия выбранного сервиса. Стандартный сервер не даёт договорной гарантии непрерывной работы. Учебная кнопка также не превращает поток большого сайта в допустимый автоматически: решение о выпуске зависит от действующей политики. Зафиксируйте поставщика, контакт для проблем, порядок смены URL и поведение приложения при отказе.
Есть и вопрос приватности. Тайловый сервер видит IP и запрашиваемые индексы; по ним можно определить область просмотра. Даже если собственный JavaScript нигде не отправляет точную геопозицию, приближение карты к ней создаёт запросы соответствующих тайлов. В уроке о геолокации вернёмся к этому различию. Пока начальная область задана разработчиком и не связана с положением посетителя.
Ожидаемый результат и следующий слой
После ручного выполнения второй папки до нажатия кнопки должна оставаться нейтральная поверхность. После нажатия ожидается подложка с подписью OSM и знакомыми элементами управления. Если CDN не загрузил Leaflet, этот шаг ещё не содержит полноценного резервного списка; его подготовим по мере появления данных и завершим в последнем уроке.
Попробуйте мысленно заменить provider.url, сохранив всё остальное. Из этого не следует, что новая конфигурация уже пригодна: потребуется проверить поддержку схемы z/x/y, лицензирование, границы масштаба и условия подключения. Зато вы видите место, где такой выбор должен быть оформлен. Следующий слой будет принадлежать нашему проекту: опишем вымышленные места в GeoJSON и проверим их до отображения.
Полный снимок этого шага находится в папке lesson-02 архива серии. При подготовке материала пример не запускался; описанные эффекты ожидаются по разобранному коду и требуют самостоятельной проверки в браузере.