Sur ChronoMEL, je balaie quatre-vingt-dix ans d'orthophotos de la métropole lilloise avec une frise temporelle. Une fois cette navigation en place, l'envie est venue tout de suite : poser des repères par-dessus les photos : bâtiments remarquables, châteaux, églises, halles industrielles. Sauf qu'un repère cartographique classique est hors du temps : il s'affiche pareil sur 1930 et sur 2025. Or un gymnase des années 1990 posé sur une prise de vue de 1930, c'est un anachronisme qui saute aux yeux. Il fallait donner une dimension temporelle à des données qui, à l'affichage, n'en ont pas.
L'idée : projeter la donnée dans le temps
Le déclic tient en une phrase : la donnée porte une date, l'affichage dépend de l'année courante. Chaque bâtiment connaît son année de construction ; la carte, elle, sait quel millésime est affiché. Croiser les deux, c'est décider, pour chaque objet, s'il a le droit d'exister à l'écran.
C'est un motif transférable bien au-delà des orthophotos. Dès qu'une donnée possède une date d'apparition (un parc éolien, une station de métro, un lotissement, un arbre planté), on peut la « projeter » sur un axe temporel et la faire vivre au rythme d'une frise. On ne cartographie plus seulement l'espace, mais l'espace à un instant donné.
La donnée : une date d'apparition… et un trou
Les bâtiments viennent de la BD TOPO (la base de données topographique de référence) de l'IGN (Institut national de l'information géographique et forestière), récupérée en WFS (Web Feature Service, le standard OGC de diffusion du vectoriel) sur la Géoplateforme (le versant IGN de l'open data géographique, à côté de data.gouv.fr). Chaque bâtiment y porte, entre autres, une année de construction. Certains châteaux remontent à 1402.
Mais la BD TOPO a un angle mort décisif pour mon usage : elle donne la date de construction, jamais celle de démolition. Je sais quand un objet apparaît, pas quand il disparaît. Cette absence commande tout le reste : je ne peux pas afficher un état « rasé depuis ». Il ne reste donc que deux états, et c'est une contrainte assumée, pas un raccourci :
// présent : le bâtiment existait déjà à l'année affichée ;
// projeté : pas encore construit à cette année-là.
export function etatAuMillesime(feature, annee) {
return annee >= feature.properties.annee ? 'present' : 'projete';
}
La leçon générale : avec une donnée temporelle, l'absence est une information autant que la présence. Ici, ce qu'on ne sait pas (la fin de vie) dessine directement ce que le produit peut promettre.
Styliser une absence
Vient la vraie question de design : que faire d'un bâtiment « pas encore construit » ? Deux mauvaises réponses. Le cacher : il surgirait d'un coup au fil de la frise, sans qu'on comprenne d'où il sort. Le montrer à l'identique : je retomberais sur l'anachronisme de départ. La bonne réponse est intermédiaire : le rendre présent mais visiblement fantôme, pour qu'on lise « ça viendra ici » sans le confondre avec l'existant :
export function styleForFeature(feature, annee) {
const { color } = usageMeta(feature.properties.categorie);
if (etatAuMillesime(feature, annee) === 'present') {
return { radius: 5, color, fillColor: color, fillOpacity: 0.85, weight: 1 };
}
// « projeté » : contour pointillé, intérieur vide, plus discret.
return { radius: 4, color, fillOpacity: 0, opacity: 0.5, dashArray: '2,2' };
}
Le vocabulaire visuel fait tout le travail : un disque plein et net pour ce qui existe, un cercle pointillé, translucide et vide pour ce qui n'est pas encore là. Même couleur d'usage (religieux, sportif, industriel, patrimoine) dans les deux cas, pour que l'œil suive un même objet d'un millésime à l'autre et le voie « se remplir » à l'année de sa construction.


Recalculer au fil de la frise
Projeter dans le temps, ce n'est pas styliser une fois : c'est restyler à chaque changement de millésime. Quand la frise temporelle passe de 1983 à 1994, toute la couche doit réévaluer l'état de chaque point. En pratique, on rejoue le style de la couche à chaque bascule d'année :
function onMillesimeChange(annee) {
batiLayer.setStyle((feature) => styleForFeature(feature, annee));
}
C'est peu coûteux (un millier de circleMarker, un recalcul trivial), mais ça partage le même impératif que la frise elle-même : traiter l'année affichée comme une source de vérité unique, et resynchroniser dessus tout ce qui en dépend.
Quand le millier devient cent mille
Rejouer setStyle sur toute la couche à chaque bascule d'année reste indolore sur un millier de points. Sur une couche dense (tout le bâti d'une métropole, et plus seulement ses bâtiments remarquables), deux limites apparaissent : le coût du rendu et le coût du restylage.
Pour le rendu, Leaflet dessine par défaut chaque circleMarker comme un élément SVG distinct. Basculer la carte en rendu canvas remplace ces milliers de nœuds DOM par une seule surface :
const map = L.map('map', { preferCanvas: true });
Pour le restylage, l'observation utile tient à la nature même de la donnée : un bâtiment ne change d'état qu'une seule fois, à son année de construction. Passer de 1983 à 1994, ce n'est donc pas réévaluer toute la couche, c'est ne toucher qu'aux objets dont l'année tombe dans l'intervalle parcouru :
function onMillesimeChange(precedent, annee) {
const [min, max] = precedent < annee ? [precedent, annee] : [annee, precedent];
batiLayer.eachLayer((layer) => {
const a = layer.feature.properties.annee;
if (a > min && a <= max) {
layer.setStyle(styleForFeature(layer.feature, annee));
}
});
}
Balayer la frise année par année ne restyle alors qu'une poignée d'objets à chaque pas, au lieu de la couche entière. Le saut direct de 1930 à 2025 reste le pire cas, et c'est très bien ainsi : c'est le seul moment où l'on accepte d'attendre. Le premier rendu, lui, garde son passage complet, puisqu'aucun état n'est encore connu.
Le piège : au clic, la carte a oublié l'année
Dernier obstacle, et le plus instructif. ChronoMEL est bâti sur leaflet-atlas, une carto pilotée par la config : le panneau de détail d'une entité est décrit par un detailBuilder, une fonction qui reçoit les properties de l'objet cliqué et en fait du HTML. Problème : ce builder ne reçoit que les propriétés de la feature, pas le millésime courant. Or je veux afficher « construit en 1968, déjà présent » ou « pas encore construit à l'année affichée », ce qui suppose de connaître… l'année affichée.
La config ne prévoyait pas ce croisement. Plutôt que de forker la librairie, je fais passer l'information par un canal latéral : un petit module qui mémorise le millésime courant, écrit par l'orchestrateur de la frise, lu par le detailBuilder.
let millesimeAffiche = null;
export const setMillesimeAffiche = (annee) => { millesimeAffiche = annee; };
export const getMillesimeAffiche = () => millesimeAffiche;
C'est exactement le genre de couture qu'impose une librairie déclarative quand un besoin sort de son cadre (le sujet de l'escape hatch onReady, décliné ici en état partagé). Rien d'élégant, mais franc et local : une variable de module plutôt qu'un fork.
Le même motif, avec une date de fin
La BD TOPO ne donne qu'une date d'apparition, ce qui limite ChronoMEL à deux états. Beaucoup de jeux datés en portent deux : une ligne de tramway a une année d'ouverture et, parfois, une année de fermeture ; une friche industrielle a une date de mise en service et une date d'arrêt. La fonction de croisement encaisse ce cas sans changer de forme, elle renvoie simplement un état de plus :
export function etatAuMillesime(feature, annee) {
const { debut, fin } = feature.properties;
if (annee < debut) return 'projete';
if (fin && annee >= fin) return 'disparu';
return 'present';
}
Ce qui change, ce n'est pas le code, c'est le vocabulaire visuel : il faut une troisième écriture, distincte du « projeté » sans lui ressembler. Le pointillé étant déjà pris pour ce qui n'existe pas encore, le « disparu » se lit bien en disque plein mais désaturé, ou en croix discrète, quelque chose qui dise « ça a existé » plutôt que « ça viendra ». La règle qui tient dans les deux cas : un état, une écriture, jamais deux états qui se ressemblent.
C'est là que se mesure la portée du motif. La partie logique tient en trois lignes et se transpose telle quelle ; le vrai travail de conception reste le choix des signes, et il se refait à chaque jeu de données.
Conclusion

Donner le temps à une carte ne demande pas un moteur spécialisé : une date par objet, une année de référence, une fonction qui croise les deux, et un vocabulaire visuel pour l'entre-deux. Le vrai travail est ailleurs, dans l'honnêteté vis-à-vis de la donnée (pas de date de démolition, donc pas d'état « disparu »), et dans le soin porté à ce qui n'existe pas encore plutôt qu'à ce qui existe. Une fois ce motif en place, il se rejoue à l'identique pour n'importe quel jeu daté : c'est moins une fonctionnalité de ChronoMEL qu'une manière de penser la donnée cartographique, au présent de chaque millésime.
Fin de cette série GEO, six articles depuis le vocabulaire de la donnée géo jusqu'ici, en passant par le choix de vendoriser plutôt que d'importer. Tout ce qu'elle décrit tient dans un seul projet, ChronoMEL, en ligne et consultable, et dans de l'open data que n'importe qui peut télécharger.