Des articles approfondis sur les technologies qui façonnent l'avenir.

Un pays entier en voxels : pipeline de géodonnées

Comment une pipeline voxel a transformé les géodonnées ouvertes suisses en monde Minecraft 1:1, avec des leçons sur les tuiles, le LOD et le streaming spatial.

Vallée alpine de style voxel avec un glacier en blocs et des sommets cubiques en gradins.
Un bloc, un mètre : un pays entier reconstruit en voxels à partir de géodonnées gouvernementales ouvertes.

Quelque part sur le portail de données ouvertes du gouvernement suisse se trouve un modèle de hauteur de la face nord de l'Eiger, échantillonné assez finement pour qu'on sente presque la roche. Un développeur a alimenté une pipeline voxel avec des fichiers de ce type et en a fait un produit jouable : la Suisse entière sous forme de monde Minecraft 1:1, où chaque bloc représente un mètre et où le Cervin est exactement aussi haut et aussi escarpé que l'original. Le téléchargement pèse 24 Go compressé, et la zone de démonstration gratuite couvre à elle seule environ 2 400 km². L'ingénierie sous-jacente (découpage en tuiles, niveaux de détail, génération à la demande) est une leçon de maîtrise pour déplacer d'énormes jeux de données spatiales sans que tout parte en fumée.

Le problème : un pays en voxels ne tient pas en mémoire

Faisons le calcul ensemble. La Suisse couvre 41 285 km². À raison d'un bloc par mètre carré, cela fait 41,3 milliards de colonnes de surface avant même de creuser le moindre trou. Donnez à chaque colonne une profondeur modeste de 100 blocs (roche, terre, fond de lac) et vous atteignez quatre mille milliards de blocs. Même à un octet par bloc, cela représente quatre téraoctets de géométrie brute. Pourtant, le monde livré se compresse en un téléchargement de 24 Go, ce qui en dit long : presque rien de cette géométrie n'est réellement stocké comme géométrie. Elle est stockée sous forme de recette.

L'axe vertical pose aussi problème. Minecraft vanilla limite le monde à 384 blocs de hauteur de construction. La Suisse va de 193 m au-dessus du niveau de la mer au Lac Majeur jusqu'à 4 634 m à la Dufourspitze, soit un écart de 4 441 m, plus de onze fois la limite vanilla, avant même qu'on ait creusé la moindre mine. Il faut donc Cubic Chunks, un mod qui remplace le stockage en colonnes de Minecraft par des cubes de 16×16×16 empilés aussi haut que nécessaire. Ce mod ne tourne de façon stable que sur Minecraft Java 1.12.2, et c'est pourquoi un projet livré aujourd'hui fixe une version du jeu vieille de plusieurs années.

La mémoire est le troisième mur. Construire les blocs à partir des données sources la première fois qu'un joueur visite une zone demande beaucoup de calcul : le projet exige 16 Go de RAM minimum et est testé sur des machines comme un Ryzen 7 7800X3D avec 32 Go. Quiconque a déjà lutté avec des budgets de streaming dans les jeux ou le SIG reconnaîtra la forme du problème. C'est le même tri qu'on fait quand la VRAM de son GPU est saturée : on ne peut pas tout garder, alors on devient très doué pour décider ce qu'on garde tout de suite.

Première tentative ratée : tout cuire à l'avance

L'approche évidente est un gros traitement hors ligne : lire les géodonnées, produire des fichiers de région Minecraft (le format Anvil stocke le monde en régions de 512×512 blocs), compresser le résultat et le livrer. Le Danemark a fait quelque chose de similaire en 2014, quand l'agence danoise des données géographiques a publié tout le pays sous forme de carte Minecraft. C'était une démonstration phare de données ouvertes, et elle a prouvé qu'une carte à l'échelle d'un pays était possible. Alors pourquoi ne pas faire pareil ?

Parce que la cuisson préalable a des coûts structurels qui grandissent avec l'ambition. Chaque bloc de chaque région doit être généré, écrit et compressé avant que quiconque joue, ce qui transforme chaque mise à jour des données en reconstruction complète et en nouvelle livraison de plusieurs dizaines de gigaoctets. Chaque joueur télécharge des montagnes de terrain qu'il ne visitera jamais, et la plupart des 41 000 km² ne seront jamais visités par personne. Pire encore, un monde précuit se fige à l'instant de son instantané de données dès sa publication. C'est un artefact écrit une seule fois. Il ne peut pas s'améliorer sans régénérer et redistribuer l'ensemble, et vos utilisateurs retéléchargent la planète entière.

Première tentative ratée : une carte de hauteur à une seule résolution

La deuxième tentation est la normalisation : convertir chaque entrée en une seule trame globale à une résolution unique, pour que la pipeline n'ait qu'un seul chemin de code. En Suisse, vous disposez de swissALTI3D, un modèle de terrain haute résolution. Au-delà de la frontière, il y a le MNE Copernicus GLO-30, un point d'altitude tous les 30 mètres. Si vous ramenez tout à 30 m pour l'uniformité, la face nord de l'Eiger devient un tas de sable à gradins. Si vous ramenez tout à la résolution la plus fine disponible, le stockage et le traitement explosent, alors que les régions frontalières ne contiennent toujours aucune donnée réelle : vous venez simplement de suréchantillonner votre ignorance.

L'hétérogénéité touche la sémantique autant que l'altitude. swissBUILDINGS3D couvre la Suisse, donc le générateur peut élever des bâtiments voxel simplifiés sur des emprises réelles, mais uniquement à l'intérieur du pays. Passez la frontière près de Genève et il n'existe plus de couche de bâtiments : la pipeline applique donc le seul repli raisonnable, elle plante des arbres là où devraient se trouver des maisons. L'eau a besoin de sa propre source (swissBATHY3D pour les fonds de lac), tout comme les glaciers, les forêts et les vignobles. Il n'existe pas de fichier d'entrée unique. En géodonnées, il n'y en a jamais.

La leçon se généralise bien au-delà de Minecraft : la résolution est une métadonnée par tuile, pas une constante globale. Une pipeline qui suppose des entrées uniformes va soit gaspiller de la finesse, soit en inventer. Or une finesse inventée est pire qu'un grossissement honnête, parce que personne ne peut faire la différence de l'intérieur.

Une grille de tuiles cartographiques traversant un entonnoir et ressortant sous forme de paysage voxel assemblé.
L'inversion centrale : garder les données sous forme de tuiles et de recettes, et n'assembler les blocs qu'à l'endroit où quelqu'un va réellement.

Ce qui fonctionne vraiment : une pipeline en tuiles qui génère à la demande

Le projet suisse fonctionne parce qu'il inverse le réflexe de tout cuire. Vous ne téléchargez pas un monde fini ; vous téléchargez un paquet de monde, c'est-à-dire des géodonnées prétraitées plus un générateur sur mesure, et le jeu construit les blocs la première fois que vous visitez réellement un endroit. Une première visite dans une nouvelle zone prend quelques minutes de calcul ; ensuite, ce sont des chunks en cache, comme dans n'importe quel monde Minecraft. Le calcul se paie à chaque visite plutôt qu'à l'avance, et vous ne générez jamais de terrain que personne ne foule. La tuile est l'unité de tout : stockage, cache, génération et pannes.

Le découpage est ancré dans le LV95, le système de coordonnées officiel de la mensuration suisse. Chaque tuile enregistre les jeux de données qui la couvrent et à quelle résolution, et la conversion des coordonnées de mensuration en coordonnées de blocs est une simple transformation affine. Le projet publie même la formule, pour que vous puissiez récupérer des coordonnées dans le visualiseur cartographique national et vous téléporter au point correspondant en jeu :

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.

Par-dessus la grille en tuiles, la génération est une pile de couches évaluée colonne par colonne. L'altitude de base vient de swissALTI3D. La couverture du sol (eau, forêt, routes, voies ferrées, barrages, filets pare-avalanches) vient de swissTLM3D. Les fonds de lac viennent de swissBATHY3D, la hauteur des arbres de swissSURFACE3D, les contours des glaciers des inventaires GLAMOS remontant à 1850, et l'épaisseur de la glace d'un jeu de données de l'ETH Zurich. Ainsi, le glacier d'Aletsch est réellement de la glace sculptée et non une texture blanche plaquée sur la roche. Les prairies, les éboulis et les vignobles proviennent de la carte fédérale des habitats ; même les arbres de rue de Zurich viennent du cadastre arboricole de la ville. Au total, une quinzaine de jeux de données ouverts, tous utilisables librement : swisstopo a ouvert ses données, y compris pour un usage commercial, en 2021.

Un monde voxel n'est pas une image du terrain. C'est un cache de millions de petites décisions prises par votre pipeline, une par mètre carré, et la qualité du monde est exactement celle de ces décisions.

Le niveau de détail est un problème de données, pas une astuce de rendu

La distance d'affichage par défaut de Minecraft ne montre que quelques centaines de mètres. Les captures d'écran du projet montrent le glacier d'Aletsch s'éloigner en courbe sur des kilomètres. Cette vue lointaine (« Fernsicht ») est une représentation distincte, à fidélité réduite, que le jeu construit une seule fois au premier lancement. C'est du niveau de détail classique, mais la décision importante concerne l'emplacement du LOD : il est calculé à partir des données sources au moment de l'ingestion, et non bricolé par le moteur de rendu à la volée.

Si vous avez déjà construit des pyramides de tuiles cartographiques, vous connaissez le calcul. Chaque niveau de zoom divise la résolution par deux sur chaque axe, donc une pyramide complète coûte 1 + 1/4 + 1/16 + … ≈ 4/3 de la couche de base, soit une surcharge de stockage de 33 % pour un comportement fluide à toutes les échelles. Le LOD d'un terrain voxel est la même pyramide posée debout : le terrain lointain correspond au niveau de zoom grossier, le terrain proche au niveau fin, et tout l'art consiste à choisir un schéma de clés qui permet de récupérer rapidement la bonne cellule. Nous nous sommes heurtés aux mêmes questions avec les compromis entre geohashes et index spatiaux dignes de ce nom : le choix de la clé détermine le coût de recherche partout en aval.

Ma décision préférée sur le LOD n'a rien de visuel. Le téléchargement gratuit est une tranche de 2 400 km², la région UNESCO Jungfrau-Aletsch : l'Eiger, le Mönch, la Jungfrau et le plus grand glacier des Alpes. Elle tourne avec exactement la même pipeline que le pays entier payant. C'est un déploiement canari transformé en fonctionnalité produit. Il pèse 1,1 Go au lieu de 24, ne demande aucune inscription, et si ça tourne sur votre machine, la carte complète tournera aussi. Si vous livrez de gros produits de données, copiez cette idée sans scrupule : votre offre gratuite doit être une tranche représentative de votre terrain le plus difficile, pas une démo amputée de votre cas le plus facile.

Montagnes voxel passant de petits blocs fins au premier plan à d'immenses blocs grossiers dans la brume lointaine.
Le terrain lointain n'est que le niveau de zoom grossier de la même pyramide : le LOD est décidé à l'ingestion, pas par le moteur de rendu.

Diffuser des tuiles spatiales dans le navigateur

La même rigueur apparaît dans la page web discrète du projet. Sa carte d'ensemble propose un curseur pour comparer le monde voxel à la vraie photographie aérienne, et l'imagerie ne vient pas du tout des serveurs du projet. Votre navigateur récupère les tuiles SWISSIMAGE directement auprès du service de swisstopo, et seulement lorsque vous déplacez le curseur. Paresseux, direct depuis la source, compatible avec le cache : la forte bande passante est servie par une infrastructure publique qui existe précisément pour cela, et la facture d'hébergement du projet reste proche de zéro.

L'adressage des tuiles sur le web est un problème résolu, et il vaut la peine de le connaître par cœur si vous touchez un jour au géospatial :

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.

Deux détails comptent plus que la formule. D'abord, la requête est différée jusqu'à ce que l'intention de l'utilisateur soit claire : le curseur doit réellement bouger. C'est à la fois une optimisation de la bande passante et une marque de politesse envers un service public gratuit financé par de l'argent public. Ensuite, la provenance accompagne les tuiles : la page crédite SWISSIMAGE, swissBOUNDARIES3D et swissNAMES3D directement sur la carte. Quand votre produit est une œuvre dérivée de données ouvertes, l'attribution au moment de l'affichage n'est pas une décoration. C'est la licence qui fonctionne comme prévu.

Ce qu'il faut emprunter à la pipeline suisse

Commençons par les contre-arguments, car des praticiens en ont soulevé de bons. Les efforts manuels comme Build the Earth mobilisent des milliers de bénévoles pour modéliser de vrais bâtiments, et aucune pipeline automatisée n'atteint cette fidélité : l'emprise voxelisée de swissBUILDINGS3D reste une approximation grossière, et si l'architecture vous intéresse, un export du seul terrain est sans doute l'artefact le plus propre. D'accord. Mais couverture et fidélité sont directement en compromis : l'automatisation vous donne 41 285 km² aujourd'hui, la construction manuelle vous donnera une magnifique vieille ville, un jour. Ma position : pour un produit de données, la génération automatisée avec une provenance publiée l'emporte, et les repères manuels soignés peuvent se superposer là où ils comptent.

Et la provenance est la partie que tout le monde escamote. Ce projet publie une liste des défauts connus : des trous près des frontières où seule la grille Copernicus à 30 m atteint, des zones irrégulières ou plates, des ponts et des routes erronés ou absents, un contrôle qualité fait par échantillonnage et non mètre par mètre. Cette liste fait un vrai travail : elle transforme les rapports de bogues « votre monde est faux ici » en documentation du type « oui, ce jeu de données s'arrête là ». Tout produit de données dérivées devrait publier sa matrice de provenance et sa liste d'erreurs connues. Ceux qui ne le font pas ne sont pas plus précis : ils sont simplement moins honnêtes.

  • Découpez tout en tuiles. La tuile est votre unité de stockage, de cache, de génération et de panne. Les monolithes ne se dégradent pas en douceur.
  • Ne sous-échantillonnez jamais à l'ingestion. Conservez la résolution native de chaque source et rééchantillonnez au moment du rendu.
  • Générez à la demande. Dès que le monde dépasse le temps de jeu de quiconque, un calcul par visite bat la précuisson.
  • Construisez la pyramide de LOD à l'ingestion. Prévoyez la surcharge d'environ 33 % et achetez une mise à l'échelle fluide à toutes les distances.
  • Livrez une tranche canari. Un morceau gratuit et représentatif de vos données les plus difficiles vaut mieux que n'importe quelle fiche technique.
  • Publiez la provenance et les défauts connus. Ils font partie du produit, pas d'une excuse pour celui-ci.

Le pays entier se vend 15 CHF, avec toutes les versions futures incluses, et uniquement aux résidents suisses, pour des raisons juridiques, une contrainte étrangement charmante pour un bien purement numérique. Mais le vrai produit n'est pas la carte. C'est la démonstration qu'une seule personne, avec des données publiques, une pipeline en tuiles et une documentation honnête, peut livrer plus vite que l'infrastructure géospatiale de la plupart des équipes. Votre jeu de données ne fait probablement pas 41 285 km². Vos excuses non plus.