Teselas de Protomaps Basemap

Todo el mapa base de Protomaps, del zoom 0 al 15, servido como un único archivo PMTiles por HTTP plano. Apunte cualquier cliente PMTiles a la URL y funcionará.

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

¿Una tesela de OpenStreetMap bloqueada o un error 403 de tile.openstreetmap.org? Empiece aquí.

Pruébelo

El mismo archivo en tres estilos. Al cambiar de estilo, las teselas ya cargadas se mantienen, por lo que el cambio es instantáneo.

z0

Cree el suyo

Cambie un color o desactive una capa y el mapa se actualizará al instante. Luego copie el JSON del estilo, que es un estilo normal de MapLibre que puede alojar por su cuenta. Apunta a estas teselas, así que sigue funcionando donde sea que lo coloque.

Úselo

El archivo admite solicitudes de rango HTTP, por lo que cualquier cliente PMTiles puede leerlo.

Uno. Poner un mapa en la página

Copie este archivo. Es el ejemplo completo: tres etiquetas, una URL de estilo, un mapa. Ambas bibliotecas tienen versión fija y licencia BSD-3-Clause, y este mismo servidor ofrece los mismos archivos si prefiere no usar una 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>

Dos. Cambiar las teselas en un estilo que ya tiene

Todo estilo de MapLibre lee sus teselas de una fuente. Para usar estas teselas, cambie ese único campo url. Nada más cambia en el estilo.

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

O hágalo en el código, lo cual es útil si el estilo proviene de un servidor que usted no controla:

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

Tres. Dónde colocar el estilo que ha creado

El botón JSON del estilo en el editor de arriba abre el estilo que este mapa está dibujando ahora mismo, con sus cambios incluidos, y el cuadro de diálogo tiene un botón para copiar. Hay dos formas de usar lo que genera, y ambas son páginas web normales.

Guárdelo como un archivo junto a su página y apunte style hacia él, tal como se usan los tres estilos en este servidor. Esta es la opción adecuada si el estilo es suyo y cambia con su sitio:

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

O manténgalo dentro del HTML, lo que supone un solo archivo que mover y nada que alojar por separado. Es el mismo estilo, solo cambia el lugar donde reside:

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

Deje las líneas glyphs y sprite tal como están. Son las que dibujan las etiquetas y los iconos, y apuntan a este servidor, de modo que un estilo pegado en su propia página seguirá funcionando desde donde sea que lo sirva. Apúntelas a protomaps.github.io/basemaps-assets si prefiere no depender en absoluto de este servidor.

Una cosa que comprobar antes de hacer el cambio. Este archivo usa el esquema v3 de Protomaps. Un estilo escrito para un esquema distinto cargará pero no dibujará nada, porque los nombres de las capas de origen no coinciden. Más abajo se enumeran los nueve nombres de capa, junto con un estilo v3 listo para copiar.

Cuatro. Fuentes, sprites y las bibliotecas

Un mapa se compone de las teselas, una URL de glyphs para el texto, opcionalmente una URL de sprite para los iconos y las bibliotecas para dibujarlo todo. Todo eso está aquí, en las rutas que cada componente espera, para que el mapa pueda servirse desde un único nombre de host si así lo quiere.

QuéURL aquíLicencia

Los archivos de MapLibre y PMTiles están en jsDelivr y aquí. Cualquiera de las dos URL funciona.

Esta página no utiliza la última fila. Es el renderizador vectorial de Leaflet, publicado aquí para que el cambio requiera una sola etiqueta de script. Leaflet en sí permanece en su propia CDN.

Dos familias tipográficas, Noto Sans Regular y Noto Sans Medium, divididas en los 256 rangos que pide MapLibre, de 256 puntos de código cada uno. Se publican los 256 rangos, no solo los latinos: Protomaps etiqueta los elementos con su nombre local, por lo que Tokio o El Cairo necesitan rangos en las decenas de miles. Si solo publica lo que usa un mapa en inglés, esas etiquetas desaparecen sin ningún error que explique el motivo.

Cinco hojas de sprites, una por cada variante original, a 1x y 2x. Los estilos de abajo dibujan iconos a partir de ellas, por lo que se descargan. Si quiere puntos de poblaciones, escudos de carreteras o puntos de interés en un estilo propio, apunte sprite a la variante que corresponda. Los nombres de los iconos de una hoja están listados en su archivo light.json.

Qué dibuja el mapa y de dónde sale cada cosa

Cada capa corresponde a una de las nueve capas de origen de este archivo, así que nada de lo que hay aquí necesita un segundo archivo. Vale la pena mencionar dos de ellas, porque ambas parecen datos que faltan cuando no es así:

CapaProviene de¿En esta compilación?
Formas, carreteras, edificios, agua, tierraearth, landcover, landuse, water, roads, buildingssí
Nombres de lugares, carreteras y masas de aguael campo name en esas mismas capassí
Iconos de puntos de interés, núcleos urbanospois.kind, places, más el spritesí
Escudos de rutaroads.shield_textno. La compilación que se sirve actualmente no tiene ese campo
Números de portalbuildings.kind=address y buildings.addr_housenumberno, por el mismo motivo

No hay un archivo de direcciones separado ni una capa de direcciones que descargar. El proyecto original coloca los puntos de dirección dentro de la capa buildings, como elementos con kind=address y un addr_housenumber. Ese campo llegó en una compilación de Protomaps posterior a la que sirve este mapa, por lo que la capa de números de portal ya está definida y se activará sola cuando la próxima actualización mensual los incorpore. Lo mismo ocurre con el texto de los escudos de ruta: la compilación actual incluye shield_text_length, que indica cuántos caracteres tiene un número de ruta pero no cuáles son, de modo que un escudo dibujado con ese dato sería una figura en blanco en cada autopista.

Escribir una capa propia y darle color

Una capa se compone de un filtro, un nivel de zoom, una forma y un color. Hay dos detalles en el filtro con los que todo el mundo tropieza, y ambos fallan sin dar error:

Así se definen una carretera, su nombre y un interruptor para ocultar la línea:

{
  "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 nombre es una capa de símbolos que lee el campo name, con un halo o sin nada detrás. Cuando falta un glifo no se produce ningún aviso, así que la fuente solicitada debe ser una de las dos disponibles en este servidor:

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

Que una capa no dibuje nada no se considera un error, y MapLibre no indicará qué comprobación falló. Si la suya aparece en blanco, compruebe en este orden: el nombre del campo, el minzoom de la capa, la fuente tipográfica y luego el nombre del icono en el archivo light.json de la hoja de sprites.

Si tiene un estilo cuyos glifos apuntan a demotiles.maplibre.org, todo el cambio consiste en sustituir ese nombre de host. La ruta es idéntica a propósito.

"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 estilo pide una fuente tipográfica que no sea una de las dos anteriores, no se dibujará ningún texto. Los estilos propios de Protomaps usan Noto Sans Medium para el texto en negrita; en este conjunto no existe Noto Sans Bold, razón por la cual un estilo que la solicite pierde todas las etiquetas sin mostrar errores.

Qué servidor es más rápido y qué se decidió al respecto. Medido en lugar de supuesto: solicitudes intercaladas, una por una, en una sola conexión, descartando las tres primeras muestras y separando el tiempo hasta el primer byte del tiempo de transferencia. En esa prueba, una CDN respondió entre 10 y 45 ms antes para cada uno de estos archivos, lo que supera claramente la regla del 15 % y el margen de las muestras. Por esa razón, las bibliotecas se cargan desde jsDelivr.

Las fuentes y los sprites se sirven desde aquí aunque la misma medición indique que una CDN respondería unos 10 ms antes. Es una decisión, no una medición. Un mapa que proviene de un único nombre de host no deja de funcionar cuando cae el servidor de un tercero, y un servidor de glifos caído significa un mapa sin etiquetas. Si prefiere ganar esos 10 ms, apunte glyphs a protomaps.github.io/basemaps-assets/fonts y funcionará con la misma ruta. Son las cifras de un lector desde una ciudad, y repetir la comparativa requiere un solo comando.

Cinco. Sobre Leaflet

L.tileLayer de Leaflet dibuja imágenes; estas son teselas vectoriales. Un mapa de Leaflet puede seguir usándolas y, con cualquiera de las dos opciones, conserva sus marcadores, ventanas emergentes y controles. Ambos cambios se explican en Migrar desde las teselas de OpenStreetMap.

Seis. Leer el archivo con la biblioteca PMTiles

Sin mapa, solo datos. Útil para scripts, pruebas y cualquier cosa que no sea una imagen.

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

Fíjese en las dos clases. FetchSource es el lector de bytes por HTTP, y PMTiles es el lector que se apoya en él y entiende el formato. FileSource es el equivalente de FetchSource para archivos locales; si se le pasa una cadena con una URL, no funcionará.

El archivo es una instantánea, no un flujo en tiempo real. En ¿Qué tan recientes son los datos? se detalla la frecuencia de actualización.

Migrar desde las teselas de OpenStreetMap

El bloqueo por la política de uso de teselas, el error 403 y qué hacer al respecto. Sin clave, sin cuota.

Por qué el mapa muestra un aviso de bloqueo

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

Los servidores de teselas enviaron esa imagen en lugar del mapa a propósito, aplicando la política de uso de teselas. No es una avería, y corregir la URL no lo soluciona.

Lo que se suele intentar primero:

Oficinas, centros educativos, VPN y redes móviles comparten una misma dirección IP, y los bloqueos se aplican tanto a direcciones como a aplicaciones. Desde su lado no es posible distinguir cuál de los dos casos le afecta.

Así que deje de consultar esos servidores. Hay tres formas de salir de ellos.

Mantener Leaflet

protomaps-leaflet dibuja teselas vectoriales en Leaflet. Lee el archivo directamente y se añade como una capa normal, por lo que los marcadores, ventanas emergentes, GeoJSON y controles se quedan donde están. Aquí hay una copia probada con este archivo: https://tiles.jasontally.com/vendor/protomaps-leaflet.js.

Antes:

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

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

protomaps-leaflet está en modo de mantenimiento en el proyecto original: recibe correcciones, no funciones nuevas. Es adecuado para una aplicación Leaflet ya existente. Los proyectos nuevos deberían empezar con MapLibre GL.

Mantener Leaflet, renderizar con MapLibre

@maplibre/maplibre-gl-leaflet coloca un mapa de MapLibre dentro del panel de teselas de Leaflet. Úselo cuando quiera uno de los estilos de arriba, o relieve y sombreado de laderas, en una aplicación que siga siendo 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>

Pasar a MapLibre GL

Una sola biblioteca, sin adaptadores, y los estilos de arriba funcionan tal cual. La página completa está en Uno. Poner un mapa en la página. El cambio consiste sobre todo en invertir el orden de las coordenadas:

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 estilo con una fuente vectorial; véase más arriba
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 }), luego addLayer
L.control.scale()new maplibregl.ScaleControl()
map.invalidateSize()map.resize()

setView([lat, lng]) recibe primero la latitud; todas las llamadas de MapLibre reciben [lng, lat]. Si un mapa aparece en el hemisferio equivocado se debe a eso, no a las teselas. Las fuentes y capas añadidas antes de que cargue el estilo se descartan sin dar error, así que añádalas dentro de map.on("load", …).

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

Eso proviene de pmtiles.leafletRasterLayer, que dibuja archivos PMTiles ráster como imágenes. Estas son teselas vectoriales, por lo que las rechaza. Ninguna opción cambia ese comportamiento, y aquí no hay una copia en formato ráster. Utilice una de las opciones anteriores.

“download.openstreetmap.fr returned 403”

Ese servidor es una réplica de extractos y replicación, no un servidor de teselas. En octubre de 2026 bloqueó direcciones tras descargas abusivas, y los operadores indicaron que restablecerían el acceso caso por caso. Nada de lo que hay aquí lo necesita: este archivo proviene de las publicaciones de Protomaps, por lo que usted no tiene que ensamblar archivos .osm.pbf por su cuenta. Si descarga datos de OSM directamente, utilice un extracto periódico en lugar del flujo por minuto.

Estilos

Las mismas teselas, distintos colores. Elija uno, cópielo, modifíquelo; es suyo.

EstiloAparienciaArchivo

Qué contienen las teselas

Leído de los propios metadatos del archivo al cargar la página. Estos son los nombres de capa que debe usar un estilo.

Capa de origenZoomCampos
cargando…

Qué no es esto

Respuestas claras, para que nada le sorprenda más adelante.

¿Qué tan recientes son los datos?

Las teselas son una copia de Protomaps Basemap, reconstruida una vez al mes.

JSON del estilo

open raw