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.

Three independent narrowings, and what each one savesLayer selection, attribute selection and zoom narrowing compared on what they remove, the typical size reduction, and whether the result can be reversed.WHAT EACH FLAG REMOVESremovestypical savingreversible?-L layerWhole layersProportional to thelayerNo-x attributeOne attributeeverywhere5-40% depending oncardinalityNo-XAll attributesOften over halfNo-Z / -zWhole zoom levels4x per level droppedNo
None of these is reversible from the output. The input archive is the only thing that can produce a wider variant later.

Production Command

A public variant of an internal basemap, dropping two layers, several attributes and the deepest zoom:

bash
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.

What to verify on a narrowed tilesetFive checks for a derived public tileset: layer set, attribute set, zoom range, metadata attribution and a decoded sample tile.BEFORE PUBLISHING A SUBSETThe layer set is exactly the intended listcompare against a manifest, not by eyeThe attribute set contains nothing internalread it from the output metadata, not the command lineEvery -x named an attribute that existeda typo is ignored without warningAttribution and name reflect the derived buildinheriting the internal name publishes itA decoded sample tile carries no excluded keythe only check that reads the actual bytes
The third and fifth rows are the ones that catch a silently ignored exclusion — the failure mode that leaks internal data.

Verifying that last point directly:

bash
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; }
A public variant derived from the internal buildOne internal build produces the full tileset; a filtering pass drops internal layers, attributes and the deepest zoom to produce the public variant, and both are published under the same version prefix.ONE BUILD, TWO TILESETSInternal build6 layersz0-z16tile-join filter-L, -x, -zreads tiles, not source dataPublic variant3 layersz0-z14Publish bothsame v43 prefix
Both artefacts carry the same version prefix, because they came from the same build — which is what makes their provenance traceable.

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.