Тайли базової карти Protomaps
Уся базова карта Protomaps, масштаби від 0 до 15, як один архів PMTiles через звичайний HTTP. Вкажіть цю URL-адресу будь-якому клієнту PMTiles, і все працюватиме.
https://tiles.jasontally.com/basemap.pmtiles
- Без ключа APIНе потрібно реєструватися, жодних токенів у вашому коді.
- Без обмеження запитівНемає шейпінгу, квот чи лімітів на сплески трафіку.
- ЩомісяцяПерезбирається з найновішого випуску Protomaps раз на місяць.
Заблоковано тайл OpenStreetMap або помилка 403 від tile.openstreetmap.org? Почніть тут.
Спробувати
Один і той самий архів у трьох стилях. Під час зміни стилю вже завантажені тайли залишаються на місці, тому перемикання відбувається миттєво.
Створіть власний стиль
Змініть колір або вимкніть шар — карта оновлюється одразу. Потім скопіюйте JSON стилю: це звичайний стиль MapLibre, який ви можете розмістити у себе. Він посилається на ці тайли, тому працюватиме будь-де.
Використання
Архів підтримує HTTP-запити діапазонів (range requests), тому його може читати будь-який клієнт PMTiles.
Перше. Додайте карту на сторінку
Скопіюйте цей файл. Це повний приклад: три теги, одна URL-адреса стилю, одна карта. Обидві бібліотеки мають зафіксовані версії та ліцензію BSD-3-Clause, а ті самі файли роздаються і з цього вузла, якщо ви не хочете використовувати CDN.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Basemap</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css">
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pmtiles@3.2.1/dist/pmtiles.js"></script>
<style>html, body, #map { margin: 0; height: 100%; }</style>
</head>
<body>
<div id="map"></div>
<script>
// Teach MapLibre how to read a PMTiles archive.
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);
new maplibregl.Map({
container: "map",
style: "https://tiles.jasontally.com/styles/light.json",
center: [2.35, 48.85], // Paris
zoom: 4
});
</script>
</body>
</html>
Друге. Замініть тайли в наявному стилі
Кожен стиль MapLibre читає тайли з джерела (source). Щоб використовувати ці тайли, змініть лише одне поле url. Більше нічого в стилі змінювати не потрібно.
"sources": {
"protomaps": {
"type": "vector",
- "url": "pmtiles://https://demo-bucket.protomaps.com/v4.pmtiles",
+ "url": "pmtiles://https://tiles.jasontally.com/basemap.pmtiles",
"attribution": "<a href=\"https://www.openstreetmap.org/copyright\">© OpenStreetMap contributors</a>"
}
}
Або зробіть це в коді — зручно, якщо стиль надходить із сервера, який ви не контролюєте:
// Rewrite every PMTiles source in a style to point at these tiles.
const style = await (await fetch("https://your-server/style.json")).json();
for (const source of Object.values(style.sources)) {
if (source.url?.startsWith("pmtiles://")) {
source.url = "pmtiles://https://tiles.jasontally.com/basemap.pmtiles";
}
}
new maplibregl.Map({ container: "map", style, center: [2.35, 48.85], zoom: 4 });
Третє. Де розмістити створений стиль
Кнопка JSON стилю в конструкторі вище відкриває поточний стиль карти з усіма вашими змінами, а у вікні є кнопка копіювання. Отриманий код можна використати двома способами у звичайній вебсторінці.
Збережіть його як файл поруч зі сторінкою та вкажіть його в style — так само, як використовуються три стилі на цьому вузлі. Цей варіант підходить, якщо стиль належить вам і змінюється разом із сайтом:
// Save the copied JSON as my-basemap.json beside this file.
new maplibregl.Map({
container: "map",
style: "my-basemap.json", // relative, same origin
center: [2.35, 48.85],
zoom: 4
});
Або залиште його прямо в HTML — тоді у вас буде лише один файл і нічого не доведеться розміщувати окремо. Стиль той самий, змінюється лише місце зберігання:
// The copied JSON, pasted straight into the page.
const myStyle = {
"version": 8,
"name": "Protomaps Basemap — custom",
"glyphs": "https://tiles.jasontally.com/font/{fontstack}/{range}.pbf",
"sprite": "https://tiles.jasontally.com/sprites/v4/light",
"sources": { /* ... as copied ... */ },
"layers": [ /* ... as copied ... */ ]
};
new maplibregl.Map({ container: "map", style: myStyle, zoom: 4 });
Залиште рядки glyphs і sprite без змін. Вони відповідають за підписи та значки й вказують на цей вузол, тому стиль, вставлений у вашу сторінку, працюватиме з будь-якого сервера. Вкажіть замість цього protomaps.github.io/basemaps-assets, якщо не хочете залежати від цього вузла взагалі.
Що варто перевірити перед заміною. Цей архів використовує схему Protomaps v3. Стиль, написаний для іншої схеми, завантажиться, але нічого не намалює, оскільки назви вихідних шарів не збігаються. Дев'ять назв шарів наведено нижче, як і готовий стиль v3, який можна взяти за основу.
Четверте. Шрифти, спрайти та бібліотеки
Карта складається з тайлів, URL-адреси glyphs для тексту, необов'язкової URL-адреси sprite для значків і бібліотек, які все це малюють. Усе це є тут за стандартними шляхами, тож за бажанням усю карту можна завантажувати з одного домену.
| Що саме | URL тут | Ліцензія |
|---|
Файли MapLibre та PMTiles доступні на jsDelivr і тут. Працює будь-яка з адрес.
Останній рядок на цій сторінці не використовується. Це векторний візуалізатор для Leaflet, опублікований тут, щоб заміна зводилася до одного тегу script. Сам Leaflet залишається на власному CDN.
Два набори шрифтів, Noto Sans Regular і Noto Sans Medium, розбиті на 256 діапазонів по 256 кодових точок, як того вимагає MapLibre. Опубліковано всі 256 діапазонів, а не лише латиницю: Protomaps підписує об'єкти місцевими назвами, тому для Токіо чи Каїра потрібні діапазони в десятках тисяч. Якщо опублікувати лише діапазони для англійської карти, ці підписи зникнуть без жодного повідомлення про помилку.
П'ять таблиць спрайтів, по одній на кожен варіант оформлення вихідного проєкту, у роздільній здатності 1x та 2x. Наведені нижче стилі беруть значки з них. Якщо у власному стилі вам потрібні позначки міст, дорожні щити чи значки об'єктів (POI), вкажіть у sprite потрібний варіант. Назви значків у таблиці перелічені у відповідному файлі light.json.
Що малює карта і звідки береться кожен елемент
Кожен шар стилю бере дані з одного з дев'яти вихідних шарів цього архіву, тому другий архів не потрібен. Варто окремо згадати два з них, бо обидва виглядають так, ніби чогось бракує, хоча це не так:
| Шар | Джерело даних | Є в цій збірці? |
|---|---|---|
| Контури, дороги, будівлі, вода, суша | earth, landcover, landuse, water, roads, buildings | так |
| Назви населених пунктів, доріг і водойм | поле name у тих самих шарах | так |
| Значки об'єктів (POI), позначки міст | pois.kind, places, а також спрайт | так |
| Номери трас на щитах | roads.shield_text | ні. У поточній збірці цього поля немає |
| Номери будинків | buildings.kind=address і buildings.addr_housenumber | ні, з тієї самої причини |
Окремого адресного архіву не існує, і завантажувати окремий шар адрес не потрібно. Основний проєкт Protomaps розміщує адресні точки всередині шару buildings як об'єкти з kind=address і полем addr_housenumber. Це поле з'явилося у новішій збірці Protomaps, ніж та, що роздається зараз, тому шар номерів будинків уже закладений у стиль і ввімкнеться автоматично під час наступного щомісячного оновлення. Те саме стосується тексту на дорожніх щитах: поточна збірка містить лише shield_text_length (кількість символів у номері траси, а не сам номер), тож щити на автомагістралях були б порожніми.
Як написати власний шар і задати йому колір
Шар — це фільтр, масштаб, форма та колір. У фільтрах є дві пастки, на які натрапляють усі, і обидві не видають жодної помилки:
kind— це загальна категорія. Для доріг це одне зі значень:highway,major_road,minor_road,path,rail. Назви з документації вихідного проєкту —motorway,primary,residential— належать полюkind_detail, тому шар, який фільтруєkindза цими значеннями, нічого не намалює і не повідомить про причину.- Шар не малюється нижче свого
minzoom. Архів закінчується на масштабі z15, тому шар із minzoom 16 не буде намальований ніколи. Більшість об'єктів також мають власне полеmin_zoom— масштаб, починаючи з якого їх варто показувати за даними архіву, і готові стилі перевіряють його також.
Отже, дорога, її назва та перемикач для вимкнення лінії виглядають так:
{
"id": "road-major",
"type": "line",
"source": "protomaps",
"source-layer": "roads", // one of the nine
"minzoom": 4, // not drawn below this
"filter": ["==", ["get", "kind"], "major_road"],
"layout": {
"line-cap": "round",
"visibility": "visible" // "none" to hide it
},
"paint": {
"line-color": "#f7c44d", // any CSS colour
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 1.7, 12, 6]
}
}
Назва — це символьний шар (symbol), який читає поле name, з обведенням (halo) або без нього. Якщо гліфа бракує, помилка не виводиться, тому шрифт має бути одним із двох, наявних на цьому вузлі:
{
"id": "road-label-major",
"type": "symbol",
"source": "protomaps",
"source-layer": "roads",
"minzoom": 11,
"layout": {
"text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
"text-font": ["Noto Sans Medium"], // or Noto Sans Regular
"text-size": 10,
"symbol-placement": "line" // follow the road
},
"paint": {
"text-color": "#2b2a26",
"text-halo-color": "#ffffff",
"text-halo-width": 1.1
}
}
Шар, який нічого не малює, не вважається помилкою, і MapLibre не підкаже, яка саме перевірка не пройшла. Якщо шар порожній, перевірте по черзі: назву поля, minzoom шару, назву шрифту, а потім назву значка у файлі light.json таблиці спрайтів.
Якщо ваш стиль завантажує гліфи з demotiles.maplibre.org, достатньо просто замінити ім'я вузла. Шлях навмисно залишено таким самим.
"glyphs": "https://tiles.jasontally.com/font/{fontstack}/{range}.pbf",
"sprite": "https://tiles.jasontally.com/sprites/v4/light"
// before, in a style you already have
- "glyphs": "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf",
+ "glyphs": "https://tiles.jasontally.com/font/{fontstack}/{range}.pbf",
Якщо стиль запитує шрифт, якого немає серед двох зазначених вище, текст не відображатиметься. Власні стилі Protomaps використовують Noto Sans Medium для напівжирного тексту; Noto Sans Bold у цьому наборі немає, тому стиль, який його вимагає, мовчки втрачає всі підписи.
Який вузол швидший і яке рішення було прийнято. Ми виміряли, а не припустили: почергові запити, по одному за раз, через одне з'єднання, перші три вибірки відкинуто, час до першого байта відокремлено від часу передавання. У цьому тесті CDN відповідав на 10–45 мс швидше для кожного з цих файлів, що впевнено перевищує поріг у 15%. Саме тому бібліотеки завантажуються з jsDelivr.
Шрифти та спрайти роздаються звідси, хоча ті самі вимірювання показують, що CDN відповів би приблизно на 10 мс швидше. Це свідоме рішення, а не результат вимірювань. Карта, яка завантажується з одного домену, не ламається через збій стороннього сервісу, а недоступний сервер гліфів означає карту без жодного підпису. Якщо вам важливіші ці 10 мс, вкажіть у glyphs адресу protomaps.github.io/basemaps-assets/fonts — шлях там такий самий. Це показники одного користувача в одному місті, а повторити порівняння можна однією командою.
П'яте. Про Leaflet
L.tileLayer у Leaflet малює готові зображення, а тут — векторні тайли. Проте карта на Leaflet все одно може їх використовувати, зберігаючи ваші маркери, спливаючі вікна та елементи керування. Обидва варіанти заміни описано в розділі Перехід із тайлів OpenStreetMap.
Шосте. Читання архіву бібліотекою PMTiles
Без карти, лише дані. Підходить для скриптів, тестів і всього, де не потрібне зображення.
import { PMTiles, FetchSource } from "pmtiles";
const archive = new PMTiles(new FetchSource("https://tiles.jasontally.com/basemap.pmtiles"));
const header = await archive.getHeader();
console.log(header.minZoom, header.maxZoom); // 0 15
console.log(header.numAddressedTiles); // 1431655765
const metadata = await archive.getMetadata();
console.log(metadata.name); // "Protomaps Basemap"
const tile = await archive.getZxy(3, 4, 2); // z, x, y
console.log(tile.data.byteLength);
Зверніть увагу на два класи. FetchSource читає байти через HTTP, а PMTiles працює поверх нього і розуміє формат архіву. FileSource — це аналог FetchSource для локальних файлів; якщо передати йому рядок з URL-адресою, він не працюватиме.
Архів — це знімок стану, а не потік даних у реальному часі. Розклад оновлень наведено в розділі Наскільки свіжі дані?.
Перехід із тайлів OpenStreetMap
Блокування за політикою використання тайлів, помилка 403 і що з цим робити. Без ключів і квот.
Чому замість карти з'являється заглушка про блокування
App is not following the tile usage policy of OpenStreetMap’s volunteer-run servers
General block: app is not following the tile usage policy of openstreetmap volunteer-run servers
403 Forbidden — tile.openstreetmap.org
Сервери тайлів надіслали це зображення замість карти навмисно, відповідно до політики використання тайлів. Це не збій, і виправлення URL-адреси цього не усуне.
Що зазвичай пробують спершу:
- Заміна піддоменів
a/b/cна основний домен не зніме блокування. Канонічна URL-адреса є обов'язковою, але блокування прив'язується до застосунку або заголовка Referer, а не до імені хоста. - Повторні спроби лише погіршують ситуацію. Надмірна кількість запитів — одна з причин, через які вмикається блокування.
- Жодні хитрощі із заголовками не врятують сторінку, відкриту з диска. Протокол
file://не надсилає заголовокReferer. НадсиланняX-Requested-Withнатомість вимагатиме попереднього запиту CORS (preflight) для кожного тайла, а сервери тайлів відхиляють і його. Firefox та Chromium надсилають різні заголовки, але правило однакове для обох. - Працює на
localhost, але блокується на робочому сайті? Справа в джерелі запиту (origin). Блокується саме заголовок Referer вашого домену, аlocalhostнадсилає інший.
Офіси, навчальні заклади, VPN та мобільні мережі використовують спільні IP-адреси, а блокування застосовуються як до адрес, так і до застосунків. Зі свого боку ви не зможете визначити, яке саме блокування спрацювало.
Тому припиніть звертатися до тих серверів. Є три способи перейти з них.
Залишитися на Leaflet
protomaps-leaflet малює векторні тайли в Leaflet. Плагін сам читає архів і додається як звичайний шар, тому маркери, спливаючі вікна, GeoJSON та елементи керування залишаються на своїх місцях. Копія, перевірена з цим архівом, лежить тут: https://tiles.jasontally.com/vendor/protomaps-leaflet.js.
Було:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
const map = L.map("map").setView([48.85, 2.35], 12);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
</script>
Стало:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
+ <script src="https://tiles.jasontally.com/vendor/protomaps-leaflet.js"></script>
<script>
const map = L.map("map").setView([48.85, 2.35], 12);
- L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { ... }).addTo(map);
+ protomapsL.leafletLayer({
+ url: "https://tiles.jasontally.com/basemap.pmtiles",
+ flavor: "light",
+ lang: "en"
+ }).addTo(map);
</script>
flavorможе мати значенняlight,dark,white,grayscaleабоblack. Це власні теми бібліотеки, а не стилі, наведені вище, тому кольори дещо відрізнятимуться.- Підписи малюються на canvas системним шрифтом. Не потрібна URL-адреса гліфів, і жоден шрифт не поверне 404.
- z15 — найдетальніший тайл. На більших масштабах шар автоматично збільшує тайли z15, а параметр
maxDataZoom: 15уже встановлено за замовчуванням.
Бібліотека protomaps-leaflet перебуває в режимі підтримки: виходять лише виправлення помилок, без нових функцій. Вона добре підходить для наявних застосунків на Leaflet. Нові проєкти варто починати на MapLibre GL.
Залишити Leaflet, малювати через MapLibre
@maplibre/maplibre-gl-leaflet вбудовує карту MapLibre у панель тайлів Leaflet. Використовуйте його, якщо вам потрібен один зі стилів вище або рельєф і відмивка схилів у застосунку, який залишається на Leaflet.
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://tiles.jasontally.com/vendor/maplibre-gl.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://tiles.jasontally.com/vendor/maplibre-gl.js"></script>
<script src="https://tiles.jasontally.com/vendor/pmtiles.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@maplibre/maplibre-gl-leaflet@0.1.4/dist/leaflet-maplibre-gl.js"></script>
<script>
// The style's source is pmtiles://, so MapLibre needs the protocol first.
maplibregl.addProtocol("pmtiles", new pmtiles.Protocol().tile);
const map = L.map("map", { minZoom: 1 }).setView([48.85, 2.35], 12);
const gl = L.maplibreGL({
style: "https://tiles.jasontally.com/styles/light.json"
}).addTo(map);
// gl.getMaplibreMap() is the MapLibre map, for sources and layers.
</script>
- Спершу зареєструйте протокол. Без рядка
addProtocolу консолі з'явиться помилкаURL scheme "pmtiles" is not supported, а карта залишиться порожньою. - MapLibre малює на один рівень масштабу нижче за Leaflet. Розмір його тайлів становить 512 пікселів проти 256 у Leaflet, тому шар із
minzoom12 з'являється, коли Leaflet досягає z13. - Leaflet, як і раніше, малює поверх карти. Полотно (canvas) розміщується в панелі тайлів, тому маркери та накладення працюють так само, як і поверх растрового шару.
Перейти на MapLibre GL
Одна бібліотека, жодних адаптерів, а наведені вище стилі працюють як є. Повний код сторінки наведено в розділі Перше. Додайте карту на сторінку. Перехід зводиться здебільшого до зміни порядку координат:
| Leaflet | MapLibre GL |
|---|---|
L.map("map").setView([lat, lng], z) | new maplibregl.Map({ container: "map", center: [lng, lat], zoom: z }) |
L.tileLayer(url).addTo(map) | стиль із векторним джерелом; див. вище |
L.marker([lat, lng]).bindPopup(html) | new maplibregl.Marker().setLngLat([lng, lat]).setPopup(new maplibregl.Popup().setHTML(html)) |
L.geoJSON(data) | addSource(id, { type: "geojson", data }), потім addLayer |
L.control.scale() | new maplibregl.ScaleControl() |
map.invalidateSize() | map.resize() |
У setView([lat, lng]) першою йде широта, а в усіх викликах MapLibre — [lng, lat] (довгота, широта). Якщо карта опинилася не в тій півкулі, причина в цьому, а не в тайлах. Джерела та шари, додані до завантаження стилю, відкидаються без помилки, тому додавайте їх усередині map.on("load", …).
“archive contains MVT vector tiles, but leafletRasterLayer is for displaying raster tiles”
Це повідомлення від pmtiles.leafletRasterLayer, який призначений для малювання растрових PMTiles як зображень. Тут векторні тайли, тому він їх не приймає. Жодне налаштування цього не змінить, а растрової копії тут немає. Скористайтеся одним із наведених вище варіантів.
“download.openstreetmap.fr returned 403”
Це дзеркало вивантажень і реплікації даних, а не сервер тайлів. У жовтні 2026 року воно заблокувало низку адрес через надмірні завантаження, а оператори повідомили, що відновлюватимуть доступ в індивідуальному порядку. Тут воно не потрібне: цей архів формується з готових випусків Protomaps, тож вам не треба збирати його з .osm.pbf самостійно. Якщо ж ви завантажуєте дані OSM напряму, беріть періодичні зрізи замість щохвилинного потоку.
Стилі
Ті самі тайли, різні кольори. Оберіть один, скопіюйте, змініть — він ваш.
| Стиль | Вигляд | Файл |
|---|
Що всередині тайлів
Зчитується з метаданих самого архіву під час завантаження сторінки. Саме ці назви шарів має використовувати стиль.
| Вихідний шар | Масштаб | Поля |
|---|---|---|
| завантаження… | ||
Чим цей сервіс не є
Прямі відповіді, щоб нічого не стало для вас несподіванкою пізніше.
- Немає гарантованого рівня обслуговування (SLA). Це безкоштовний сервіс без жодних договірних зобов'язань. Він може працювати повільно, може бути недоступним, а URL-адреса та дані можуть змінитися або зникнути без попередження. Якщо ваш продукт залежить від карти, розмістіть власну копію тайлів або придбайте тарифний план із підтримкою в іншого постачальника.
- Дані не оновлюються щогодини. Ця копія є знімком найновішого випуску Protomaps, а не потоком у реальному часі. Змін, внесених сьогодні в OpenStreetMap, тут немає. Розклад оновлень дивіться в розділі Наскільки свіжі дані?.
- Завантаження всього файлу одним запитом не підтримується. Сервер відповідає діапазонами розміром не більше 512 КіБ — це максимум, який будь-коли запитує клієнт PMTiles. Спроба завантажити весь архів одним запитом поверне код
416, і це очікувана поведінка. - Зазначення авторства обов'язкове. Тайли створені на основі даних OpenStreetMap. Зберігайте рядок авторства (attribution) у своєму стилі або на карті.
Наскільки свіжі дані?
Ці тайли є копією базової карти Protomaps, яка перезбирається раз на місяць.
- Оновлення раз на місяць. Збирання запускається в першу неділю місяця між 00:30 та 07:00 UTC — це єдине вікно, у яке основний проєкт не публікував випусків за три роки історії релізів. Оновлення відбувається щомісяця, а не щотижня, оскільки новий архів робить недійсними кешовані фрагменти на CDN та в браузерах користувачів, а кожне перезбирання витрачає ресурси.
- Більшість місяців не вимагають жодних дій. Перезбирання відбувається лише тоді, коли основний проєкт публікує новий випуск.
- Яка саме збірка використовується просто зараз? Натисніть кнопку Метадані архіву у верхній частині сторінки. Вона читає заголовок безпосередньо з архіву, тому інформація завжди актуальна, а не зашита в текст сторінки.
- Перезбирання відбувається не миттєво. Воно складається з тринадцяти окремих етапів, розтягнутих приблизно на шість із половиною годин, один за одним. Протягом більшої частини цього часу копія є сумішшю двох випусків. Тайл, запитаний у цей період, може надійти вже з новішої збірки, ніж зазначено в заголовку, і виглядатиме як тайл з іншого місця, але сама карта не зламається.
- Оновлення може бути пропущене. Процес працює без гарантій. Якщо один з етапів завершиться помилкою, решта копії залишиться на попередній збірці. Карта при цьому залишається повністю робочою.