Tuiles Protomaps Basemap

L'intégralité de Protomaps Basemap, du zoom 0 au zoom 15, servie sous forme d'une seule archive PMTiles en HTTP standard. Pointez n'importe quel client PMTiles vers l'URL et cela fonctionne.

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

Une tuile OpenStreetMap bloquée ou une erreur 403 depuis tile.openstreetmap.org ? Commencez ici.

Essayer

La même archive dans trois styles. Changez de style : les tuiles déjà chargées restent en place, le changement est donc instantané.

z0

Créer le vôtre

Modifiez une couleur ou désactivez une couche et la carte se met à jour en direct. Copiez ensuite le JSON du style : c'est un style MapLibre standard que vous pouvez héberger vous-même. Il pointe vers ces tuiles et continue donc de fonctionner où que vous le placiez.

Utilisation

L'archive prend en charge les requêtes HTTP Range, de sorte que tout client PMTiles peut la lire.

Un. Afficher une carte sur la page

Copiez ce fichier. Il contient tout l'exemple : trois balises, une URL de style, une carte. Les deux bibliothèques ont une version figée sous licence BSD-3-Clause, et les mêmes fichiers sont servis depuis cet hôte si vous préférez éviter un 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>

Deux. Remplacer les tuiles dans un style existant

Chaque style MapLibre lit ses tuiles depuis une source. Pour utiliser ces tuiles, modifiez uniquement ce champ url. Rien d'autre ne change dans le style.

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

Ou faites-le dans le code, ce qui est utile si le style provient d'un serveur que vous ne contrôlez pas :

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

Trois. Où placer le style que vous avez créé

Le bouton JSON du style dans l'éditeur ci-dessus ouvre le style que cette carte affiche en ce moment, modifications comprises, avec un bouton pour le copier. Il y a deux façons d'utiliser ce code dans une page web ordinaire.

Enregistrez-le dans un fichier à côté de votre page et faites pointer style dessus, comme le font les trois styles de cet hôte. C'est l'option à choisir si le style vous appartient et évolue avec votre 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
});

Ou gardez-le directement dans le HTML : un seul fichier à déplacer et rien d'autre à héberger. Le style est identique, seul son emplacement change :

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

Laissez les lignes glyphs et sprite telles quelles. Elles servent à dessiner les libellés et les icônes, et pointent vers cet hôte : un style collé dans votre propre page continuera donc de fonctionner quel que soit l'endroit d'où vous le servez. Faites-les plutôt pointer vers protomaps.github.io/basemaps-assets si vous préférez ne pas dépendre du tout de cet hôte.

Un point à vérifier avant de remplacer vos tuiles. Cette archive utilise le schéma Protomaps v3. Un style écrit pour un autre schéma se chargera mais n'affichera rien, car les noms des couches sources ne correspondent pas. Les neuf noms de couches sont listés ci-dessous, ainsi qu'un style v3 prêt à l'emploi que vous pouvez copier.

Quatre. Polices, sprites et bibliothèques

Une carte se compose des tuiles, d'une URL glyphs pour le texte, éventuellement d'une URL sprite pour les icônes, et des bibliothèques chargées de tout afficher. Tout cela est hébergé ici, aux chemins attendus par chaque composant, pour que la carte puisse provenir d'un seul nom d'hôte si vous le souhaitez.

ÉlémentURL localeLicence

Les fichiers MapLibre et PMTiles sont disponibles sur jsDelivr et ici. Les deux URL fonctionnent.

La dernière ligne n'est pas utilisée par cette page. Il s'agit du moteur de rendu vectoriel pour Leaflet, publié ici afin que la migration tienne en une seule balise de script. Leaflet lui-même reste sur son propre CDN.

Deux piles de polices, Noto Sans Regular et Noto Sans Medium, découpées dans les 256 plages demandées par MapLibre, par blocs de 256 points de code. Les 256 plages sont publiées, pas seulement les caractères latins : Protomaps affiche les noms locaux, de sorte que Tokyo ou Le Caire nécessitent des plages situées dans les dizaines de milliers. Si l'on ne publie que ce qu'utilise une carte en anglais, ces libellés disparaissent sans la moindre erreur.

Cinq feuilles de sprites, une par variante amont, en 1x et 2x. Les styles ci-dessous y puisent leurs icônes et les téléchargent donc. Si vous voulez des points de villes, des cartouches routiers ou des points d'intérêt dans votre propre style, faites pointer sprite vers la variante correspondante. Les noms des icônes d'une feuille sont listés dans son fichier light.json.

Ce que la carte affiche et d'où provient chaque élément

Chaque couche provient de l'une des neuf couches sources de cette archive ; rien ici ne nécessite une seconde archive. Deux cas méritent d'être signalés, car ils ressemblent à des données manquantes alors qu'il n'en est rien :

CoucheProvient deDans cette version ?
Formes, routes, bâtiments, eau, terresearth, landcover, landuse, water, roads, buildingsoui
Noms de lieux, de routes et de cours d'eaule champ name de ces mêmes couchesoui
Icônes de lieux, points de villespois.kind, places, ainsi que le spriteoui
Cartouches routiersroads.shield_textnon. La version servie actuellement ne contient pas ce champ
Numéros de ruebuildings.kind=address et buildings.addr_housenumbernon, pour la même raison

Il n'y a pas d'archive d'adresses séparée ni de couche d'adresses à récupérer. Le projet amont place les points d'adresse à l'intérieur de la couche buildings, sous forme d'objets ayant kind=address et un champ addr_housenumber. Ce champ a été ajouté dans une version de Protomaps plus récente que celle servie actuellement ; la couche des numéros de rue est déjà prête et s'activera d'elle-même lors de la prochaine mise à jour mensuelle. Il en va de même pour les cartouches routiers : la version actuelle contient shield_text_length, qui indique le nombre de caractères du numéro de route mais pas son texte, ce qui dessinerait un cartouche vide sur chaque autoroute.

Écrire soi-même une couche et la colorer

Une couche se définit par un filtre, un zoom, une forme et une couleur. Deux pièges dans les filtres trompent tout le monde et échouent tous deux sans renvoyer d'erreur :

Voici donc une route, son nom et une option pour masquer la ligne :

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

Un nom est une couche de type symbole qui lit le champ name, avec ou sans halo derrière. Un glyphe manquant échoue en silence : la pile de polices demandée doit donc être l'une des deux présentes sur cet hôte :

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

Une couche qui n'affiche rien n'est pas considérée comme une erreur, et MapLibre n'indiquera pas quel test a échoué. Si la vôtre reste vide, vérifiez dans cet ordre : le nom du champ, le minzoom de la couche, la pile de polices, puis le nom de l'icône dans le fichier light.json de la feuille de sprites.

Si vous avez un style dont les glyphes pointent vers demotiles.maplibre.org, il suffit de remplacer ce nom d'hôte. Le chemin est volontairement identique.

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

Si un style demande une pile de polices qui n'est pas l'une des deux ci-dessus, aucun texte ne sera affiché. Les styles de Protomaps utilisent Noto Sans Medium pour le texte en gras ; il n'y a pas de Noto Sans Bold dans cet ensemble, c'est pourquoi un style qui en demande un perd tous ses libellés sans erreur.

Quel hôte est le plus rapide, et ce qui a été retenu. Mesuré plutôt que supposé : requêtes entrelacées, une par une, sur une seule connexion, trois premiers échantillons écartés, temps jusqu'au premier octet séparé du transfert. Lors de ce test, un CDN a répondu 10 à 45 ms plus tôt pour chacun de ces fichiers, ce qui dépasse nettement le seuil de 15 %. C'est pour cette raison que les bibliothèques sont chargées depuis jsDelivr.

Les polices et les sprites sont servis depuis cet hôte, même si la même mesure montre qu'un CDN répondrait environ 10 ms plus tôt. C'est un choix, pas une mesure. Une carte qui provient d'un seul nom d'hôte ne tombe pas en panne lorsqu'un service tiers flanche, et un serveur de glyphes indisponible produit une carte sans libellés. Si vous préférez gagner ces 10 ms, faites pointer glyphs vers protomaps.github.io/basemaps-assets/fonts : cela fonctionne avec le même chemin. Ce sont les chiffres d'un lecteur depuis une ville donnée, et une seule commande suffit pour relancer la comparaison.

Cinq. À propos de Leaflet

L.tileLayer dans Leaflet affiche des images, tandis que ces tuiles sont vectorielles. Une carte Leaflet peut tout de même les utiliser, et les deux méthodes conservent vos marqueurs, popups et contrôles. Les deux remplacements sont détaillés dans Quitter les tuiles OpenStreetMap.

Six. Lire l'archive avec la bibliothèque PMTiles

Pas de carte, juste les données. Utile pour les scripts, les tests et tout ce qui n'est pas une image.

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

Notez les deux classes. FetchSource lit les octets via HTTP, et PMTiles s'appuie dessus pour interpréter le format. FileSource est l'équivalent de FetchSource pour un fichier local ; il ne fonctionnera pas si vous lui passez une URL sous forme de chaîne.

L'archive est un instantané, pas un flux en temps réel. La section Quelle est la fraîcheur des données ? détaille la fréquence de mise à jour.

Quitter les tuiles OpenStreetMap

Le blocage lié à la politique d'utilisation des tuiles, l'erreur 403 et comment y remédier. Sans clé, sans quota.

Pourquoi la carte affiche un message de blocage

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

Les serveurs de tuiles ont envoyé cette image à la place de la carte, délibérément, en application de la politique d'utilisation des tuiles. Ce n'est pas une panne, et corriger l'URL ne lève pas le blocage.

Ce que l'on essaie en premier :

Les bureaux, les écoles, les VPN et les réseaux mobiles partagent une même adresse IP, et les blocages s'appliquent aussi bien aux adresses qu'aux applications. De votre côté, impossible de savoir quel cas vous concerne.

Cessez donc d'interroger ces serveurs. Trois solutions permettent de s'en passer.

Conserver Leaflet

protomaps-leaflet dessine des tuiles vectorielles dans Leaflet. Il lit l'archive lui-même et s'ajoute comme une couche ordinaire : vos marqueurs, popups, GeoJSON et contrôles restent en place. Une copie testée avec cette archive est hébergée ici : https://tiles.jasontally.com/vendor/protomaps-leaflet.js.

Avant :

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

Après :

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

En amont, protomaps-leaflet est en mode maintenance : corrections de bogues uniquement, pas de nouvelles fonctionnalités. Cela convient à une application Leaflet existante. Pour un nouveau projet, partez plutôt sur MapLibre GL.

Conserver Leaflet, afficher avec MapLibre

@maplibre/maplibre-gl-leaflet place une carte MapLibre dans le panneau de tuiles de Leaflet. Utilisez-le si vous voulez l'un des styles ci-dessus, ou du relief et de l'ombrage, dans une application qui reste sous 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>

Passer à MapLibre GL

Une seule bibliothèque, aucun adaptateur, et les styles ci-dessus fonctionnent tels quels. La page complète se trouve dans Un. Afficher une carte sur la page. Le changement consiste surtout à inverser l'ordre des coordonnées :

LeafletMapLibre GL
L.map("map").setView([lat, lng], z)new maplibregl.Map({ container: "map", center: [lng, lat], zoom: z })
L.tileLayer(url).addTo(map)un style avec une source vectorielle ; voir ci-dessus
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 }), puis addLayer
L.control.scale()new maplibregl.ScaleControl()
map.invalidateSize()map.resize()

setView([lat, lng]) prend la latitude en premier ; chaque appel MapLibre prend [lng, lat]. Si votre carte se retrouve dans le mauvais hémisphère, cela vient de là, pas des tuiles. Les sources et les couches ajoutées avant le chargement du style sont ignorées sans erreur : ajoutez-les dans map.on("load", …).

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

Ce message provient de pmtiles.leafletRasterLayer, qui affiche des PMTiles raster sous forme d'images. Comme ces tuiles sont vectorielles, il les refuse. Aucune option ne permet de changer cela et il n'existe pas de copie raster ici. Utilisez l'une des solutions ci-dessus.

“download.openstreetmap.fr returned 403”

Il s'agit d'un miroir d'extraits et de réplication, pas d'un serveur de tuiles. En octobre 2026, il a bloqué des adresses suite à des téléchargements abusifs, et les administrateurs ont indiqué qu'ils rétabliraient l'accès au cas par cas. Rien ici n'en dépend : cette archive provient des versions publiées par Protomaps, vous n'avez donc pas à assembler de fichiers .osm.pbf vous-même. Si vous récupérez des données OSM, téléchargez un extrait périodique plutôt que le flux à la minute.

Styles

Les mêmes tuiles, des couleurs différentes. Choisissez-en un, copiez-le, modifiez-le : il est à vous.

StyleAperçuFichier

Contenu des tuiles

Lu directement depuis les métadonnées de l'archive au chargement de la page. Ce sont les noms de couches qu'un style doit utiliser.

Couche sourceZoomChamps
chargement…

Ce que ce service n'est pas

Des réponses claires, pour éviter toute mauvaise surprise par la suite.

Quelle est la fraîcheur des données ?

Les tuiles sont une copie de Protomaps Basemap, reconstruite une fois par mois.

JSON du style

open raw