Transmission #009: Vector Tiles and the MVT Decoder
With the chunked map and terrain lining up (Transmission #008), we turned to Protomaps vector tiles: same 3×3 chunks, but we wanted buildings, roads, and water from vector data so each chunk only shows the features that actually belong to that chunk’s piece of the world. For a while we either saw nothing or the same single building repeated on every chunk. Here’s what was going on and how it works now.
How it works now
The map is built in chunks. Each chunk has its own center (lat/long) and requests the vector tile that covers that area. One tile can span several of our grid cells, so after decoding we filter features: we keep only buildings, roads, and water whose geometry falls inside this chunk’s bounds. That way each chunk draws only what belongs to it, at the right position. Terrain comes from raster elevation data, and Y is real meters—height in the world is true elevation, so hills and valleys have the right scale. Together, that’s the full picture: chunked terrain + chunked vector data, all aligned.
What we ran into
We had a hand-rolled decoder for the vector tile format (MVT / Mapbox Vector Tiles). It turned out we were only getting one building’s geometry out of the whole tile; the rest were decoded as empty. So either the strict “only this chunk” filter removed that single building (and we saw nothing), or we relaxed the filter and that one building showed up on every chunk—same building, same relative spot, everywhere. The bug was in the decoder, not in the chunk logic or the filter.
What we’re using
Rather than keep debugging low-level protobuf parsing, we copied in the decoder from mapbox/vector-tile-cs (BSD-3-Clause). It lives under Assets/Scripts/MapboxVectorTile/—no NuGet, no extra setup. Our pipeline still expects the same layer/feature shape, so we just convert the Mapbox decoder’s output into that. If that path ever fails (e.g. unexpected data), we fall back to the old decoder. With the Mapbox logic in place, we get all the buildings (and roads, water) from each tile, and the strict per-chunk filter does its job: each chunk shows only its own features at the right X/Z.
We also switched to Raster elevation for terrain and made Y represent real meters. That’s a big part of why the terrain looks so much better—the world has the right proportions and no longer feels flat or squashed.
So: the entire map now generates correctly—chunked terrain, chunked vector data, one decoder we can trust, and elevation in real meters. It finally looks like the place.