Kafelki Protomaps Basemap

Cała mapa podkładowa Protomaps Basemap, poziomy przybliżenia od 0 do 15, udostępniana jako jedno archiwum PMTiles przez zwykły protokół HTTP. Wskaż ten adres URL dowolnemu klientowi PMTiles i gotowe.

https://tiles.jasontally.com/basemap.pmtiles

Zablokowany kafelek OpenStreetMap albo błąd 403 z tile.openstreetmap.org? Zacznij tutaj.

Wypróbuj

To samo archiwum w trzech stylach. Przy zmianie stylu załadowane już kafelki zostają na miejscu, więc przełączenie jest natychmiastowe.

z0

Stwórz własny styl

Zmień kolor lub wyłącz warstwę, a mapa zaktualizuje się na bieżąco. Następnie skopiuj plik JSON stylu — to zwykły styl MapLibre, który możesz hostować u siebie. Wskazuje na te kafelki, więc działa niezależnie od tego, gdzie go umieścisz.

Jak użyć

Archiwum obsługuje żądania zakresowe HTTP (Range requests), więc odczyta je każdy klient PMTiles.

Jeden. Osadź mapę na stronie

Skopiuj ten plik. To cały przykład: trzy znaczniki, jeden adres URL stylu, jedna mapa. Obie biblioteki mają przypięte wersje i licencję BSD-3-Clause, a te same pliki są też serwowane z tego hosta, jeśli wolisz nie korzystać z sieci 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>

Dwa. Podmień kafelki w posiadanym stylu

Każdy styl MapLibre pobiera kafelki ze źródła danych. Aby użyć tych kafelków, zmień ten jeden parametr url. Nic więcej w stylu się nie zmienia.

  "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\">&copy; OpenStreetMap contributors</a>"
    }
  }

Albo zrób to w kodzie, co przydaje się, gdy styl pochodzi z serwera, nad którym nie masz kontroli:

// 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 });

Trzy. Gdzie umieścić utworzony styl

Przycisk JSON stylu w edytorze powyżej otwiera styl, którego mapa używa w tej chwili, razem ze wszystkimi zmianami, a okno dialogowe ma przycisk kopiowania. Wynik można wykorzystać na dwa sposoby — oba to zwykłe strony internetowe.

Zapisz go jako plik obok swojej strony i wskaż go w parametrze style, tak jak używane są trzy style na tym hoście. Wybierz ten sposób, jeśli styl jest twój i zmienia się wraz z witryną:

// 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
});

Albo zostaw go bezpośrednio w kodzie HTML — wtedy masz tylko jeden plik do przenoszenia i nic dodatkowego do hostowania. Ten sam styl, tylko w innym miejscu:

// 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 });

Wiersze glyphs oraz sprite zostaw bez zmian. Odpowiadają za rysowanie etykiet i ikon oraz wskazują na ten host, dzięki czemu styl wklejony na własną stronę działa niezależnie od miejsca, z którego ją serwujesz. Jeśli wolisz w ogóle nie zależeć od tego hosta, skieruj je na protomaps.github.io/basemaps-assets.

Jedna rzecz do sprawdzenia przed podmianą. To archiwum używa schematu Protomaps v3. Styl napisany dla innego schematu załaduje się, ale nic nie narysuje, ponieważ nazwy warstw źródłowych nie będą się zgadzać. Poniżej znajdziesz listę dziewięciu nazw warstw oraz gotowy styl v3 do skopiowania.

Cztery. Czcionki, sprite'y i biblioteki

Mapa składa się z kafelków, adresu URL glyphs dla tekstu, opcjonalnie adresu URL sprite dla ikon oraz bibliotek, które to wszystko rysują. Wszystkie te elementy są dostępne tutaj pod oczekiwanymi ścieżkami, więc cała mapa może być ładowana z jednej nazwy hosta.

CoURL tutajLicencja

Pliki MapLibre i PMTiles są dostępne na jsDelivr oraz tutaj. Oba adresy URL działają.

Ostatni wiersz nie jest używany przez tę stronę. To wektorowy renderer dla biblioteki Leaflet, opublikowany tutaj, aby podmiana wymagała tylko jednego znacznika skryptu. Sam Leaflet pozostaje na własnym CDN.

Dwa stosy czcionek, Noto Sans Regular oraz Noto Sans Medium, podzielone na 256 zakresów po 256 punktów kodowych, o które odpytuje MapLibre. Opublikowano wszystkie 256 zakresów, nie tylko łacińskie: Protomaps opisuje obiekty ich lokalnymi nazwami, więc Tokio czy Kair wymagają zakresów rzędu dziesiątek tysięcy. Gdy opublikuje się tylko to, czego używa mapa angielska, etykiety te znikają bez żadnego komunikatu o błędzie.

Pięć arkuszy sprite'ów, po jednym na każdy wariant z projektu źródłowego, w rozdzielczości 1x i 2x. Poniższe style pobierają z nich ikony. Jeśli we własnym stylu chcesz mieć punkty miejscowości, tabliczki drogowe lub ikony miejsc (POI), skieruj pole sprite na plik odpowiedniego wariantu. Nazwy ikon w arkuszu są wymienione w odpowiadającym mu pliku light.json.

Co rysuje mapa i skąd pochodzi każdy element

Każda warstwa korzysta z jednej z dziewięciu warstw źródłowych w tym archiwum, więc nic tutaj nie wymaga drugiego archiwum. Dwie rzeczy warto omówić osobno, bo wyglądają jak braki, choć nimi nie są:

WarstwaPochodzi zW tym wydaniu?
Obszary, drogi, budynki, woda, lądearth, landcover, landuse, water, roads, buildingstak
Nazwy miejscowości, dróg i wódpole name na tych samych warstwachtak
Ikony miejsc (POI), punkty miejscowościpois.kind, places oraz arkusz spritetak
Tabliczki numerów drógroads.shield_textnie. Udostępniane wydanie nie zawiera tego pola
Numery domówbuildings.kind=address oraz buildings.addr_housenumbernie, z tego samego powodu

Nie ma osobnego archiwum adresowego ani osobnej warstwy adresów do pobrania. Projekt źródłowy umieszcza punkty adresowe wewnątrz warstwy buildings jako obiekty z atrybutem kind=address oraz polem addr_housenumber. Pole to pojawiło się w nowszym wydaniu Protomaps niż obecnie serwowane przez tę mapę, dlatego warstwa numerów domów jest już przygotowana i włączy się samoczynnie, gdy kolejna comiesięczna aktualizacja dostarczy te dane. Tak samo jest z tekstem tabliczek drogowych: obecne wydanie zawiera pole shield_text_length, które określa liczbę znaków numeru drogi, a nie jego treść, więc narysowana na tej podstawie tabliczka byłaby pustym kształtem na każdej autostradzie.

Samodzielne pisanie i kolorowanie warstwy

Warstwa to filtr, poziom przybliżenia, kształt i kolor. Dwie pułapki w filtrze zdarzają się każdemu i obie kończą się brakiem efektu bez żadnego błędu:

Oto droga, jej nazwa oraz przełącznik wyłączający linię:

{
  "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]
  }
}

Nazwa to warstwa symboli odczytująca pole name, z obwódką (halo) lub bez tła. Brakujący glif nie zgłasza błędu, więc wybrany stos czcionek musi być jednym z dwóch dostępnych na tym hoście:

{
  "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
  }
}

Warstwa, która nic nie rysuje, nie jest traktowana jako błąd i MapLibre nie wskaże, który warunek nie został spełniony. Jeśli warstwa jest pusta, sprawdź kolejno: nazwę pola, minzoom warstwy, stos czcionek, a na końcu nazwę ikony w pliku light.json danego arkusza sprite'ów.

Jeśli masz styl, w którym glify wskazują na demotiles.maplibre.org, cała zmiana polega na podmianie tej nazwy hosta. Ścieżka jest celowo taka sama.

"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",

Jeśli styl zażąda stosu czcionek innego niż dwa powyższe, tekst nie zostanie narysowany. Własne style Protomaps używają Noto Sans Medium dla tekstu pogrubionego; w tym zestawie nie ma Noto Sans Bold, dlatego styl odwołujący się do tej czcionki po cichu traci wszystkie etykiety.

Który host jest szybszy i jaka zapadła decyzja. Zmierzono zamiast zakładać: naprzemienne zapytania, pojedynczo, na jednym połączeniu, pierwsze trzy próbki odrzucone, czas do pierwszego bajtu (TTFB) oddzielony od czasu transferu. W tym teście sieć CDN odpowiadała o 10 do 45 ms szybciej dla każdego z tych plików, co w pełni spełnia regułę 15% i wyraźnie przekracza margines błędu pomiarowego. Z tego powodu biblioteki są ładowane z jsDelivr.

Czcionki i sprite'y są serwowane stąd, mimo że ten sam pomiar wykazuje, iż CDN odpowiedziałby o około 10 ms szybciej. To świadoma decyzja, a nie kwestia pomiaru. Mapa ładowana z jednej nazwy hosta nie przestaje działać przy awarii cudzego serwera, a niedziałający serwer glifów oznacza mapę bez żadnych etykiet. Jeśli wolisz zyskać te 10 ms, skieruj glyphs na protomaps.github.io/basemaps-assets/fonts — działa z tą samą ścieżką. To wyniki jednego użytkownika z jednego miasta, a powtórzenie porównania wymaga jednego polecenia.

Pięć. O bibliotece Leaflet

L.tileLayer w bibliotece Leaflet rysuje obrazy, natomiast tutaj mamy kafelki wektorowe. Mapa Leaflet nadal może z nich korzystać, a oba sposoby zachowują twoje znaczniki, dymki (popups) i kontrolki. Obie metody podmiany opisano w sekcji Przejście z kafelków OpenStreetMap.

Sześć. Odczyt archiwum za pomocą biblioteki PMTiles

Bez mapy, same dane. Przydatne w skryptach, testach i wszędzie tam, gdzie potrzebny jest odczyt danych, a nie obraz.

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);

Zwróć uwagę na dwie klasy. FetchSource odpowiada za odczyt bajtów przez HTTP, a PMTiles działa nad nią i interpretuje format archiwum. FileSource to odpowiednik FetchSource dla plików lokalnych — nie zadziała po przekazaniu ciągu z adresem URL.

Archiwum jest migawką, a nie strumieniem na żywo. Harmonogram aktualizacji znajdziesz w sekcji Jak aktualne są te dane?.

Przejście z kafelków OpenStreetMap

Blokada wynikająca z polityki korzystania z kafelków, błąd 403 i jak sobie z tym poradzić. Bez klucza, bez limitów.

Dlaczego mapa wyświetla komunikat o blokadzie

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

Serwery kafelków celowo wysłały ten obraz zamiast mapy na mocy polityki korzystania z kafelków. To nie awaria, a poprawienie adresu URL nie zdejmie blokady.

Czego zwykle próbuje się najpierw:

Biura, szkoły, sieci VPN i sieci komórkowe współdzielą adresy IP, a blokady obejmują zarówno adresy, jak i aplikacje. Ze swojej strony nie sprawdzisz, która z tych blokad cię dotyczy.

Przestań więc odpytywać tamte serwery. Są trzy sposoby na odejście od nich.

Pozostań przy Leaflet

protomaps-leaflet rysuje kafelki wektorowe w bibliotece Leaflet. Samodzielnie odczytuje archiwum i dodaje się jako zwykła warstwa, dzięki czemu znaczniki, dymki, warstwy GeoJSON i kontrolki zostają bez zmian. Tutaj znajduje się kopia przetestowana z tym archiwum: https://tiles.jasontally.com/vendor/protomaps-leaflet.js.

Przed:

<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: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
  }).addTo(map);
</script>

Po:

<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>

Biblioteka protomaps-leaflet jest w projekcie źródłowym w trybie utrzymaniowym: tylko poprawki błędów, bez nowych funkcji. Do istniejącej aplikacji w Leaflet wystarczy. Nowe projekty warto zaczynać na MapLibre GL.

Pozostań przy Leaflet, renderuj przez MapLibre

@maplibre/maplibre-gl-leaflet umieszcza mapę MapLibre w panelu kafelków (tile pane) biblioteki Leaflet. Użyj tego rozwiązania, gdy chcesz skorzystać z jednego z powyższych stylów albo z terenu i cieniowania rzeźby w aplikacji, która pozostaje przy 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>

Przejdź na MapLibre GL

Jedna biblioteka, żadnego adaptera, a powyższe style działają od razu. Pełny przykład strony znajduje się w sekcji Jeden. Osadź mapę na stronie. Przejście polega głównie na odwróceniu kolejności współrzędnych:

LeafletMapLibre GL
L.map("map").setView([lat, lng], z)new maplibregl.Map({ container: "map", center: [lng, lat], zoom: z })
L.tileLayer(url).addTo(map)styl z wektorowym źródłem danych; zobacz wyżej
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 }), a następnie addLayer
L.control.scale()new maplibregl.ScaleControl()
map.invalidateSize()map.resize()

setView([lat, lng]) przyjmuje najpierw szerokość geograficzną; każde wywołanie w MapLibre przyjmuje [lng, lat]. Jeśli mapa pokazuje niewłaściwą półkulę, to właśnie z tego powodu, a nie przez kafelki. Źródła danych i warstwy dodane przed załadowaniem stylu są pomijane bez błędu, więc dodawaj je wewnątrz map.on("load", …).

“archive contains MVT vector tiles, but leafletRasterLayer is for displaying raster tiles”

Ten komunikat pochodzi z pmtiles.leafletRasterLayer, który rysuje rastrowe archiwa PMTiles jako obrazy. Tutaj znajdują się kafelki wektorowe, więc są odrzucane. Żadna opcja tego nie zmieni, a kopii rastrowej tutaj nie ma. Użyj jednego z rozwiązań opisanych wyżej.

“download.openstreetmap.fr returned 403”

To serwer lustrzany z wyciągami i replikacją danych, a nie serwer kafelków. W październiku 2026 roku zablokował adresy IP po nadmiernym pobieraniu, a operatorzy zapowiedzieli przywracanie dostępu indywidualnie dla każdego przypadku. Nic na tej stronie go nie potrzebuje: tutejsze archiwum pochodzi z wydań Protomaps, więc nie musisz samodzielnie składać plików .osm.pbf. Jeśli pobierasz surowe dane OSM, korzystaj z okresowych wyciągów zamiast ze strumienia minutowego.

Style

Te same kafelki, inne kolory. Wybierz styl, skopiuj go, zmodyfikuj — należy do ciebie.

StylWyglądPlik

Co znajduje się w kafelkach

Odczytywane bezpośrednio z metadanych archiwum przy ładowaniu strony. Są to nazwy warstw, których musi używać styl.

Warstwa źródłowaPrzybliżeniePola
ładowanie…

Czym ta usługa nie jest

Proste odpowiedzi, żeby nic cię później nie zaskoczyło.

Jak aktualne są te dane?

Kafelki są kopią mapy Protomaps Basemap, przebudowywaną raz w miesiącu.

JSON stylu

open raw