inframaps
v0.0.1
Published
A self-contained slice of the world as vector tiles plus terrain
Readme
inframaps
The whole world as vector tiles plus terrain, from zoom 0 to 7, in one npm package that runs straight from a CDN: no server, no API key.
- Try it: the viewer and the examples, one capability each, run straight from the package on unpkg.
- Embed it: load it from jsDelivr, as below.
Use each CDN for its own job. jsDelivr serves HTML as plain text, so the pages only open on unpkg or your own server. unpkg answers a missing file without a CORS header, so a page on another site cannot tell a tile the package leaves out from a network failure, and those tiles fail instead of being drawn from their parent. Every path in the package is relative, so any copy of this folder on your own server does both jobs.
Use it from a CDN
<link href="https://cdn.jsdelivr.net/npm/[email protected]/renderer/[email protected]/maplibre.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/renderer/[email protected]/maplibre.js"></script>
<div id="map" style="height: 100vh"></div>
<script type="module">
const root = "https://cdn.jsdelivr.net/npm/[email protected]/";
const { registerInframapsProtocol } = await import(root + "protocol.mjs");
registerInframapsProtocol(maplibregl);
const style = await fetch(root + "themes/avenue.json").then((r) => r.json());
style.glyphs = root + style.glyphs.slice(2);
style.sprite = root + style.sprite.slice(2);
style.sources.__bmk_bm_src = {
type: "vector",
tiles: ["inframaps://vector/{z}/{x}/{y}"],
maxzoom: 7,
attribution: "© OpenStreetMap"
};
style.sources.__bmk_tr_src = {
type: "raster-dem",
tiles: [root + "terrain/{z}/{x}/{y}.webp"],
tileSize: 512,
maxzoom: 7,
encoding: "terrarium"
};
// The themes use one MapLibre 6 paint key that MapLibre 5 rejects.
for (const layer of style.layers) {
if (layer.paint?.["line-layer-opacity"] !== undefined) {
layer.paint["line-opacity"] = layer.paint["line-layer-opacity"];
delete layer.paint["line-layer-opacity"];
}
}
new maplibregl.Map({ container: "map", style, center: [10, 45], zoom: 3 });
</script>Pin the version in every URL. Without one, jsDelivr resolves each file on its own, and just after a release a page can mix files from two versions.
The protocol finds its tiles next to itself, so the same code works from a CDN,
from node_modules, or from a copy of this folder on your own server: change
root. Every theme in themes/index.json loads the same way; the two source
ids are listed there too.
The protocol
registerInframapsProtocol(maplibregl, options); // returns a function that unregisters| Option | Default | Meaning |
| --------- | ------------------------------ | ---------------------------------------- |
| base | vector/ beside protocol.mjs | where the tile tree lives |
| levels | 4 | ancestors to try when a tile is missing |
| scheme | "inframaps" | protocol name |
| onEvent | none | called once per tile, see below |
Some deep tiles over open sea and empty land are left out to keep the package small. When one is missing, the protocol cuts its nearest ancestor to the right quarter and scales it up, so the map shows less detail rather than a hole.
onEvent receives one of:
{ type: "decoded", z, x, y, stored, plain, ms }
{ type: "stretched", z, x, y, from }
{ type: "missing", z, x, y }
{ type: "failed", z, x, y, error }What is inside
| Path | Contents |
| ------------- | ------------------------------------------------- |
| vector/ | gzip Mapbox Vector Tiles, z0 to z7 |
| terrain/ | Terrarium-encoded WebP elevation tiles, z0 to z7 |
| themes/ | MapLibre styles; themes/index.json lists them |
| fonts/ | glyph ranges for six scripts |
| sprites/ | sprite sheets |
| renderer/ | a patched MapLibre GL JS 5.24.0 |
| examples/ | showcase pages; examples/index.html lists them |
| typefaces/ | the viewer panel's Noto Sans, woff2, OFL licence |
| processors/ | the Node programs that built the tile trees |
Rebuilding the tiles
The processors need Node 24 and sharp:
node processors/vector.mjs --input ./vector.pmtiles --output ./vector
node processors/terrain.mjs --input ./terrain.pmtiles --output ./terrainBoth require --input and --output and have no defaults.
