Tile di Protomaps Basemap
L'intera Protomaps Basemap, dallo zoom 0 al 15, servita come un unico archivio PMTiles su normale HTTP. Punta qualsiasi client PMTiles all'URL e funziona.
https://tiles.jasontally.com/basemap.pmtiles
- Nessuna chiave APINessuna registrazione, nessun token nel codice.
- Nessun limite di richiesteNessuna limitazione di banda, nessuna quota, nessun tetto ai picchi.
- MensileRicostruito una volta al mese dall'ultima versione di Protomaps.
Una tile OpenStreetMap bloccata o un 403 da tile.openstreetmap.org? Inizia da qui.
Provalo
Lo stesso archivio in tre stili. Cambiando stile, le tile già caricate restano al loro posto: il passaggio è istantaneo.
Crea il tuo stile
Cambia un colore o disattiva un layer e la mappa si aggiorna in tempo reale. Poi copia il JSON dello stile: è un normale stile MapLibre che puoi ospitare per conto tuo. Punta a queste tile, quindi continua a funzionare ovunque lo metti.
Come usarlo
L'archivio supporta le range request HTTP, quindi ogni client PMTiles può leggerlo.
Uno. Mettere una mappa nella pagina
Copia questo file. È l'intero esempio: tre tag, un URL di stile, una mappa. Entrambe le librerie hanno versione fissata e licenza BSD-3-Clause, e gli stessi file sono serviti anche da questo host se preferisci non usare 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>
Due. Sostituire le tile in uno stile esistente
Ogni stile MapLibre legge le tile da una sorgente. Per usare queste tile, cambia quell'unico url. Nel resto dello stile non cambia nulla.
"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>"
}
}
Oppure fallo via codice, utile se lo stile proviene da un server che non controlli:
// 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 });
Tre. Dove mettere lo stile che hai creato
Il pulsante JSON dello stile nell'editor qui sopra apre lo stile che la mappa sta disegnando in questo momento, modifiche incluse, con un pulsante per copiarlo. Ci sono due modi per usare il risultato, ed entrambi sono normali pagine web.
Salvalo come file accanto alla tua pagina e fai puntare style a quel file, proprio come fanno i tre stili su questo host. È la scelta giusta se lo stile è tuo e cambia insieme al tuo sito:
// 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
});
Oppure tienilo direttamente nell'HTML: un solo file da spostare e nulla da ospitare a parte. Lo stile è lo stesso, cambia solo dove si trova:
// 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 });
Lascia invariate le righe glyphs e sprite. Servono a disegnare le etichette e le icone e puntano a questo host, così uno stile incollato nella tua pagina continua a funzionare da qualsiasi posto lo servi. Puntale invece a protomaps.github.io/basemaps-assets se preferisci non dipendere affatto da questo host.
Una cosa da verificare prima di sostituire le tile. Questo archivio usa Protomaps schema v3. Uno stile scritto per uno schema diverso verrà caricato ma non disegnerà nulla, perché i nomi dei layer sorgente non coincidono. I nove nomi dei layer sono elencati più avanti, insieme a uno stile v3 pronto da cui copiare.
Quattro. Font, sprite e librerie
Una mappa è fatta dalle tile, da un URL glyphs per il testo, facoltativamente da un URL sprite per le icone e dalle librerie che disegnano il tutto. Tutto questo si trova qui, ai percorsi che ciascun componente si aspetta, così l'intera mappa può provenire da un unico hostname se lo desideri.
| Cosa | URL su questo host | Licenza |
|---|
I file di MapLibre e PMTiles sono sia su jsDelivr sia qui. Entrambi gli URL funzionano.
L'ultima riga non è usata da questa pagina. È il renderer vettoriale per Leaflet, pubblicato qui in modo che la sostituzione richieda un solo tag script. Leaflet resta sulla propria CDN.
Due font, Noto Sans Regular e Noto Sans Medium, suddivisi nei 256 intervalli richiesti da MapLibre, 256 codepoint alla volta. Sono pubblicati tutti i 256 intervalli, non solo quelli latini: Protomaps etichetta gli elementi con il nome locale, quindi Tokyo o Il Cairo richiedono intervalli nell'ordine delle decine di migliaia. Se pubblichi solo ciò che serve a una mappa in inglese, quelle etichette spariscono senza alcun errore che ne spieghi il motivo.
Cinque fogli di sprite, uno per ciascuna variante upstream, a 1x e 2x. Gli stili qui sotto prendono le icone da questi file. Se vuoi indicatori di centri abitati, scudi autostradali o punti di interesse in un tuo stile, fai puntare sprite alla variante che preferisci. I nomi delle icone presenti in un foglio sono elencati nel relativo light.json.
Cosa disegna la mappa e da dove proviene ogni elemento
Ogni layer corrisponde a uno dei nove layer sorgente di questo archivio, quindi nulla richiede un secondo archivio. Vale la pena segnalarne due, perché entrambi sembrano dati mancanti quando in realtà non lo sono:
| Layer | Proviene da | In questa build? |
|---|---|---|
| Geometrie, strade, edifici, acqua, suolo | earth, landcover, landuse, water, roads, buildings | sì |
| Nomi di luoghi, strade e corpi idrici | il campo name di quegli stessi layer | sì |
| Icone dei punti di interesse, centri abitati | pois.kind, places, più lo sprite | sì |
| Scudi stradali | roads.shield_text | no. La build attualmente servita non contiene questo campo |
| Numeri civici | buildings.kind=address e buildings.addr_housenumber | no, per lo stesso motivo |
Non esiste un archivio separato per gli indirizzi e non c'è alcun layer di indirizzi da scaricare. Il progetto upstream inserisce i punti indirizzo dentro il layer buildings, come elementi con kind=address e addr_housenumber. Quel campo è stato introdotto in una build di Protomaps successiva a quella servita ora, quindi il layer dei numeri civici è già pronto e si attiverà da solo con il prossimo aggiornamento mensile. Lo stesso vale per il testo degli scudi stradali: la build attuale contiene shield_text_length, che indica quanti caratteri ha il numero di strada ma non quali sono; uno scudo disegnato così sarebbe una sagoma vuota su ogni autostrada.
Scrivere un layer da zero e colorarlo
Un layer è fatto da un filtro, uno zoom, una forma e un colore. Due dettagli nei filtri traggono in inganno quasi tutti, ed entrambi falliscono senza dare errore:
kindè la categoria generale. Per le strade i valori sonohighway,major_road,minor_road,path,rail. I nomi citati nella documentazione upstream —motorway,primary,residential— sono invece valori dikind_detail; un layer che filtrakindcon quei valori non disegna nulla e non segnala alcun errore.- Un layer non viene disegnato sotto il proprio
minzoom. L'archivio si ferma a z15, quindi un layer con minzoom 16 non viene mai disegnato. La maggior parte degli elementi ha anche un proprio attributomin_zoom, ovvero il livello di zoom a partire dal quale i dati indicano di mostrarli, e gli stili forniti controllano anche quello.
Ecco quindi una strada, il suo nome e il comando per nascondere la linea:
{
"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 nome è un layer di tipo symbol che legge il campo name, con o senza alone di contorno. Un glyph mancante non produce errori, quindi il font richiesto deve essere uno dei due presenti su questo host:
{
"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
}
}
Un layer che non disegna nulla non è considerato un errore, e MapLibre non indica quale controllo è fallito. Se il tuo resta vuoto, verifica in quest'ordine: il nome del campo, il minzoom del layer, il font e infine il nome dell'icona confrontandolo con il light.json dello sprite.
Se hai uno stile in cui i glyph puntano a demotiles.maplibre.org, basta sostituire quell'hostname. Il percorso è identico di proposito.
"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",
Se uno stile richiede un font diverso dai due indicati sopra, il testo non verrà disegnato. Gli stili di Protomaps usano Noto Sans Medium per il grassetto; in questo set non c'è Noto Sans Bold, motivo per cui uno stile che lo richiede perde tutte le etichette senza errori.
Quale host è più veloce e cosa è stato scelto. Misurato invece che supposto: richieste alternate, una alla volta, su una sola connessione, primi tre campioni scartati, tempo al primo byte separato dal trasferimento. In quel test una CDN ha risposto da 10 a 45 ms prima per ciascuno di questi file, superando nettamente la soglia del 15%. Per questo motivo le librerie vengono caricate da jsDelivr.
I font e gli sprite sono invece serviti da qui, anche se la stessa misurazione indica che una CDN risponderebbe circa 10 ms prima. È una scelta progettuale, non di misura. Una mappa che dipende da un solo hostname non smette di funzionare quando cade il server di qualcun altro, e un server di glyph irraggiungibile lascia la mappa senza etichette. Se preferisci guadagnare quei 10 ms, fai puntare glyphs a protomaps.github.io/basemaps-assets/fonts: funziona con lo stesso percorso. Sono numeri rilevati da una singola città, e basta un comando per ripetere il confronto.
Cinque. Informazioni su Leaflet
L.tileLayer di Leaflet disegna immagini, mentre queste sono tile vettoriali. Una mappa Leaflet può comunque usarle mantenendo marker, popup e controlli. Entrambe le soluzioni sono descritte in Passare dalle tile di OpenStreetMap.
Sei. Leggere l'archivio con la libreria PMTiles
Nessuna mappa, solo dati. Utile per script, test e tutto ciò che non è un'immagine.
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);
Nota le due classi. FetchSource legge i byte via HTTP, mentre PMTiles è il lettore sovrastante che interpreta il formato. FileSource è l'equivalente di FetchSource per i file locali: se gli passi un URL come stringa non funziona.
L'archivio è un'istantanea, non un flusso in tempo reale. I dettagli sulle tempistiche sono in Quanto sono aggiornati i dati?.
Passare dalle tile di OpenStreetMap
Il blocco per la politica sull'uso delle tile, l'errore 403 e come risolvere. Nessuna chiave, nessuna quota.
Perché la mappa mostra un avviso di blocco
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
I server di tile hanno inviato quell'immagine al posto della mappa di proposito, in base alla politica sull'uso delle tile. Non è un guasto, e correggere l'URL non risolve il problema.
I primi tentativi più comuni:
- Cambiare
a/b/ccon l'host canonico non rimuove il blocco. L'URL canonico è obbligatorio, ma il blocco è legato all'applicazione o al Referer rilevato, non all'hostname. - Riprovare peggiora la situazione. Il volume di richieste è proprio uno dei motivi del blocco.
- Nessun trucco con gli header salva una pagina aperta da disco.
file://non invia alcunReferer. InviareX-Requested-Withal suo posto richiederebbe un preflight CORS per ogni tile, e i server di tile rifiutano anche quel preflight. Firefox e Chromium inviano header diversi, ma la regola vale per entrambi. - Funziona su
localhostma è bloccato sul sito in produzione? Dipende dall'origine. A essere bloccato è il Referer del tuo dominio, mentrelocalhostne invia uno diverso.
Uffici, scuole, VPN e reti mobili condividono lo stesso indirizzo IP, e i blocchi si applicano sia agli indirizzi sia alle applicazioni. Dal tuo lato non puoi sapere quale dei due casi ti riguarda.
Quindi smetti di interrogare quei server. Ci sono tre modi per sostituirli.
Mantenere Leaflet
protomaps-leaflet disegna tile vettoriali in Leaflet. Legge direttamente l'archivio e si aggiunge come un normale layer, così marker, popup, GeoJSON e controlli restano dove sono. Una copia testata con questo archivio si trova qui: https://tiles.jasontally.com/vendor/protomaps-leaflet.js.
Prima:
<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>
Dopo:
<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>
flavoraccettalight,dark,white,grayscaleoblack. Sono i temi interni della libreria, non gli stili mostrati più sopra, quindi i colori non saranno identici.- Le etichette sono testo su canvas con il font di sistema. Nessun URL per i glyph, nessun font che possa restituire 404.
- z15 è la tile più dettagliata. Oltre quel livello il layer ingrandisce i dati esistenti, e
maxDataZoom: 15è già il valore predefinito.
protomaps-leaflet è in modalità di sola manutenzione upstream: correzioni, niente nuove funzioni. Va bene per un'applicazione Leaflet già esistente. I nuovi progetti dovrebbero partire con MapLibre GL.
Mantenere Leaflet, disegnare con MapLibre
@maplibre/maplibre-gl-leaflet inserisce una mappa MapLibre nel riquadro delle tile di Leaflet. Usalo quando vuoi uno degli stili qui sopra, oppure rilievo e ombreggiatura, in un'applicazione che resta basata su 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>
- Registra prima il protocollo. Senza la riga
addProtocolla console mostraURL scheme "pmtiles" is not supportede la mappa resta vuota. - MapLibre disegna un livello di zoom sotto Leaflet. Le sue tile sono da 512 px contro i 256 di Leaflet, quindi un layer con
minzoom12 compare quando Leaflet raggiunge z13. - Leaflet continua a disegnare sopra. Il canvas si trova nel riquadro delle tile, quindi marker e sovrapposizioni si comportano come su un layer raster.
Passare a MapLibre GL
Una sola libreria, nessun adattatore, e gli stili qui sopra funzionano così come sono. La pagina completa è in Uno. Mettere una mappa nella pagina. La migrazione consiste soprattutto nell'invertire l'ordine delle coordinate:
| 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) | uno stile con sorgente vettoriale; vedi sopra |
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 }), poi addLayer |
L.control.scale() | new maplibregl.ScaleControl() |
map.invalidateSize() | map.resize() |
setView([lat, lng]) accetta prima la latitudine; ogni chiamata MapLibre accetta invece [lng, lat]. Se la mappa finisce nell'emisfero sbagliato il motivo è questo, non le tile. Le sorgenti e i layer aggiunti prima del caricamento dello stile vengono ignorati senza errori, quindi aggiungili dentro map.on("load", …).
“archive contains MVT vector tiles, but leafletRasterLayer is for displaying raster tiles”
Questo messaggio proviene da pmtiles.leafletRasterLayer, che disegna archivi PMTiles raster come immagini. Queste sono tile vettoriali, quindi vengono rifiutate. Nessuna opzione può cambiarlo e qui non c'è una copia raster. Usa una delle soluzioni sopra.
“download.openstreetmap.fr returned 403”
Quello è un mirror di estratti e replica, non un server di tile. Nell'ottobre 2026 ha bloccato diversi indirizzi in seguito a download abusivi, e gli operatori hanno comunicato che avrebbero ripristinato l'accesso caso per caso. Nulla qui ne ha bisogno: questo archivio deriva dalle release di Protomaps, quindi non devi assemblare file .osm.pbf per conto tuo. Se scarichi dati OSM, usa un estratto periodico anziché il flusso al minuto.
Stili
Stesse tile, colori diversi. Scegline uno, copialo, modificalo: è tuo.
| Stile | Aspetto | File |
|---|
Cosa c'è nelle tile
Letto direttamente dai metadati dell'archivio al caricamento della pagina. Questi sono i nomi dei layer che uno stile deve usare.
| Layer sorgente | Zoom | Campi |
|---|---|---|
| caricamento… | ||
Cosa non è questo servizio
Risposte chiare, per evitare sorprese più avanti.
- Non c'è alcuna garanzia sul livello di servizio. Questo è un servizio gratuito senza alcun contratto alle spalle. Può essere lento, può andare fuori linea, e l'URL e i dati possono cambiare o sparire senza preavviso. Se il tuo prodotto dipende da queste mappe, ospita una tua copia delle tile oppure acquista un piano con supporto da un fornitore.
- Non è mai aggiornato all'ora. La copia è un'istantanea dell'ultima versione di Protomaps, non un flusso in tempo reale. Le modifiche fatte oggi non sono presenti. I dettagli sulle tempistiche sono in Quanto sono aggiornati i dati?.
- Nessun download dell'intero file. Le richieste ricevono risposta in intervalli di al massimo 512 KiB, che è tutto ciò che un client PMTiles richiede. Chiedere l'intero archivio in un'unica richiesta restituisce
416, come previsto. - L'attribuzione è obbligatoria. Le tile derivano da OpenStreetMap. Mantieni la riga di attribuzione nello stile o sulla mappa.
Quanto sono aggiornati i dati?
Le tile sono una copia di Protomaps Basemap, ricostruita una volta al mese.
- Ricostruito una volta al mese. La build viene eseguita la prima domenica del mese, tra le 00:30 e le 07:00 UTC, l'unica fascia oraria in cui il progetto upstream non ha mai pubblicato nulla in tre anni di storico. Mensile e non settimanale, perché un nuovo archivio invalida le parti salvate nella cache della CDN e di ogni browser che le ha già scaricate, e ogni ricostruzione consuma tempo di calcolo.
- Nessuna operazione nella maggior parte dei mesi. La ricostruzione avviene solo quando il progetto upstream ha pubblicato una nuova versione.
- Quale build upstream è in uso adesso? Usa il pulsante Metadati dell'archivio in cima a questa pagina. Legge direttamente l'intestazione dell'archivio, quindi la risposta è sempre aggiornata e non rischia di restare obsoleta nella pagina.
- La ricostruzione non è istantanea. È composta da tredici ricostruzioni separate distribuite su circa sei ore e mezza, una alla volta. Per la maggior parte di quella finestra la copia è un misto di due build upstream. Una tile richiesta durante l'aggiornamento può provenire dalla build più recente anziché da quella indicata nell'intestazione, il che si manifesta come una tile del posto sbagliato e non come una mappa rotta.
- Una ricostruzione può saltare. È un processo senza garanzie. Se un passaggio fallisce, il resto della copia rimane sulla build upstream precedente. La mappa continua comunque a funzionare.