tillefeuille
v0.1.4
Published
Merge multiple MVT sources into a multi-layer vector tile using Web-standard APIs.
Maintainers
Readme
tillefeuille
Merge multiple Mapbox Vector Tile (MVT) sources into a single multi-layer vector tile.
tillefeuille is a small, pure TypeScript library for composing vector tiles at
request time. Give it one tile coordinate and a set of source definitions, and it
fetches each source tile and returns one merged MVT payload. Layer names are
preserved by default and can be customized from the source key.
flowchart LR
A[z/x/y.pbf] --> T((tillefeuille))
B[z/x/y.pbf] --> T
C[PMTiles] --> T
T --> O[1 MVT]Its primary purpose is to run behind a server-side tile endpoint, such as a
Cloudflare Worker. The endpoint can fetch from multiple tile sources and return
one MVT to the client, so clients use a single tile source while the composition
stays on the server. tillefeuille is designed to be embedded in that service;
it does not ship an HTTP server.
Features
- Merge multiple MVT sources into one MVT response.
- Optionally derive output layer names from each source key and original layer name.
- Exclude original layer names from individual sources before merging.
- Restrict individual sources to an inclusive tile zoom range.
- Fetch tiles from HTTP(S) URL templates using
{z},{x}, and{y}tokens. - Read PMTiles v3 archives over HTTP range requests.
- Accept gzip-compressed source tiles.
- Use Web-standard APIs such as
fetch,AbortSignal, andDecompressionStream.
Installation
pnpm add tillefeuilleUsage
import { mergeVectorTiles } from "tillefeuille";
const tile = await mergeVectorTiles({
z: 14,
x: 14553,
y: 6451,
sources: {
basemap: {
url: "https://example.com/basemap/{z}/{x}/{y}.mvt",
minzoom: 8,
maxzoom: 16,
layers: {
include: ["transportation", "water"],
exclude: ["building"]
}
},
roads: { url: "https://example.com/roads/{z}/{x}/{y}.mvt" },
poi: { url: "https://example.com/poi/{z}/{x}/{y}.mvt" },
admin: { url: "pmtiles://https://example.com/admin.pmtiles" }
}
});
return new Response(tile, {
headers: {
"content-type": "application/vnd.mapbox-vector-tile"
}
});The returned Uint8Array is an uncompressed MVT. Apply response compression in
the calling service when appropriate.
API
mergeVectorTiles(options)
import { mergeVectorTiles } from "tillefeuille";
const tile: Uint8Array = await mergeVectorTiles(options);Options:
| Option | Type | Description |
| --- | --- | --- |
| z | number | Tile zoom level. |
| x | number | Tile column. |
| y | number | Tile row. |
| sources | Record<string, VectorTileSource> | Source ids mapped to { url, minzoom?, maxzoom?, layers?: { include?, exclude? } } definitions. |
| fetch | typeof fetch | Optional custom fetch implementation. Useful for tests and runtimes with wrapped fetch behavior. |
| signal | AbortSignal | Optional abort signal passed to source requests. |
| skipMissing | boolean | Whether to ignore missing source tiles. Defaults to true. |
| getLayerName | (key: string, layerName: string) => string | Optional function that returns an output layer name from the source key and original name. |
| logger | { warn(warning): void } | Optional logger notified when a same-named layer is skipped because its MVT version or extent cannot be merged. |
Missing source tiles are HTTP 404, HTTP 204, or absent PMTiles entries. When
skipMissing is true, they are omitted from the merged tile. When
skipMissing is false, a missing source tile throws an error.
Source URLs
Each source has a url. HTTP URLs must start with http:// or https:// and
include all three tile coordinate tokens:
{
roads: { url: "https://tiles.example.com/roads/{z}/{x}/{y}.mvt" }
}PMTiles sources use the pmtiles:// prefix followed by the archive URL.
layers.include retains selected original layer names, and layers.exclude
removes them, before they are renamed and merged. These filters apply to both
HTTP and PMTiles sources. If both specify a name, layers.exclude wins:
{
admin: {
url: "pmtiles://https://tiles.example.com/admin.pmtiles",
layers: {
include: ["boundary", "building", "poi"],
exclude: ["building", "poi"]
}
}
}minzoom and maxzoom limit a source to that inclusive zoom range. A source
outside its range is not fetched.
For PMTiles sources, the archive server must support HTTP range requests.
Layer Naming
Layer names are preserved as they appear in the source tile by default. Set
getLayerName to customize them. For example, source-prefixed names can be
created with:
const tile = await mergeVectorTiles({
// ...
getLayerName: (key, layerName) => `${key}:${layerName}`
});For example, if the roads source contains a layer named transportation, the
merged tile contains that layer as:
roads:transportationLayers with the same output name are merged when they use the same MVT version
and coordinate extent; their features and attribute dictionaries are combined.
When those values differ, the first layer in sources insertion order is kept.
Set logger to receive an incompatible-layer warning with the skipped and
retained source keys, their MVT versions and extents, and the mismatched fields:
const tile = await mergeVectorTiles({
// ...
logger: console
});Runtime Requirements
tillefeuille targets modern JavaScript runtimes with Web-standard APIs:
fetchResponseHeadersAbortSignalDecompressionStreamfor gzip source tiles
It is intended for runtimes such as modern Node.js, Cloudflare Workers, and other Fetch-compatible environments.
Local Demo
pnpm run demoThe demo builds the library and starts a Vite app that uses MapLibre GL JS. It
registers a custom protocol that calls mergeVectorTiles, inspects the merged
tile, and renders layers by geometry type. It exists for debugging and
explanation only; production deployments should compose tiles in a server-side
endpoint such as a Cloudflare Worker.
The same demo is deployed to GitHub Pages when main is pushed:
https://kanahiro.github.io/tillefeuille/demo/
Development
pnpm install
pnpm run build
pnpm testBefore publishing, pnpm pack and pnpm publish run the prepack script,
which builds the package and runs the test suite.
Releases
Publishing is performed by GitHub Actions when a GitHub release is published.
Create an annotated tag named v<package-version> (for example, v0.1.1),
then create and publish the corresponding GitHub release.
The workflow verifies that the tag matches package.json, runs the prepack
checks, and publishes with npm provenance. Configure npm Trusted Publishing once
for the Kanahiro/tillefeuille repository and the
publish.yml workflow, allowing npm publish and setting its environment to
npm-publish. The workflow uses an OIDC identity; do not add an npm access
token as a GitHub Actions secret.
For an approval gate, create the npm-publish environment in the repository
settings and require a reviewer before deployment. The workflow targets that
environment before it can publish.
For an approval gate, create the npm-publish environment in the repository
settings and require a reviewer before deployment. The workflow targets that
environment before it can publish.
License
MIT
