Filtering Layers Out of an Existing Tileset
tile-join -L layer_to_keep writes a new archive containing only the layers you name, reading the existing tiles rather than the source data. It is the fastest way to produce a public subset of an internal tileset, a lightweight mobile variant, or a licence-constrained regional build — minutes of I/O instead of a full rebuild.
When to Use This
Three situations recur. A public tileset derived from an internal one that carries extra layers or attributes. A mobile variant that drops the layers a small screen never shows. And a redistribution build where one source layer’s licence does not permit onward sharing.
The common thread is that the source data is unchanged and only the published selection differs. If the geometry or the tiling parameters need to change, this is the wrong tool and a rebuild is the right one.
Specification Detail
| Flag | Effect |
|---|---|
-L / --layer |
Keep only the named layer; repeatable |
-X / --exclude-all |
Drop every attribute from every kept layer |
-x / --exclude |
Drop one named attribute; repeatable |
-y / --include |
Keep only the named attributes; repeatable |
-Z / -z |
Narrow the zoom range of the output |
Layer selection and attribute selection are independent, so a build can keep two of six layers and three of forty attributes in one pass. Zoom narrowing simply omits tiles outside the range, which is where most of the size reduction comes from when the input has deep zooms.
Production Command
A public variant of an internal basemap, dropping two layers, several attributes and the deepest zoom:
tile-join \
--output dist/basemap-public.mbtiles \
--force \
--layer roads \
--layer water \
--layer landcover \
--exclude internal_id \
--exclude source_system \
--exclude ingest_batch \
--maximum-zoom 14 \
--name "Basemap (public) v43" \
--attribution "© OpenStreetMap contributors" \
dist/basemap-internal.mbtiles
# Confirm the narrowing actually happened
sqlite3 dist/basemap-public.mbtiles "SELECT value FROM metadata WHERE name='json';" \
| jq -r '.vector_layers[] | "\(.id): \(.fields | keys | join(", "))"'
# landcover: class
# roads: highway, name, oneway
# water: name
That last check is the one that matters for a public build. A -x that names an attribute which does not exist is silently ignored, so a typo in an exclusion leaves internal data in a published tileset with no error anywhere.
Verifying that last point directly:
tippecanoe-decode dist/basemap-public.mbtiles 12 2048 1362 \
| jq -r '[.features[].properties | keys[]] | unique | .[]' \
| grep -E 'internal_id|source_system|ingest_batch' \
&& { echo "excluded attribute still present"; exit 1; }
Interaction Effects
With deduplication. Narrowing often makes many tiles identical — dropping the only layer that varied across a region leaves a large run of matching tiles. The saving materialises properly on conversion to PMTiles, which deduplicates by content hash.
With the size budget. Narrowing only ever shrinks tiles, so a filtered variant of a passing tileset cannot fail the budget. It is the one operation in this section that needs no size gate.
With the style contract. A narrowed tileset is a different published interface. Its consumers must have their own style, and validating that style against the narrowed metadata is what stops a public map referencing a layer that only exists internally.
With versioning. Both variants should carry the same version prefix, since they derive from the same build. Publishing v43/basemap.pmtiles and v43/basemap-public.pmtiles keeps their provenance obvious.
Performance Impact
The operation is a full read and write of the retained tiles, so its cost is proportional to the output size rather than the input:
| Operation | Input | Output | Time |
|---|---|---|---|
| Drop 3 of 6 layers | 12 GB | 7.1 GB | 9 min |
| Drop 12 attributes | 12 GB | 8.8 GB | 12 min |
| Narrow z16 → z14 | 12 GB | 0.9 GB | 2 min |
| All three together | 12 GB | 0.6 GB | 2 min |
Zoom narrowing dominates because each level removed is roughly four times the tiles of the one below it. When a variant needs to be small, narrowing the range is the first lever, not the last.
Common Mistakes
Assuming -x warns on a missing attribute. It does not. Verify against the output metadata.
Filtering instead of rebuilding when the geometry differs. A narrowed tileset inherits the input’s simplification and zoom thresholds. A mobile variant that needs coarser geometry, not just fewer layers, needs a rebuild.
Publishing a subset without changing the attribution or name. The metadata is inherited, so a public archive can end up announcing itself as the internal one.
Keeping the derived archive as the source for the next variant. Each narrowing is lossy; chaining them compounds the loss and eventually nobody can reproduce the wide version. Always derive from the full build.
FAQ
Can I add a layer back later?
Not from the narrowed archive — the data is gone. Merge the missing layer’s own archive into it, or re-derive from the full build, which is why the full build should be retained.
Does filtering re-encode tiles?
Yes. Removing a layer or an attribute means writing a new protobuf message per tile. Geometry is unchanged, but the bytes are new, so content hashes differ from the input.
Is -X safe for a public build?
It removes every attribute, which makes the tileset geometry-only. That is genuinely useful for a base outline layer and useless for anything a style needs to filter or label on. Prefer an explicit -y list.
Can I filter a PMTiles archive?
Convert to MBTiles first. Retaining the MBTiles from the original build avoids that round trip entirely.
Related
- Layer Composition and tile-join — the parent topic and the composing direction.
- Building Multi-Layer Tilesets from Separate Sources — the build shape this narrows.
- Attribute Filtering Rules — doing the same filtering at build time, which is cheaper when you know in advance.
- Overzooming vs Generating Higher Max-Zoom Tiles — what a narrowed zoom range costs a reader.