Application Android de comparaison multi-modèles météorologiques (AROME, ARPEGE, ICON, GFS, HRRR, ECMWF, UKMO, AIFS, GEM, MET Nordic, HARMONIE, ACCESS, GRAPES…) basée sur l’API Open-Meteo.
L’app se concentre sur les données brutes et l’incertitude : au lieu d’agréger silencieusement les modèles en une seule prévision, elle expose les désaccords entre modèles pour que l’utilisateur puisse juger lui-même du niveau de confiance à accorder à la prévision.
Depuis la v1.0, l’app apprend aussi le biais historique de chaque modèle sur chaque ville favorite — comparaison quotidienne des prévisions passées à ce qui a réellement été observé (ERA5). Les modèles qui surestiment ou sous-estiment systématiquement sont signalés d’une pastille discrète dans les tableaux, sans jamais modifier la donnée brute.
app/src/main/java/com/meteocompare/app/
├── di/ ← Modules Hilt (Network, Repository, Dispatchers)
├── core/
│ ├── locale/ ← Helper applyPersistedLocale — source unique pour app + widgets
│ └── network/ ← ApiResult, NetworkMonitor, error mapping
├── data/
│ ├── remote/ ← Interfaces Retrofit + DTOs + BatchedForecastSplitter
│ │ (+ HistoricalForecastApi pour le backfill de biais)
│ ├── mapper/ ← DTO ↔ domain
│ ├── local/ ← Room (ForecastCache, ClimateNormals, ForecastSample, ObservationSample)
│ ├── preferences/ ← DataStore
│ ├── repository/ ← Implémentations (dont le coalescing des fetches concurrents)
│ └── worker/ ← BiasRefreshWorker + Scheduler (WorkManager, fenêtre 24h)
├── domain/
│ ├── model/ ← Modèles métier (City, ForecastSeries, WeatherModel, ModelFamily,
│ │ Coverage, DayNormals, HourlyConfidenceBand, WeatherCondition,
│ │ BiasSample, ModelBias, BiasVariable, BiasSignificance…)
│ ├── repository/ ← Interfaces (dont BiasSampleRepository)
│ └── usecase/ ← ConfidenceCalculator, ComputeBiasUseCase, SnapshotForecastUseCase,
│ FetchBiasObservationsUseCase, BackfillHistoricalForecastUseCase,
│ weighting strategies
├── ui/
│ ├── citylist/ ← Liste des villes favorites (accueil)
│ ├── citydetail/ ← Détail d'une ville : cartes, chart, tableaux
│ │ └── confidence/ ← Écran "Pourquoi cette confiance ?"
│ ├── settings/ ← Paramètres (modèles avec tri, thème, langue)
│ ├── components/ ← Composables réutilisables (WeatherIcon, WindArrow, ShimmerBox…)
│ ├── accessibility/ ← Formatage des descriptions TalkBack
│ ├── theme/ ← Couleurs, typographie, tokens M3
│ └── navigation/ ← Routes et NavHost
└── widget/ ← MeteoWidget (Glance), config activity, splitter loader
Listés dans WeatherModel.kt avec leur résolution native (km), leur horizon, leur zone de couverture, et leur institution source (ModelFamily).
| Modèle | Résolution | Couverture | Horizon | Institution | Par défaut |
|---|---|---|---|---|---|
| AROME France HD | 1.5 km | France | 2 j | Météo-France | ✓ |
| AROME France | 2.5 km | France | 2 j | Météo-France | |
| ARPEGE Europe | 11 km | Europe | 4 j | Météo-France | ✓ |
| ARPEGE World | 25 km | Global | 4 j | Météo-France | |
| ICON-EU | 7 km | Europe | 5 j | DWD (Allemagne) | ✓ |
| ICON | 13 km | Global | 7 j | DWD | |
| ICON-D2 | 2 km | Europe centrale | 2 j | DWD | |
| GFS | 13 km | Global | 16 j | NOAA (USA) | ✓ |
| ECMWF | 25 km | Global | 10 j | ECMWF (UE) | ✓ |
| ECMWF AIFS | 25 km | Global (IA) | 10 j | ECMWF | ✓ |
| UKMO Global | 10 km | Global | 7 j | UK Met Office | ✓ |
| GEM Global | 15 km | Global | 10 j | ECCC (Canada) | |
| HRRR | 3 km | USA continental | 2 j | NOAA | |
| MET Nordic | 1 km | Scandinavie | 3 j | MET Norway | |
| HARMONIE | 5.5 km | Europe | 2 j | KNMI (Pays-Bas) | |
| BOM ACCESS | 12 km | Global | 10 j | Bureau of Meteorology (Australie) | |
| CMA GRAPES | 15 km | Global | 10 j | China Meteorological Administration |
Les modèles marqués “Par défaut” sont activés dès la première ouverture ; les autres sont activables dans les Settings, désormais triables par zone, par famille ou par finesse (résolution native).
Diversité éditoriale du catalogue :
Open-Meteo documente que “AROME France HD has the same model area, but at higher resolution with a smaller selection of weather variables” — la variable weather_code (code météo synthétique) n’est pas exposée pour AROME HD, contrairement à AROME (2.5 km) qui la fournit. Le compromis est assumé côté modèle : la résolution 1.5 km au prix d’un jeu de sorties réduit. Idem pour cloud_cover et precipitation_probability — non exposées.
Pour éviter des cellules vides dans le tableau Jour × Modèle, on infère la condition pour AROME HD depuis les précipitations et la température min :
Les badges “%” (couverture nuageuse / probabilité de pluie) sous les icônes ne s’affichent que pour les modèles qui exposent effectivement les variables — pas de fallback bidouillé côté client.
ConfidenceCalculator agrège les prédictions multi-modèles en un score 0-100 par variable pour chaque jour ET pour chaque heure.
Algorithme :
ModelWeightingStrategy. L’écart-type est converti en % de confiance via des seuils calibrés (tight/wide par variable) — plus larges pour la pluie qui est intrinsèquement plus divergente entre modèles.cloud_cover.1/√résolution — privilégie les modèles haute-résolution sans écraser ECMWF qui reste excellent malgré sa résolution.val daily = calculator.dayConfidence(forecast, LocalDate.now())
val hourlyTemp: List<HourlyConfidenceBand> = calculator.hourlyTemperatureConfidence(forecast)
val hourlyPrecip: List<HourlyConfidenceBand> = calculator.hourlyPrecipitationConfidence(forecast)
val hourlyWind: List<HourlyConfidenceBand> = calculator.hourlyWindConfidence(forecast)
val currentCondition: WeatherCondition? = calculator.currentWeatherCondition(forecast)
val currentCloudCover: Int? = calculator.currentCloudCover(forecast)
val matrix: List<DayConditionsRow> = calculator.dailyConditionsByModel(forecast)
DayConditionsRow.extrasByModel porte les métadonnées par cellule (probabilité de pluie max journalière, couverture nuageuse moyenne journalière) qui alimentent les badges “%” sous les icônes.
Bande de confiance multi-métriques : le composant ConfidenceBandSection encapsule un sélecteur segmenté à 3 états (Température / Précipitations / Vent) au-dessus d’un graphe unique. Les 3 séries de bandes sont pré-calculées dans le ViewModel — la transition entre métriques est instantanée. Chaque bande superpose l’overlay des normales climatiques 10 ans (traits pointillés colorés par métrique) chargées depuis l’API archive d’Open-Meteo et cachées 180 jours dans Room. Le graphique est zoomable au pincement sur l’axe temps (pinch à 2 doigts + pan) et réinitialisable au double-tap.
Les seuils sont calibrés à partir d’observations empiriques sur l’Europe. À ajuster quand on aura des données de skill verification.
Un clic sur le badge de confiance (en haut à droite de la carte “Aujourd’hui”) ouvre une explication détaillée qui compose l’edge éditorial de l’app :
L’app évalue en continu la précision de chaque modèle sur chaque ville favorite en croisant :
SnapshotForecastUseCase), plus un backfill historical-forecast au premier lancement qui récupère en un appel HTTP les 30 derniers jours de prévisions par modèle (évite d’attendre 14 jours avant de voir le premier chip)BiasRefreshWorker (WorkManager, fenêtre 24h + flex 6h) et stockées dans RoomPour chaque paire (modèle, variable), ComputeBiasUseCase calcule sur une fenêtre glissante 30 jours :
Trois états visuels dans le tableau prévisions, footprint vertical identique pour préserver l’alignement des noms de modèle entre colonnes :
| État | Rendu | Sémantique |
|---|---|---|
| Biais significatif | Chip coloré rouge/bleu + flèche + valeur signée | Le modèle sur/sous-estime — utile de le corriger mentalement |
| Calibré | Chip gris neutre + coche + petite valeur signée | Le modèle est fiable, aucune correction nécessaire |
| En attente | Pastille vide avec dash | < 14 jours de recouvrement, pas assez de données |
Clic sur un chip (les deux premiers états) ouvre une sheet avec sparkline 30 jours superposant la prévision et l’observation, une grille de stats et un texte contextuel adapté à l’état. Pour un modèle calibré, le texte de la sheet ne parle plus de “surestimation” (mensonger) mais confirme la calibration — cohérence sémantique de bout en bout.
Coalescing des fetches : le ForecastRepositoryImpl dédoublonne les requêtes HTTPS concurrentes pour la même (city, models, forecastDays) via un registre Deferred sur un SupervisorJob du repo. Quand CityList, CityDetail et le widget cold-start-refreshent Paris en parallèle, une seule requête part réellement.
Widget Glance redimensionnable en 4 tailles :
Les modes confidence rendent une heatmap horizontale colorée par la confiance sur 7 jours, avec sous chaque cellule la valeur agrégée et le jour de la semaine. C’est le rendu widget de la bande de confiance de l’écran détail.
Localisation widget : l’écran de config et le rendu du widget utilisent tous deux le helper applyPersistedLocale (dans core/locale/) pour respecter la préférence de langue de l’app, indépendamment de la locale système.
Depuis la refonte réseau, l’app fait 1 seule requête HTTPS pour récupérer les N modèles activés, au lieu de N requêtes parallèles. OpenMeteoApi.getForecastBatched demande ?models=arome_france_hd,arpege_europe,gfs_seamless,… et Open-Meteo répond avec les variables suffixées par la clé du modèle (temperature_2m_arome_france_hd, temperature_2m_arpege_europe, …).
Le BatchedForecastSplitter décompose la réponse en un ForecastResponseDto par modèle, transparent pour le reste de la chaîne (mapper et cache Room inchangés — chaque modèle a toujours sa propre ligne cache). Un log dédié Log.i("MeteoCompare/Net", "Batched fetch: N models…") permet de vérifier l’invariant en dev via adb logcat -s MeteoCompare/Net:I.
Gain observé : latence perçue divisée par ~2 sur 3G/4G lents, économie de handshakes TLS et de wakeups radio (batterie).
Aucune clé API n’est nécessaire — Open-Meteo est gratuit pour usage non commercial.
./gradlew testDebugUnitTest # tests JVM rapides
./gradlew connectedDebugAndroidTest # UI, navigation, Room et DataStore sur appareil
./gradlew lintDebug assembleDebug # analyse statique + compilation APK
La suite instrumentée utilise des repositories Hilt factices : aucune requête
Open-Meteo n’est effectuée pendant androidTest. Elle couvre les parcours de
navigation, les états Compose, l’accessibilité, la configuration widget, les
DAO Room en mémoire, DataStore et la locale persistée.
La stratégie complète, les règles de stabilité et les commandes Windows sont
documentées dans TESTING.md.
Toutes les zones interactives ont des contentDescription lisibles par TalkBack :
A11yFormatter qui résument les données clés. La bande de confiance annonce en plus son état de zoom (“Graphique zoomé, double-tap pour réinitialiser”) pour rester compréhensible quand l’utilisateur zoome sans voir l’écran.heading() pour permettre la navigation par titre.Le module ui/accessibility/A11yFormatter.kt centralise les chaînes pour garder une terminologie cohérente.
Le fichier PRIVACY.md à la racine est conforme aux exigences Play Store : zéro collecte de données, déclaration explicite des permissions, des services tiers (Open-Meteo) et du stockage local.
À héberger sur GitHub Pages ou un Gist public, puis fournir l’URL dans Play Console.
L’app est gratuite et open-source. Plusieurs options pour soutenir :
Aucun privilège n’est accordé aux donateurs — l’app et le code source restent identiques pour tous. Voir DONATIONS.md pour plus de détails.
Fait :
À venir :
Apache License 2.0 — vous pouvez utiliser, modifier et redistribuer le code librement, à condition de conserver la notice de copyright.
Les données météo sont fournies par Open-Meteo (également open-source, AGPL-3.0). Les modèles eux-mêmes sont produits par leurs organismes respectifs : Météo-France (AROME, ARPEGE), DWD (ICON, ICON-D2), NOAA (GFS, HRRR), ECMWF (IFS et AIFS), UK Met Office (UKMO), Environnement et Changement climatique Canada (GEM), MET Norway (MET Nordic), KNMI (HARMONIE), Bureau of Meteorology Australie (ACCESS), China Meteorological Administration (GRAPES).