La Métropole Européenne de Lille publie en open data ses orthophotographies depuis 1930. Quatre-vingt-dix ans de territoire vus du ciel, millésime après millésime : l'étalement urbain, les friches qui deviennent des quartiers, une rivière que l'on recouvre puis que l'on rouvre. J'en ai fait ChronoMEL, une visionneuse temporelle qui permet de voyager dans le temps et de comparer deux millésimes. Voici comment passer de la donnée publique brute à un produit cartographique utilisable dans le navigateur, pièges compris.
Trouver la donnée
Tout commence sur data.gouv.fr, où l'organisation MEL publie ses jeux « Orthophotographie - MEL - AAAA », un par année de prise de vue. Les données sont sous Licence Ouverte (Etalab) : réutilisation libre, y compris commerciale, à la seule condition de créditer la source. D'où une contrainte à intégrer dès le départ : prévoir l'attribution dans la carte (« © Métropole Européenne de Lille - Licence Ouverte »).
Bonus pour explorer le catalogue sans quitter son terminal : le serveur MCP data.gouv permet de chercher les jeux et leurs ressources par requête, pratique pour recenser d'un coup tous les millésimes disponibles de 1930 à 2025.
Trouver, juger et consommer l'open data sur data.gouv.fr

Comprendre les flux géo
Une orthophotographie (une prise de vue aérienne redressée à l'aide de modèles d'élévation) n'arrive jamais « toute faite » côté front. Elle se consomme via des standards, et il faut savoir lequel sert à quoi :
- WMS (Web Map Service, servi par GeoServer, le serveur géospatial open source qui publie la donnée) : une image de carte calculée à la demande, c'est le fond historique, une couche par année ;
- GeoTIFF COG (Cloud Optimized GeoTIFF, un raster lisible par portions via HTTP) : le raster en téléchargement, hors navigateur ;
- OGC API Features (l'API REST/JSON de l'OGC pour le vectoriel) : le contour des communes en GeoJSON, une couche vectorielle de repère par-dessus les photos.
Pas besoin de tout maîtriser pour démarrer, mais comprendre la différence entre une couche raster (une image) et une couche vectorielle (des géométries) évite bien des confusions. Concrètement, les orthophotos sont du raster lourd, donc servies en WMS (le serveur dessine, le navigateur reçoit une image) ; le contour des communes est du vectoriel léger, donc récupéré en GeoJSON via OGC API Features et stylé côté client.
Pour qui veut creuser projections, WMS/WMTS, OGC API et GeoServer :
La donnée géo pour les devs web : formats et standards
La projection : vérifier la requête, pas la doc
C'est le moment où ChronoMEL a failli déraper. Leaflet (la bibliothèque JavaScript open source qui affiche la carte interactive côté navigateur) travaille en EPSG:3857 (Web Mercator), comme tous les fonds de carte web. Le service WMS, lui, n'annonce dans son GetCapabilities que EPSG:2154 (Lambert-93, la projection légale de la France métropolitaine) et CRS:84 (WGS84 en ordre longitude/latitude). Sur le papier : incompatibilité, il faut reprojeter, donc embarquer proj4leaflet et sa complexité.
| EPSG:2154 (Lambert-93) | CRS:84 (WGS84) | EPSG:3857 (Web Mercator) | |
|---|---|---|---|
Annoncé dans GetCapabilities |
oui | oui | absent |
| Attendu par Leaflet | non | non | oui |
Résultat d'un GetMap réel en EPSG:3857 |
/ | / | image valide |
Vérification avant de coder : un simple GetMap en EPSG:3857, sans passer par proj4leaflet, renvoie parfaitement une image. GeoServer reprojette à la volée, même quand le SRS (Spatial Reference System, synonyme de CRS dans le vocabulaire OGC) n'est pas listé dans ses capacités. Une heure de proj4leaflet économisée par un seul appel de test : avec les flux géo, la requête réelle en dit plus que le GetCapabilities.
Empiler dans Leaflet
Une fois la projection réglée, ajouter une couche WMS tient en quelques lignes :
L.tileLayer.wms('https://mel-geoserver.lillemetropole.fr/geoserver/Raster/wms', {
layers: '1930_ortho', format: 'image/png', transparent: true, version: '1.3.0',
}).setZIndex(250).addTo(map);
Le vrai sujet, dès qu'il y a plusieurs couches, c'est l'ordre d'empilement. On veut : le fond de carte tout en bas, l'orthophoto par-dessus, et le contour vectoriel des communes au sommet pour rester lisible. D'où une gestion explicite des z-index (ici 250 pour l'ortho), faute de quoi les communes disparaissent sous la photo. Leaflet organise les couches en panes, chacune avec son z-index ; raisonner en panes plutôt qu'en setZIndex individuels rend l'empilement prévisible quand le nombre de couches grandit.

Petite subtilité de cohérence visuelle : demander du JPEG opaque semblait raisonnable pour un fond de carte plein, mais GeoServer renvoie une tuile blanche dès qu'on sort de l'emprise couverte par un millésime, un carré qui masque le fond OpenStreetMap au lieu de laisser voir à travers. La bonne combinaison est format: 'image/png' avec transparent: true : hors couverture, la tuile devient réellement transparente et laisse apparaître le fond de carte, sans rien perdre en netteté sur les zones couvertes.
Toute cette plomberie (couches, ordre d'empilement, état dans l'URL) finit par se répéter d'une carte à l'autre. Je l'ai depuis extraite dans une carto pilotée par la config, pour ne plus la recoder à chaque projet.

L'UX « chrono » : voyager dans le temps
Afficher une année, c'est une carte. Naviguer dans quatre-vingt-dix ans, c'est un produit. Deux dispositifs portent l'expérience :
- une frise temporelle : un curseur que l'on balaie pour changer de millésime, avec une transition douce d'une année à l'autre ;
- un comparateur à rideau : on superpose deux années et on glisse un séparateur vertical pour révéler l'avant et l'après côte à côte.
La frise cache un piège de performance. Balayer le curseur, c'est traverser rapidement une dizaine de millésimes : si l'on déclenche un changement de couche WMS à chaque pixel parcouru, on inonde GeoServer de requêtes qui seront obsolètes avant même d'arriver.
On temporise donc le changement de millésime (n'agir qu'une fois le curseur stabilisé) et l'on garde en mémoire la couche précédente le temps que la nouvelle charge, pour éviter un clignotement blanc entre deux années. Une carte temporelle, c'est autant un travail sur le ressenti de fluidité que sur l'affichage lui-même.

Cette navigation temporelle devient vite un socle : tout ce qu'on superpose peut en hériter. J'y ai posé des repères vectoriels datés, un millier de bâtiments remarquables qui n'apparaissent qu'à partir de leur année de construction.
Les raccourcis clavier, sans marcher sur Leaflet
Une carte temporelle appelle la navigation au clavier : c'est de l'accessibilité et du confort power-user. Mais dans une carte, le clavier est déjà occupé : les flèches déplacent la carte (le pan). Y mapper « millésime précédent / suivant » créerait un conflit frustrant.
J'ai choisi « , » et « . », à la manière du pas-à-pas vidéo de YouTube : intuitif, et sans collision avec le pan. Deux garde-fous sont indispensables pour ne casser ni le navigateur, ni la saisie de texte :
document.addEventListener('keydown', (e) => {
if (e.ctrlKey || e.metaKey || e.altKey) return; // ne pas clobber le navigateur
const t = e.target;
if (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA'
|| t.tagName === 'SELECT' || t.isContentEditable) return; // ne pas voler la frappe
// … nos raccourcis : step(), cycleVariant()
});
Reste la découvrabilité : un raccourci que personne ne connaît n'existe pas. Beaucoup de librairies exposent une config shortcuts qui remplace la liste par défaut : on perdrait alors les raccourcis natifs de Leaflet. La parade : garder un écouteur clavier dédié, et injecter ses propres lignes dans l'overlay d'aide existant (la fenêtre « ? »), plutôt que de le réécrire.
On annonce aussi chaque touche via les title / aria-label des boutons correspondants. Avec ces deux garde-fous en place et les raccourcis documentés dans l'overlay d'aide, le clavier reste utilisable sans jamais marcher sur les pieds de Leaflet.
Dans ChronoMEL, appuyer sur « ? » affiche la liste complète des raccourcis.
Déployer
ChronoMEL est un site statique : une application Vite buildée, publiée sur GitHub Pages via GitHub Actions. Pas de serveur à maintenir : les flux géo (WMS, OGC API) sont consommés directement par le navigateur depuis GeoServer. C'est tout l'intérêt d'une carto côté client : l'hébergement se réduit à des fichiers statiques, et le coût d'exploitation est quasi nul. C'est ce qui permet à un side project d'être fait sérieusement sans devenir une charge d'exploitation.
Les orthophotos elles-mêmes n'ont rien à rafraîchir : servies en direct par le WMS de GeoServer, elles sont à jour par construction. Ce qui doit l'être, en revanche, ce sont les couches vectorielles qui les accompagnent (contour des communes, bâti remarquable daté, réseau ferré, hydrographie…), rechargées depuis leurs sources respectives. Un workflow planifié les retélécharge chaque mois et ouvre une pull request automatique s'il détecte un changement, sans jamais la fusionner seul. Une donnée géographique qui change silencieusement en production est plus difficile à repérer qu'une dépendance qui casse le build : la revue humaine reste le dernier filet avant que ces couches n'atteignent le site publié.
Conclusion

La MEL publie quatre-vingt-dix ans de son histoire visuelle en ouvert, mais passer de la donnée brute sur data.gouv.fr à une carte utilisable n'a rien eu d'automatique : il a fallu vérifier la projection réelle plutôt que se fier à la documentation déclarée, rendre l'empilement des couches prévisible en raisonnant par panes, et choisir un format transparent pour ne pas masquer le fond hors couverture. Restait à temporiser la frise pour ne pas inonder GeoServer de requêtes, et à ouvrir la navigation au clavier sans marcher sur les pieds de Leaflet. Aucun de ces pièges n'est propre à ChronoMEL : on les retrouve dans n'importe quelle carte temporelle bâtie sur des flux WMS et OGC API Features. La donnée existe déjà ; reste à prendre le temps de l'afficher, et de la garder à jour. Le résultat se parcourt en ligne sur chronomel.