Turning a Whole Country Into Voxels: A Geodata Pipeline
How a voxel pipeline turned Switzerland's open geodata into a 1:1 Minecraft world, with lessons on tiles, level of detail, and streaming spatial data.

Somewhere in the Swiss government's open-data portal sits a heightmap of the Eiger north face sampled finely enough that you can feel the rock. One developer fed files like it into a voxel pipeline and shipped the result as a playable product: the whole of Switzerland as a 1:1 Minecraft world, where every block is a meter and the Matterhorn stands exactly as tall and as steep as the real thing. The download is 24 GB zipped, and the free sample region alone covers roughly 2,400 km². The engineering underneath — tiling, level of detail, lazy generation — is a masterclass in moving enormous spatial datasets without everything catching fire.
The Problem: A Country of Voxels Won't Fit in Memory
Do the math with me. Switzerland covers 41,285 km². At one block per square meter, that's 41.3 billion surface columns before you dig a single hole. Give each column a modest 100 blocks of depth — stone, dirt, lake bed — and you're at four trillion blocks. Even at one byte per block, that's four terabytes of raw geometry. Yet the shipped world compresses to a 24 GB download, which tells you something important: almost none of that geometry is stored as geometry. It's stored as a recipe.
The vertical axis breaks things too. Vanilla Minecraft caps the world at 384 blocks of build height. Switzerland runs from 193 m above sea level at Lago Maggiore to 4,634 m on the Dufourspitze — a 4,441 m span, more than eleven times the vanilla limit, before anyone has dug a mine. So the world needs Cubic Chunks, a mod that replaces Minecraft's column-based storage with 16×16×16 cubes stacked as high as needed. That mod only runs stably on Minecraft Java 1.12.2, which is why a project shipping today pins a game version from years ago.
Memory is the third wall. Building blocks from source data the first time a player visits an area is compute-hungry: the project asks for 16 GB of RAM minimum and is tested on machines like a Ryzen 7 7800X3D with 32 GB. Anyone who has fought streaming budgets in games or GIS will recognize the shape of this. It's the same triage you do when your GPU runs out of VRAM — you can't hold it all, so you get very good at deciding what to hold right now.
Failed Attempt One: Bake the Whole World Up Front
The obvious approach is one big offline job: read the geodata, emit Minecraft region files — the Anvil format stores the world in 512×512-block regions — zip the result, ship it. Denmark famously did something like this in 2014, when the Danish Geodata Agency released the entire country as a Minecraft map. It was a landmark open-data demo and it proved a country-sized map was possible. So why not that?
Because pre-baking has structural costs that scale with ambition. Every block of every region must be generated, written and compressed before anyone plays, which turns every data refresh into a full rebuild plus a re-ship of tens of gigabytes. Every player downloads mountains of terrain they'll never visit — and most of 41,000 km² will never be visited by anyone at all. Worst of all, a baked world freezes at its data snapshot the moment you publish. It's a write-once artifact. It can't improve unless you regenerate and redistribute everything, and your users re-download the planet.
Failed Attempt Two: One Heightmap at One Resolution
The second temptation is normalization: convert every input into a single global raster at a single resolution, so the pipeline has exactly one code path. Inside Switzerland you have swissALTI3D, a high-resolution terrain model. Beyond the border you have the Copernicus GLO-30 DEM — one elevation point every 30 meters. Resample everything down to 30 m for uniformity and the Eiger north face becomes a stepped sandpile. Resample everything up to the finest available resolution and your storage and processing explode, while the border regions still contain no real data — you've just upsampled your ignorance.
Heterogeneity hits semantics as hard as elevation. swissBUILDINGS3D covers Switzerland, so the generator can raise simplified voxel buildings on real footprints — but only inside the country. Cross the border near Geneva and there's no building layer at all, so the pipeline does the only sensible fallback: it plants trees where houses should be. Water needs its own source (swissBATHY3D for lake floors), as do glaciers, forests and vineyards. There is no single input file. In geodata, there never is.
The lesson generalizes well beyond Minecraft: resolution is per-tile metadata, not a global constant. A pipeline that assumes uniform inputs will either waste fidelity or invent it — and invented fidelity is worse than honest coarseness, because nobody can tell the difference from the inside.

What Actually Works: A Tiled Pipeline That Generates Lazily
The Swiss project works because it inverts the bake-everything instinct. You don't download a finished world; you download a world package — preprocessed geodata plus a custom generator — and the game constructs the blocks the first time you actually visit a place. A first visit to a new area takes a few minutes of churn; after that it's cached chunks like any other Minecraft world. Compute is paid per visit instead of up front, and you never generate terrain nobody stands on. The tile is the unit of everything: storage, caching, generation and failure.
Tiling is anchored to LV95, Switzerland's official survey coordinate system. Each tile records which datasets cover it and at what resolution, and converting survey coordinates to block coordinates is a pure affine transform. The project even publishes the formula so you can grab coordinates from the national map viewer and teleport to the matching spot in-game:
def lv95_to_block(east: float, north: float, height: float) -> tuple[int, int, int]:
"""Convert Swiss LV95 survey coordinates to Minecraft block coordinates."""
x = int(east - 2_480_000) # strip the LV95 easting offset
z = int(1_300_000 - north) # Minecraft's z axis grows southward
y = int(height) # one block = one meter above sea level
return x, y, z
# The summit of the Jungfrau, 4,158 m:
print(lv95_to_block(2_640_188, 1_154_072, 4158))
# -> (160188, 4158, 145928)
# x and z match the in-game teleport command exactly; the game just
# drops you a few meters higher so you land on the summit, not in it.
On top of the tiled grid, generation is a layer stack evaluated per column. Base height comes from swissALTI3D. Surface cover — water, forest, roads, railway lines, dams, avalanche barriers — comes from swissTLM3D. Lake floors come from swissBATHY3D, tree heights from swissSURFACE3D, glacier outlines from the GLAMOS inventories going back to 1850, and ice thickness from an ETH Zürich dataset, so the Aletsch glacier is genuinely carved ice rather than a white texture draped on rock. Meadows, scree and vineyards come from the federal habitat map; even Zürich's street trees come from the city's own tree cadastre. Around fifteen open datasets in total, all free to use — swisstopo opened its data, including for commercial use, in 2021.
A voxel world isn't a picture of terrain. It's a cache of millions of small decisions your pipeline made, one per square meter — and the quality of the world is exactly the quality of those decisions.
Level of Detail Is a Data Problem, Not a Rendering Trick
Minecraft's default render distance shows you a couple of hundred meters. The project's screenshots show the Aletsch glacier curving away for kilometers — that far view ("Fernsicht") is a separate, reduced-fidelity representation the game builds once at first launch. This is classic level of detail, but the important decision is where the LOD lives: it's computed from the source data at ingest time, not fudged by the renderer on the fly.
If you've built map-tile pyramids, you know the math. Each zoom level halves resolution on both axes, so a full pyramid costs 1 + 1/4 + 1/16 + … ≈ 4/3 of the base layer — a 33% storage premium for smooth behavior at every scale. Voxel terrain LOD is the same pyramid stood upright: far terrain is the coarse zoom level, near terrain is the fine one, and the real art is picking a key scheme that lets you fetch the right cell fast. We hit the same questions in the trade-offs between geohashes and proper spatial indexes — key choice decides your lookup cost everywhere downstream.
My favorite LOD decision here isn't visual at all. The free download is a 2,400 km² slice — the Jungfrau-Aletsch UNESCO region: Eiger, Mönch, Jungfrau and the biggest glacier in the Alps — running exactly the same pipeline as the paid whole country. That's a canary release as a product feature. It's 1.1 GB instead of 24, needs no signup, and if it runs on your machine, the full map will too. If you ship big data products, steal this outright: your free tier should be a representative slice of your hardest terrain, not a crippled demo of your easiest.

Streaming Spatial Tiles in the Browser
The same discipline shows up in the project's unassuming web page. Its overview map lets you drag a slider to compare the voxel world against the real aerial photograph — and the imagery doesn't come from the project's servers at all. Your browser fetches SWISSIMAGE tiles directly from swisstopo's own service, and only once you move the slider. Lazy, origin-direct, cache-friendly: the heavy bandwidth is served by public infrastructure that exists for exactly this purpose, and the project's own hosting bill stays near zero.
Tile addressing on the web is a solved problem, and it's worth knowing cold if you ever touch geospatial work:
import math
def latlon_to_tile(lat: float, lon: float, zoom: int) -> tuple[int, int]:
"""Standard slippy-map (XYZ) tile addressing used by OSM and most WMTS fronts."""
n = 2 ** zoom
x = int((lon + 180.0) / 360.0 * n)
lat_rad = math.radians(lat)
y = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n)
return x, y
# At zoom 15, any point on Earth lands in one of 1,073,741,824 tiles.
# Fetch the few that intersect the viewport. Skip the other billion.
Two details matter more than the formula. First, the request is deferred until user intent is unambiguous — the slider actually moves — which is both a bandwidth optimization and a politeness measure toward a free public service run on public money. Second, provenance travels with the tiles: the page credits SWISSIMAGE, swissBOUNDARIES3D and swissNAMES3D right at the map. When your product is a derived work of open data, attribution at the point of display isn't decoration. It's the license working as intended.
What to Steal From the Switzerland Pipeline
Counterpoints first, because practitioners raised good ones. Hand-built efforts like Build the Earth put thousands of volunteers to work modeling actual buildings, and no automated pipeline touches that fidelity — a voxelized swissBUILDINGS3D footprint is a crude approximation, and if architecture is what you care about, a terrain-only export is arguably the cleaner artifact. Fair. But coverage and fidelity trade off directly: automation gets you 41,285 km² today, hand-building gets you a gorgeous old town eventually. My position: for a data product, automated generation with published provenance wins, and curated hand-built landmarks can layer on top where they matter.
And provenance is the part everyone skips. This project ships a known-defects list: gaps near the borders where only the 30 m Copernicus grid reaches, patchy or flat spots, bridges and roads that are wrong or missing, QA done by sampling rather than meter by meter. That list does real work — it converts "your world is wrong here" bug reports into "yes, that dataset ends there" documentation. Every derived-data product should publish its provenance matrix and its known-wrong list. The ones that don't aren't more accurate; they're just less honest.
- Tile everything. The tile is your unit of storage, caching, generation and failure. Monoliths don't degrade gracefully.
- Never downsample at ingest. Keep native resolution per source and resample at render time.
- Generate lazily. Once the world is bigger than anyone's playtime, per-visit compute beats pre-baking.
- Build the LOD pyramid at ingest. Budget the ~33% overhead and buy smooth scaling at every distance.
- Ship a canary slice. A free, representative chunk of your hardest data beats any spec sheet.
- Publish provenance and known defects. They're part of the product, not an apology for it.
The full country sells for 15 CHF with every future version included — and only to Swiss residents, for legal reasons, which is an oddly charming constraint for a purely digital good. But the real product isn't the map. It's the demonstration that a single person with public data, a tiled pipeline and honest documentation can out-ship most teams' geospatial infrastructure. Your dataset probably isn't 41,285 km². Your excuses are smaller too.
