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
- Sin clave de APISin registros ni tokens en su código.
- Sin límite de peticionesSin estrangulamiento, sin cuota, sin tope de ráfagas.
- MensualReconstruido una vez al mes a partir de la versión más reciente de Protomaps.
¿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.
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\">© 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í:
| Capa | Proviene de | ¿En esta compilación? |
|---|---|---|
| Formas, carreteras, edificios, agua, tierra | earth, landcover, landuse, water, roads, buildings | sí |
| Nombres de lugares, carreteras y masas de agua | el campo name en esas mismas capas | sí |
| Iconos de puntos de interés, núcleos urbanos | pois.kind, places, más el sprite | sí |
| Escudos de ruta | roads.shield_text | no. La compilación que se sirve actualmente no tiene ese campo |
| Números de portal | buildings.kind=address y buildings.addr_housenumber | no, 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:
kindes la categoría general. Para carreteras es uno de estos valores:highway,major_road,minor_road,path,rail. Los nombres que aparecen en la documentación original —motorway,primary,residential— son valores dekind_detail, y una capa que filtrekindbuscando esos valores no dibujará nada ni avisará en ningún sitio.- Una capa no se dibuja por debajo de su propio
minzoom. El archivo llega hasta z15, por lo que una capa cuyo minzoom sea 16 no se dibujará nunca. La mayoría de los elementos también tienen su propiomin_zoom, el nivel de zoom a partir del cual los datos indican que vale la pena mostrarlos, y los estilos incluidos también comprueban ese valor.
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:
- Cambiar
a/b/cpor el host canónico no elimina el bloqueo. La URL canónica es obligatoria, pero el bloqueo se asocia a la aplicación o al encabezado Referer detectado, no al nombre de host. - Reintentar las peticiones lo empeora. El volumen de tráfico es parte del motivo del bloqueo.
- Ningún truco con encabezados salva una página abierta desde el disco.
file://no envíaReferer. EnviarX-Requested-Withen su lugar requeriría una solicitud preflight CORS por cada tesela, y los servidores de teselas también rechazan ese preflight. Firefox y Chromium difieren en qué encabezados envían, pero la regla es la misma para ambos. - ¿Funciona en
localhostpero se bloquea en el sitio en producción? La causa es el origen. Lo que se bloquea es el Referer de su dominio, ylocalhostenvía uno distinto.
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: '© <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>
flavorpuede serlight,dark,white,grayscaleoblack. Son los temas internos de la biblioteca, no los estilos de arriba, por lo que los colores no coincidirán exactamente.- Las etiquetas son texto en canvas con la fuente del sistema. No hay URL de glifos ni fuentes que puedan dar error 404.
- z15 es la tesela más detallada. La capa amplía los datos más allá de ese nivel, y
maxDataZoom: 15ya viene configurado por defecto.
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>
- Registre primero el protocolo. Sin la línea
addProtocol, la consola muestraURL scheme "pmtiles" is not supportedy el mapa se queda en blanco. - MapLibre dibuja un nivel de zoom por debajo de Leaflet. Sus teselas son de 512 px frente a los 256 px de Leaflet, por lo que una capa con
minzoom12 aparece cuando Leaflet llega a z13. - Leaflet sigue dibujando encima. El lienzo se sitúa en el panel de teselas, de modo que los marcadores y las superposiciones se comportan igual que sobre una capa ráster.
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:
| 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) | 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.
| Estilo | Apariencia | Archivo |
|---|
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 origen | Zoom | Campos |
|---|---|---|
| cargando… | ||
Qué no es esto
Respuestas claras, para que nada le sorprenda más adelante.
- No hay garantía de nivel de servicio. Este es un servicio gratuito sin ningún contrato detrás. Puede ir lento, puede caerse, y tanto la URL como los datos pueden cambiar o desaparecer sin previo aviso. Si su producto depende de ello, aloje su propia copia de las teselas o contrate un plan con soporte de algún proveedor.
- Nunca está actualizado al minuto. La copia es una instantánea de la versión más reciente de Protomaps, no un flujo en vivo. Las ediciones hechas hoy no están incluidas. En ¿Qué tan recientes son los datos? se explica la frecuencia.
- No se permite descargar el archivo completo. Las solicitudes se responden en rangos de como máximo 512 KiB, que es todo lo que pide un cliente PMTiles. Solicitar el archivo entero en una sola petición devuelve
416, que es el comportamiento correcto. - La atribución es obligatoria. Las teselas se derivan de OpenStreetMap. Mantenga la línea de atribución en su estilo o en su mapa.
¿Qué tan recientes son los datos?
Las teselas son una copia de Protomaps Basemap, reconstruida una vez al mes.
- Reconstruido una vez al mes. El proceso se ejecuta el primer domingo de cada mes, entre las 00:30 y las 07:00 UTC, la única franja en la que el proyecto original no ha publicado en tres años de historial de versiones. Es mensual y no semanal porque un archivo nuevo invalida las partes en caché de la CDN y de cada navegador que ya las tenía, y cada reconstrucción consume tiempo de compilación.
- La mayoría de los meses no hay nada que hacer. Solo se reconstruye cuando el proyecto original ha publicado una versión nueva.
- ¿Qué compilación original está en uso ahora mismo? Use el botón Metadatos del archivo en la parte superior de esta página. Lee directamente el encabezado del propio archivo, por lo que la respuesta está siempre al día en lugar de ser un dato estático que pudiera haber quedado desfasado en la página.
- Una reconstrucción no es instantánea. Son trece reconstrucciones separadas repartidas a lo largo de unas seis horas y media, una tras otra. Durante la mayor parte de esa ventana, la copia es una mezcla de dos compilaciones originales. Una tesela que solicite durante ese tiempo puede provenir de la compilación más nueva en lugar de la anunciada por el encabezado, lo cual se muestra como una tesela del lugar equivocado, no como un mapa roto.
- Una reconstrucción puede fallar. Es una tarea sin garantías. Si un paso falla, el resto de la copia permanece en la compilación original anterior. El mapa sigue funcionando.