MVT zoom clamp ships transit into continental tiles the style never draws it in #28

Closed
opened 2026-08-01 21:14:33 +00:00 by art-bot · 1 comment
Collaborator

transit is z9–14. The generated zxy_map_transit uses an IF/ELSIF chain rather than a
bounds check, so a z5 request is served the z9 table — every tram and metro line in
Poland and Sweden, in a tile where a country is a few hundred pixels wide.

Measured: 41 kB of the 182 kB z5 tile 17/10 is transit, and 7.8 kB at z6. That is now
the second-largest layer in that tile, after cover and ahead of roads (11 kB).

The clamp is documented as intentional in CLAUDE.md ("MVT function zoom range behaviour")
and the argument is good for z > maxz — a client zooming past our best data should get
the best we have rather than a blank tile. It is much weaker for z < minz: the layer
has a minz precisely because it is meaningless up there, the style's own minzoom will not
draw it, and the bytes are pure waste on the largest-area tiles we serve.

Worth deciding as a general rule rather than special-casing transit — stations (z8–14)
and water_points (z9–14) have the same shape. Options roughly:

  • return an empty layer below minz (a per-layer flag, or the default for z < minz);
  • keep the clamp and rely on nothing requesting those zooms — which is not true today,
    since the browser does request them.

Related: docs/performance.md, "Where the bytes are after both".

`transit` is z9–14. The generated `zxy_map_transit` uses an IF/ELSIF chain rather than a bounds check, so **a z5 request is served the z9 table** — every tram and metro line in Poland and Sweden, in a tile where a country is a few hundred pixels wide. Measured: **41 kB of the 182 kB z5 tile 17/10** is `transit`, and 7.8 kB at z6. That is now the second-largest layer in that tile, after `cover` and ahead of `roads` (11 kB). The clamp is documented as intentional in CLAUDE.md ("MVT function zoom range behaviour") and the argument is good for **z > maxz** — a client zooming past our best data should get the best we have rather than a blank tile. It is much weaker for **z < minz**: the layer has a minz precisely because it is meaningless up there, the style's own `minzoom` will not draw it, and the bytes are pure waste on the largest-area tiles we serve. Worth deciding as a general rule rather than special-casing `transit` — `stations` (z8–14) and `water_points` (z9–14) have the same shape. Options roughly: - return an empty layer below `minz` (a per-layer flag, or the default for `z < minz`); - keep the clamp and rely on nothing requesting those zooms — which is not true today, since the browser does request them. Related: `docs/performance.md`, "Where the bytes are after both".
Owner

I think we discussed a general rule before. minz being configured can have two meanings: 1. don't generalize past this point, just serve whatever is in there in lower zooms. 2. stop the data from appearing after this z. And the conclusion was that if we want data to stop appearing at zoom level A, we should have an empty table at that zoom level.

maybe the problem is that minz is misnamed? Actually, what's a good reason (other than that I implemented it) to stop generalizing at a certain z? What problem does it solve? I don't think it does anything other than being a confusing configuration parameter. We're not saving time or disk space, the amount of data at low zoom levels should be microscopic.

Let's redesign this properly. We need a lowest zoom globally for the whole importer, configured in the top importer config. It should be set to 3 (feel free to argue about 2 as Google Maps does or 4-5 because we're limited to Europe), we will not serve anything smaller than that because there is just no point. On import every generalization cascade continues until it hits that number. If a layer wants to stop serving data at a certain z, it should just add a filter, unless you think that it's cleaner to make it a configuration parameter per layer called something like "stop_z". minz should never be used again because it was confusing.

I think we discussed a general rule before. minz being configured can have two meanings: 1. don't generalize past this point, just serve whatever is in there in lower zooms. 2. stop the data from appearing after this z. And the conclusion was that if we want data to stop appearing at zoom level A, we should have an empty table at that zoom level. maybe the problem is that minz is misnamed? Actually, what's a good reason (other than that I implemented it) to stop generalizing at a certain z? What problem does it solve? I don't think it does anything other than being a confusing configuration parameter. We're not saving time or disk space, the amount of data at low zoom levels should be microscopic. Let's redesign this properly. We need a lowest zoom globally for the whole importer, configured in the top importer config. It should be set to 3 (feel free to argue about 2 as Google Maps does or 4-5 because we're limited to Europe), we will not serve anything smaller than that because there is just no point. On import every generalization cascade continues until it hits that number. If a layer wants to stop serving data at a certain z, it should just add a filter, unless you think that it's cleaner to make it a configuration parameter per layer called something like "stop_z". minz should never be used again because it was confusing.
art closed this issue 2026-08-03 21:40:23 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
art/ismap#28
No description provided.