Protomaps Basemap-tegels

De volledige Protomaps Basemap, zoomniveau 0 tot en met 15, aangeboden als één PMTiles-archief via gewone HTTP. Laat een willekeurige PMTiles-client naar de URL verwijzen en het werkt.

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

Een geblokkeerde OpenStreetMap-tegel, of een 403 van tile.openstreetmap.org? Begin hier.

Probeer het

Hetzelfde archief in drie stijlen. Als je van stijl wisselt, blijven de al geladen tegels behouden, dus de omschakeling is direct.

z0

Maak je eigen stijl

Verander een kleur of zet een laag uit en de kaart werkt direct bij. Kopieer daarna de stijl-JSON; dat is een gewone MapLibre-stijl die je zelf kunt hosten. Hij verwijst naar deze tegels en blijft dus werken, waar je hem ook neerzet.

Gebruiken

Het archief ondersteunt HTTP-range-requests, zodat elke PMTiles-client het kan lezen.

Een. Zet een kaart op de pagina

Kopieer dit bestand. Dit is het volledige voorbeeld: drie tags, één stijl-URL, één kaart. Beide bibliotheken zijn op een vaste versie vastgezet en vallen onder BSD-3-Clause; dezelfde bestanden worden ook vanaf deze host geleverd als je liever geen CDN gebruikt.

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

Twee. Vervang de tegels in een bestaande stijl

Elke MapLibre-stijl leest zijn tegels uit een bron. Om deze tegels te gebruiken, pas je die ene url aan. Verder verandert er niets aan de stijl.

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

Of doe het in code, wat handig is als de stijl van een server komt die je niet zelf beheert:

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

Drie. Waar laat je de gemaakte stijl

De knop Stijl-JSON in de editor hierboven opent de stijl die deze kaart nu tekent, inclusief je aanpassingen, en het venster heeft een kopieerknop. Er zijn twee manieren om het resultaat te gebruiken, en beide zijn gewone webpagina's.

Sla hem op als bestand naast je pagina en laat style ernaar verwijzen, net zoals de drie stijlen op deze host worden gebruikt. Kies deze optie als de stijl van jezelf is en meeverandert met je site:

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

Of zet hem direct in de HTML; dan heb je maar één bestand om te verplaatsen en niets extra's om te hosten. Dezelfde stijl, alleen op een andere plek:

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

Laat de regels glyphs en sprite staan zoals ze zijn. Zij zorgen voor het tekenen van de labels en pictogrammen en verwijzen naar deze host, zodat een stijl die je in je eigen pagina plakt blijft werken vanaf elke plek waar je hem aanbiedt. Laat ze in plaats daarvan verwijzen naar protomaps.github.io/basemaps-assets als je liever helemaal niet afhankelijk bent van deze host.

Eén ding om te controleren voor je wisselt. Dit archief gebruikt Protomaps-schema v3. Een stijl die voor een ander schema is geschreven, wordt wel geladen maar tekent niets, omdat de namen van de bronlagen niet overeenkomen. De negen laagnamen staan hieronder, samen met een kant-en-klare v3-stijl die je kunt kopiëren.

Vier. Lettertypen, sprites en de bibliotheken

Een kaart bestaat uit de tegels, een glyphs-URL voor tekst, eventueel een sprite-URL voor pictogrammen, en de bibliotheken om alles te tekenen. Dat staat hier allemaal op de paden die elk onderdeel verwacht, zodat een kaart desgewenst vanaf één enkele hostnaam kan komen.

OnderdeelURL hierLicentie

De bestanden voor MapLibre en PMTiles staan op jsDelivr en hier. Beide URL's werken.

De laatste rij wordt niet door deze pagina gebruikt. Het is de vectorrenderer voor Leaflet, hier gepubliceerd zodat de overstap maar één script-tag kost. Leaflet zelf blijft op zijn eigen CDN.

Twee lettertypereeksen, Noto Sans Regular en Noto Sans Medium, opgedeeld in de 256 bereiken waar MapLibre om vraagt, per 256 codepunten tegelijk. Alle 256 zijn gepubliceerd, niet alleen het Latijnse schrift: Protomaps zet labels in de lokale naam op de kaart, waardoor Tokio of Caïro bereiken in de tienduizenden nodig heeft. Publiceer je alleen wat een Engelstalige kaart gebruikt, dan verdwijnen die labels zonder foutmelding.

Vijf sprite-sheets, één per upstream-variant, op 1x en 2x. De stijlen hieronder halen hun pictogrammen daaruit op, dus die worden geladen. Wil je plaatsstippen, wegennummerschildjes of locatiesymbolen in een eigen stijl gebruiken, laat sprite dan verwijzen naar de variant die je gebruikt. De pictogramnamen in een sheet staan vermeld in de bijbehorende light.json.

Wat de kaart tekent en waar elk onderdeel vandaan komt

Elke laag komt uit een van de negen bronlagen in dit archief; er is dus nergens een tweede archief voor nodig. Twee lagen verdienen extra uitleg, omdat het bij beide lijkt alsof er iets ontbreekt terwijl dat niet zo is:

LaagKomt uitIn deze build?
Vlakken, wegen, gebouwen, water, landearth, landcover, landuse, water, roads, buildingsja
Plaats-, straat- en waternamenhet veld name op diezelfde lagenja
Locatiesymbolen, plaatsstippenpois.kind, places, plus de spriteja
Wegennummerschildjesroads.shield_textnee. De huidige build bevat dat veld niet
Huisnummersbuildings.kind=address en buildings.addr_housenumbernee, om dezelfde reden

Er is geen apart adresarchief en geen aparte adreslaag om op te halen. Het upstream-project plaatst adrespunten binnen de laag buildings, als objecten met kind=address en een addr_housenumber. Dat veld is toegevoegd in een latere Protomaps-build dan de build die deze kaart nu serveert; de huisnummerlaag staat al klaar en schakelt zichzelf in zodra de volgende maandelijkse verversing die gegevens meeneemt. Hetzelfde geldt voor de schildjestekst: de huidige build bevat shield_text_length, oftewel het aantal tekens van een wegnummer en niet het nummer zelf, waardoor een schildje op basis daarvan op elke snelweg een leeg vlak zou zijn.

Zelf een laag schrijven en inkleuren

Een laag bestaat uit een filter, een zoomniveau, een vorm en een kleur. Twee dingen in het filter zorgen bij bijna iedereen voor problemen, en beide falen zonder foutmelding:

Een weg, de bijbehorende naam en een schakelaar om de lijn uit te zetten zien er dus zo uit:

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

Een naam is een symboollaag die het veld name leest, met een halo of niets erachter. Een ontbrekende glyph geeft geen melding, dus het lettertype dat je opvraagt moet een van de twee op deze host zijn:

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

Een laag die niets tekent is geen fout, en MapLibre meldt niet welke controle is mislukt. Blijft je laag leeg, controleer dan in deze volgorde: de veldnaam, de minzoom van de laag, het lettertype en daarna de pictogramnaam aan de hand van de light.json van de sprite-sheet.

Heb je een stijl waarin de glyphs naar demotiles.maplibre.org verwijzen, dan is het vervangen van die hostnaam de enige aanpassing. Het pad is bewust hetzelfde.

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

Als een stijl om een lettertype vraagt dat niet een van de twee hierboven is, wordt daar niets voor getekend. De eigen stijlen van Protomaps gebruiken Noto Sans Medium voor vette tekst; Noto Sans Bold zit niet in deze set, en daarom verliest een stijl die daarom vraagt zonder melding elk label.

Welke host sneller is, en wat op basis daarvan is besloten. Gemeten in plaats van aangenomen: afwisselende verzoeken, één voor één over één verbinding, de eerste drie metingen weggelaten, tijd tot de eerste byte gescheiden van de overdracht. In die test antwoordde een CDN voor elk van deze bestanden 10 tot 45 ms sneller, wat ruimschoots voldoet aan de 15%-regel en duidelijk buiten de ruis van de metingen valt. Om die reden worden de bibliotheken vanaf jsDelivr geladen.

De lettertypen en sprites worden vanaf hier geleverd, ook al laat dezelfde meting zien dat een CDN ongeveer 10 ms sneller zou antwoorden. Dat is een keuze, geen meting. Een kaart die van één hostnaam komt, valt niet om als de server van iemand anders uitvalt, en een onbereikbare glyph-server betekent een kaart zonder labels. Heb je liever die 10 ms winst, laat glyphs dan verwijzen naar protomaps.github.io/basemaps-assets/fonts; dat werkt met hetzelfde pad. Dit zijn de cijfers van één gebruiker in één stad, en de vergelijking opnieuw uitvoeren kost één commando.

Vijf. Over Leaflet

L.tileLayer van Leaflet tekent afbeeldingen; dit zijn vectortegels. Een Leaflet-kaart kan ze toch gebruiken, en in beide gevallen blijven je markers, pop-ups en bedieningselementen behouden. Beide manieren staan in Overstappen vanaf OpenStreetMap-tegels.

Zes. Het archief lezen met de PMTiles-bibliotheek

Geen kaart, alleen gegevens. Handig voor scripts, tests en alles wat geen afbeelding is.

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

Let op de twee klassen. FetchSource leest de bytes via HTTP, en PMTiles is de lezer daarbovenop die het bestandsformaat begrijpt. FileSource is het equivalent van FetchSource voor lokale bestanden; met een URL-string werkt die niet.

Het archief is een momentopname, geen live-feed. In Hoe actueel is dit? staat het tijdschema.

Overstappen vanaf OpenStreetMap-tegels

De blokkade door het beleid voor tegelgebruik, de 403-melding en wat je eraan doet. Geen sleutel, geen quotum.

Waarom de kaart een blokkade toont

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

De tegelservers hebben die afbeelding expres gestuurd in plaats van de kaart, op grond van het beleid voor tegelgebruik. Het is geen storing, en het aanpassen van de URL lost het niet op.

Wat mensen als eerste proberen:

Kantoren, scholen, VPN's en mobiele netwerken delen een adres, en blokkades gelden zowel voor adressen als voor apps. Vanaf jouw kant kun je niet zien welke van de twee het is.

Stop dus met het bevragen van die servers. Er zijn drie manieren om over te stappen.

Bij Leaflet blijven

protomaps-leaflet tekent vectortegels in Leaflet. Het leest het archief zelf en voegt zich toe als een gewone laag, zodat markers, pop-ups, GeoJSON en bedieningselementen op hun plek blijven. Een exemplaar staat hier, getest met dit archief: https://tiles.jasontally.com/vendor/protomaps-leaflet.js.

Voor:

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

Na:

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

protomaps-leaflet verkeert upstream in onderhoudsmodus: wel foutoplossingen, geen nieuwe functies. Prima voor een bestaande Leaflet-app. Nieuw werk kun je beter op MapLibre GL beginnen.

Bij Leaflet blijven, tekenen met MapLibre

@maplibre/maplibre-gl-leaflet plaatst een MapLibre-kaart in het tegelpaneel van Leaflet. Gebruik dit als je een van de stijlen hierboven wilt, of terrein en schaduwreliëf, in een app die op Leaflet blijft.

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

Overstappen op MapLibre GL

Één bibliotheek, geen adapter, en de stijlen hierboven werken direct. De volledige pagina staat bij Een. Zet een kaart op de pagina. De aanpassing bestaat vooral uit het omdraaien van de coördinaten:

LeafletMapLibre GL
L.map("map").setView([lat, lng], z)new maplibregl.Map({ container: "map", center: [lng, lat], zoom: z })
L.tileLayer(url).addTo(map)een stijl met een vectorbron; zie hierboven
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 }), gevolgd door addLayer
L.control.scale()new maplibregl.ScaleControl()
map.invalidateSize()map.resize()

setView([lat, lng]) verwacht eerst de breedtegraad; elke aanroep in MapLibre verwacht [lng, lat]. Een kaart op het verkeerde halfrond komt daardoor, niet door de tegels. Bronnen en lagen die worden toegevoegd voordat de stijl is geladen, worden zonder foutmelding genegeerd; voeg ze dus toe binnen map.on("load", …).

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

Dat komt van pmtiles.leafletRasterLayer, die raster-PMTiles als afbeeldingen tekent. Dit zijn vectortegels, dus die worden geweigerd. Geen enkele instelling verandert dat, en er is hier geen rasterversie. Gebruik een van de opties hierboven.

“download.openstreetmap.fr returned 403”

Dat is een mirror voor extracten en replicatie, geen tegelserver. In oktober 2026 heeft die adressen geblokkeerd na overmatig downloaden, en de beheerders gaven aan dat ze de toegang per geval zouden herstellen. Niets op deze site heeft die server nodig: dit archief komt uit uitgaven van Protomaps, dus je bouwt niet zelf .osm.pbf-bestanden op. Haal je wel zelf OSM-gegevens op, gebruik dan een periodiek extract in plaats van de minuutstream.

Stijlen

Dezelfde tegels, andere kleuren. Kies er een, kopieer hem, pas hem aan; hij is van jou.

StijlVoorbeeldBestand

Wat er in de tegels zit

Bij het laden van de pagina uit de eigen metadata van het archief gelezen. Dit zijn de laagnamen die een stijl moet gebruiken.

BronlaagZoomniveauVelden
laden…

Wat dit niet is

Duidelijke antwoorden, zodat niets hier je later verrast.

Hoe actueel is dit?

De tegels zijn een kopie van de Protomaps Basemap en worden eens per maand opnieuw opgebouwd.

Stijl-JSON

open raw