Application web React/TypeScript de visualisation de la qualite de l'air sur carte interactive (Leaflet), avec filtrage par polluant, sources et pas de temps.
OpenAirMap permet de :
- afficher des appareils de mesure sur une carte avec marqueurs thematiques
- croiser plusieurs sources de donnees (AtmoRef, AtmoMicro, NebuleAir, PurpleAir, SensorCommunity, etc.)
- consulter les details dans des panneaux lateraux
- utiliser un mode historique pour rejouer des periodes passees
- basculer de langue et adapter l'application selon le domaine (branding/config)
- React 19 + TypeScript
- Vite 7
- Leaflet / React-Leaflet
- Tailwind CSS
- Node.js :
>= 20.19.0(recommande) ou>= 22.12.0 - npm : version recente (npm 10+ recommande)
- Git
La contrainte Node est alignee avec Vite 7 (^20.19.0 || >=22.12.0).
git clone <url-du-repo>
cd OpenAirMap3
npm ci
npm run devApplication disponible sur http://localhost:5173.
Le projet fournit un gabarit /.env.inc.
Copiez-le vers .env puis adaptez les valeurs selon votre environnement.
cp .env.inc .envNotes :
- toutes les variables front doivent etre prefixees par
VITE_; VITE_MAINTENANCE_MODE=trueaffiche une page de maintenance et empeche le chargement de la carte ;VITE_TOOLTIP_MIN_ZOOMaccepte un nombre (ex:11) oufalsepour desactiver le seuil de zoom.
Le mode maintenance se pilote avec le feature flag VITE_MAINTENANCE_MODE.
Quand il est actif, OpenAirMap affiche uniquement une page de maintenance et ne monte pas la carte Leaflet ni les appels de donnees.
Valeurs acceptees :
- actif :
true,1,on,yes,enabled; - inactif :
false,0,off,no,disabled.
Exemple :
VITE_MAINTENANCE_MODE=trueEn developpement, redemarrez npm run dev apres modification du .env.
En production, relancez un build puis redeployez les fichiers dist/.
Le texte affiche sur la page se configure dans public/maintenance.json.
Ce fichier permet au mainteneur de changer le contenu sans modifier le code React :
{
"title": "Maintenance en cours",
"message": "La plateforme est temporairement indisponible pendant une opération de maintenance.",
"details": "Merci de réessayer un peu plus tard.",
"contactLabel": "Contacter l'équipe",
"atmoMicroQualifiedSensors": {
"enabled": true,
"message": "Suite a un probleme technique, les donnees des capteurs qualifies ne sont plus accessibles. AtmoSud met tout en oeuvre pour le resoudre."
}
}Champs disponibles :
title: titre principal de la page ;message: message explicatif principal ;details: texte court complementaire affiche sous le message ;contactLabel: libelle du bouton de contact ;atmoMicroQualifiedSensors.enabled: active/desactive le bandeau incident AtmoMicro ;atmoMicroQualifiedSensors.message: texte du bandeau incident AtmoMicro.
OpenAirMap gere un mode degradé pour AtmoMicro lorsque l'endpoint mesures/dernieres ne fournit pas de mesures exploitables, par exemple :
204 No Contentou corps vide (null) ;- reponse JSON vide (
[]) ; - erreur reseau ou HTTP sur cet endpoint uniquement.
Comportement applique :
- un etat d'incident
atmoMicroOutageest active dans le hookuseAirQualityData(pour le bandeau) ; - les capteurs listes dans
capteurs/sitespour le polluant demande restent affiches en marqueurs inactifs (gris, pas de valeur recente), positions issues des metadonnees sites ; - un bandeau d'information est affiche en haut de la carte si AtmoMicro est selectionnee ;
- le texte du bandeau provient de
public/maintenance.json(atmoMicroQualifiedSensors.message) ; - l'utilisateur peut fermer le bandeau via une croix (fermeture locale de session UI).
Fichiers concernes :
src/services/AtmoMicroService.ts(fallback mesures -> sites, signalisMeasuresUnavailableIncident) ;src/hooks/useAirQualityData.ts(propagation deatmoMicroOutage) ;src/App.tsx(rendu du bandeau, texte centre, bouton de fermeture) ;public/maintenance.json(configuration du message).
Ce fichier est servi comme un fichier statique. En production, le mainteneur peut donc modifier maintenance.json dans les fichiers deployes sans modifier le code React. Les champs absents ou vides utilisent automatiquement le message par defaut.
Procedure type :
- Activer
VITE_MAINTENANCE_MODE=truedans l'environnement de build ; - Builder et deployer l'application ;
- Modifier si besoin le fichier deploye
maintenance.jsonpour adapter le message ; - Desactiver la maintenance en repassant
VITE_MAINTENANCE_MODE=false, puis rebuilder et redeployer.
Note cache : maintenance.json est charge avec une strategie no-store cote navigateur pour faciliter les changements de message. Si un proxy, CDN ou Nginx applique un cache supplementaire, purgez ce cache ou configurez une duree courte pour ce fichier.
Le branding et certains liens institutionnels sont portes par src/config/domainConfig.ts.
Structure principale :
DOMAIN_CONFIG.defaultcontient la configuration par defaut (logo, favicon, centre/zoom de carte, titre, liens, organisation)getConfigForDomain(domain)applique la config associee au domaine courant- si le domaine n'est pas defini dans
DOMAIN_CONFIG, fallback automatique versDOMAIN_CONFIG.default.
Pour ajouter un nouveau domaine :
- Ajouter une entree dans
DOMAIN_CONFIGavec la cle du domaine (ex:web-prod-no2.xpr) - Renseigner
logo,logo2,favicon,mapCenter,mapZoom,title,links,organization - Verifier le rendu du header, du favicon et du centrage de carte.
Le fond de carte par defaut (Carte standard) utilise StadiaMaps.
Pre-requis operationnel :
- creer un compte StadiaMaps
- declarer dans StadiaMaps la liste des domaines autorises (whitelist), incluant le domaine de production.
Symptomes en cas de mauvaise configuration StadiaMaps :
- erreurs reseau sur les tuiles dans la console navigateur
- statut
401 Unauthorizedsi le domaine courant n'est pas autorise - fond de carte absent.
Une alternative est deja integree : Carte OSM (OpenStreetMap), en plus de Carte standard (StadiaMaps) et Satellite IGN.
Procedure de bascule (sans redeploiement) :
- Ouvrir le menu des couches de fond dans l'interface carte ;
- Selectionner
Carte OSM; - Verifier que les tuiles OSM s'affichent correctement.
Cas d'usage recommande :
- utiliser
Carte OSMcomme solution de continuite si StadiaMaps renvoie des401(ou indisponibilite temporaire).
npm run dev # serveur de dev
npm run build # build production
npm run preview # verification locale du build
npm run lint # verification ESLintsrc/
components/
controls/ # menus et controles UI
map/ # carte, marqueurs, couches
panels/ # side panels par source
charts/ # visualisations historiques
services/ # acces et normalisation des donnees
hooks/ # logique metier partagee
constants/ # polluants, sources, pas de temps
config/ # config domaine / feature flags
locales/ # traductions i18n
- La disponibilite des pas de temps dans l'UI est pilotee par
src/constants/sources.tsviasupportedTimeSteps. - Chaque service de donnees doit supporter effectivement les pas annonces (mapping, requetage API, transformation).
- La configuration UI et l'implementation service doivent rester alignees pour eviter les etats incoherents (bouton actif mais donnees vides, ou inversement).
- Les panneaux source-specifiques activent/desactivent les boutons selon la compatibilite de la source.
- Le panneau de comparaison applique une regle d'intersection : un pas de temps n'est activable que s'il est supporte par toutes les sources comparees.
- Un fallback automatique vers un pas valide prioritaire (
heure, puisquartHeure, puisinstantane) est applique si un pas courant devient invalide.
Pour ajouter (ou retirer) un pas de temps sur une source donnee :
- Mettre a jour
supportedTimeStepsdanssrc/constants/sources.ts. - Mettre a jour le service associe pour qu'il supporte reellement ce pas (mapping/config/requetes).
- Verifier les panneaux de source et de comparaison pour confirmer l'etat des boutons et le chargement des graphes.
Exemple concret : AtmoMicro n'expose pas encore jour cote API, donc ce pas est desactive tant que le service ne le supporte pas.
Ce projet se deploie comme une SPA statique :
- build Vite,
- publication des fichiers
dist/, - service par Nginx avec fallback SPA.
npm ci
npm run buildLe build genere dist/.
Exemple :
rsync -avz --delete dist/ user@server:/var/www/openairmap/Exemple de serveur Nginx (/etc/nginx/sites-available/openairmap.conf) :
server {
listen 80;
server_name openairmap.example.org;
root chemin-vers-dossier-build;
index index.html;
# Fallback SPA
location / {
try_files $uri $uri/ /index.html;
}
# Cache long pour assets versionnes
location ~* \.(js|mjs|css|png|jpg|jpeg|gif|svg|ico|webp|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}
}Activation (Debian/Ubuntu) :
sudo ln -s /etc/nginx/sites-available/openairmap.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx