py_atmo_data_wrapper/demos/demo_episode_functions.py
2025-07-14 17:56:57 +02:00

536 lines
No EOL
20 KiB
Python

#!/usr/bin/env python3
"""
Script de démonstration des fonctionnalités de la classe EpisodePollution
=====================================================================
Ce script illustre l'utilisation de toutes les méthodes disponibles
dans la classe EpisodePollution pour analyser les épisodes de pollution.
Fonctionnalités testées:
- Analyse des alertes de pollution
- Identification des polluants responsables
- Analyse des niveaux d'alerte (Information/Alerte)
- Gestion des géométries complexes
- Analyse des zones géographiques affectées
- Utilisation des méthodes héritées de la classe de base
"""
import json
from pathlib import Path
from atmo_wrapper import AtmoDataWrapper
from atmo_data_wrapper import EpisodePollution, AtmoDataCollection, Coordinates
from atmo_data_wrapper import CODE_POLLUANT
def demo_basic_properties():
"""Démonstration des propriétés de base d'un objet EpisodePollution"""
print("=" * 60)
print("DÉMONSTRATION DES PROPRIÉTÉS DE BASE")
print("=" * 60)
# Exemple d'épisode de pollution aux particules fines
sample_episode = {
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[
[6.0, 49.0], [6.5, 49.0], [6.5, 49.5], [6.0, 49.5], [6.0, 49.0]
]]]
},
"properties": {
"aasqa": "90",
"source": "ATMO Grand Est",
"date_maj": "2024-01-15T10:00:00",
"lib_zone": "Agglomération de Nancy",
"code_pol": "5",
"lib_pol": "PM10",
"code_zone": "54395",
"date_dif": "2024-01-15",
"date_ech": "2024-01-16",
"etat": "PROCEDURE D'INFORMATION ET DE RECOMMANDATION"
}
}
episode = EpisodePollution(sample_episode)
print(f"Zone affectée: {episode.lib_zone}")
print(f"Code polluant: {episode.code_pol}")
print(f"Polluant: {episode.lib_pol}")
print(f"Code polluant normalisé: {episode.get_polluant_code()}")
print(f"État: {episode.etat}")
print(f"AASQA: {episode.get_aasqa_name()}")
print(f"Source: {episode.get_source()}")
print(f"Date de diffusion: {episode.date_dif}")
print(f"Date d'échéance: {episode.date_ech}")
print(f"Date de mise à jour: {episode.date_maj}")
print(f"Géométrie complexe: {episode.is_geometry_complex()}")
if episode.has_coordinates():
print(f"Coordonnées: {episode.coordinates}")
else:
print("Pas de coordonnées ponctuelles (géométrie de zone)")
print(f"Représentation: {episode}")
print()
def demo_alert_analysis():
"""Démonstration de l'analyse des alertes"""
print("=" * 60)
print("ANALYSE DES ALERTES DE POLLUTION")
print("=" * 60)
# Exemples d'épisodes avec différents niveaux d'alerte
episodes_samples = [
{
"properties": {
"aasqa": "18", "lib_zone": "Île-de-France", "code_pol": "5", "lib_pol": "PM10",
"etat": "PROCEDURE D'INFORMATION ET DE RECOMMANDATION", "date_dif": "2024-01-15"
}
},
{
"properties": {
"aasqa": "18", "lib_zone": "Paris", "code_pol": "3", "lib_pol": "O3",
"etat": "PROCEDURE D'ALERTE", "date_dif": "2024-07-20"
}
},
{
"properties": {
"aasqa": "90", "lib_zone": "Strasbourg", "code_pol": "1", "lib_pol": "NO2",
"etat": "PAS DE DEPASSEMENT", "date_dif": "2024-01-15"
}
},
{
"properties": {
"aasqa": "13", "lib_zone": "Marseille", "code_pol": "6", "lib_pol": "PM2.5",
"etat": "PROCEDURE D'INFORMATION", "date_dif": "2024-03-10"
}
},
{
"properties": {
"aasqa": "84", "lib_zone": "Lyon", "code_pol": "3", "lib_pol": "O3",
"etat": "ALERTE NIVEAU 1", "date_dif": "2024-08-15"
}
}
]
print("Analyse des différents types d'alertes:")
print()
for i, episode_data in enumerate(episodes_samples, 1):
episode_data["type"] = "Feature"
episode = EpisodePollution(episode_data)
print(f"{i}. {episode.lib_zone} - {episode.lib_pol}")
print(f" État: {episode.etat}")
print(f" Alerte active: {'' if episode.is_alert_active() else ''}")
print(f" Niveau d'alerte: {episode.get_alert_level()}")
print(f" Code polluant: {episode.get_polluant_code()}")
print(f" Date: {episode.date_dif}")
print()
def demo_pollutant_analysis():
"""Démonstration de l'analyse par polluant"""
print("=" * 60)
print("ANALYSE PAR POLLUANT")
print("=" * 60)
# Exemples avec différents polluants
pollutants_samples = [
{"code_pol": "1", "lib_pol": "NO2", "zone": "Zone urbaine dense"},
{"code_pol": "2", "lib_pol": "SO2", "zone": "Zone industrielle"},
{"code_pol": "3", "lib_pol": "O3", "zone": "Zone péri-urbaine"},
{"code_pol": "5", "lib_pol": "PM10", "zone": "Centre-ville"},
{"code_pol": "6", "lib_pol": "PM2.5", "zone": "Zone trafic"}
]
print("Polluants détectés dans les épisodes:")
print()
for i, poll_data in enumerate(pollutants_samples, 1):
episode_data = {
"type": "Feature",
"properties": {
"aasqa": "99", "lib_zone": poll_data["zone"],
"code_pol": poll_data["code_pol"], "lib_pol": poll_data["lib_pol"],
"etat": "PROCEDURE D'INFORMATION", "date_dif": "2024-01-15"
}
}
episode = EpisodePollution(episode_data)
polluant_description = CODE_POLLUANT.get(episode.get_polluant_code(), "Description non disponible")
print(f"{i}. Polluant: {episode.lib_pol} (Code: {episode.code_pol})")
print(f" Code normalisé: {episode.get_polluant_code()}")
print(f" Zone: {episode.lib_zone}")
print(f" Description: {polluant_description}")
print()
def demo_geometry_analysis():
"""Démonstration de l'analyse des géométries"""
print("=" * 60)
print("ANALYSE DES GÉOMÉTRIES")
print("=" * 60)
# Épisode avec géométrie simple (Point)
episode_point = EpisodePollution({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [2.3522, 48.8566]
},
"properties": {
"aasqa": "18", "lib_zone": "Paris Centre", "code_pol": "3", "lib_pol": "O3",
"etat": "PROCEDURE D'INFORMATION", "date_dif": "2024-07-15"
}
})
# Épisode avec géométrie complexe (MultiPolygon)
episode_multipolygon = EpisodePollution({
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[[[2.0, 48.5], [2.5, 48.5], [2.5, 49.0], [2.0, 49.0], [2.0, 48.5]]],
[[[2.8, 48.7], [3.2, 48.7], [3.2, 49.1], [2.8, 49.1], [2.8, 48.7]]]
]
},
"properties": {
"aasqa": "18", "lib_zone": "Île-de-France", "code_pol": "5", "lib_pol": "PM10",
"etat": "PROCEDURE D'ALERTE", "date_dif": "2024-01-20"
}
})
# Épisode sans géométrie
episode_no_geom = EpisodePollution({
"type": "Feature",
"properties": {
"aasqa": "90", "lib_zone": "Grand Est", "code_pol": "1", "lib_pol": "NO2",
"etat": "PAS DE DEPASSEMENT", "date_dif": "2024-01-15"
}
})
episodes = [
("Point", episode_point),
("MultiPolygon", episode_multipolygon),
("Sans géométrie", episode_no_geom)
]
print("Types de géométries dans les épisodes:")
print()
for i, (type_geom, episode) in enumerate(episodes, 1):
print(f"{i}. {type_geom} - {episode.lib_zone}")
print(f" Type géométrie: {episode.geometry.get('type', 'Non défini')}")
print(f" Géométrie complexe: {'' if episode.is_geometry_complex() else ''}")
print(f" A des coordonnées: {'' if episode.has_coordinates() else ''}")
if episode.has_coordinates():
print(f" Coordonnées: {episode.coordinates}")
print(f" État: {episode.etat}")
print()
def demo_temporal_analysis():
"""Démonstration de l'analyse temporelle des épisodes"""
print("=" * 60)
print("ANALYSE TEMPORELLE DES ÉPISODES")
print("=" * 60)
# Simulation d'épisodes à différentes dates
temporal_episodes = [
{
"date_dif": "2024-01-15", "date_ech": "2024-01-16",
"lib_zone": "Lyon", "lib_pol": "PM10", "etat": "PROCEDURE D'INFORMATION",
"aasqa": "84", "code_pol": "5"
},
{
"date_dif": "2024-07-20", "date_ech": "2024-07-21",
"lib_zone": "Marseille", "lib_pol": "O3", "etat": "PROCEDURE D'ALERTE",
"aasqa": "13", "code_pol": "3"
},
{
"date_dif": "2024-12-05", "date_ech": "2024-12-07",
"lib_zone": "Strasbourg", "lib_pol": "NO2", "etat": "PROCEDURE D'INFORMATION",
"aasqa": "90", "code_pol": "1"
}
]
print("Épisodes chronologiques:")
print()
for i, episode_data in enumerate(temporal_episodes, 1):
episode_data["type"] = "Feature"
episode = EpisodePollution(episode_data)
# Calcul de la durée (simplifié)
try:
from datetime import datetime
date_debut = datetime.strptime(episode.date_dif, "%Y-%m-%d")
date_fin = datetime.strptime(episode.date_ech, "%Y-%m-%d")
duree = (date_fin - date_debut).days
except:
duree = "Non calculable"
print(f"{i}. {episode.lib_zone} - {episode.lib_pol}")
print(f" Période: du {episode.date_dif} au {episode.date_ech}")
print(f" Durée: {duree} jour(s)" if duree != "Non calculable" else f" Durée: {duree}")
print(f" État: {episode.etat}")
print(f" Niveau: {episode.get_alert_level()}")
print()
def demo_regional_analysis():
"""Démonstration de l'analyse régionale des épisodes"""
print("=" * 60)
print("ANALYSE RÉGIONALE DES ÉPISODES")
print("=" * 60)
# Simulation d'épisodes dans différentes régions
regional_episodes = [
{"aasqa": "18", "region": "Île-de-France", "lib_zone": "Paris", "lib_pol": "PM10"},
{"aasqa": "84", "region": "Auvergne-Rhône-Alpes", "lib_zone": "Lyon", "lib_pol": "O3"},
{"aasqa": "13", "region": "Provence-Alpes-Côte d'Azur", "lib_zone": "Marseille", "lib_pol": "PM2.5"},
{"aasqa": "90", "region": "Grand Est", "lib_zone": "Strasbourg", "lib_pol": "NO2"},
{"aasqa": "59", "region": "Hauts-de-France", "lib_zone": "Lille", "lib_pol": "O3"}
]
print("Épisodes par région AASQA:")
print()
for i, episode_data in enumerate(regional_episodes, 1):
episode_data.update({
"type": "Feature",
"code_pol": "5", "etat": "PROCEDURE D'INFORMATION", "date_dif": "2024-01-15"
})
episode = EpisodePollution(episode_data)
print(f"{i}. Région: {episode_data['region']}")
print(f" AASQA: {episode.get_aasqa_name()}")
print(f" Zone: {episode.lib_zone}")
print(f" Polluant: {episode.lib_pol}")
print(f" Code AASQA: {episode.aasqa}")
print()
def demo_inherited_methods():
"""Démonstration des méthodes héritées de la classe de base"""
print("=" * 60)
print("MÉTHODES HÉRITÉES DE LA CLASSE DE BASE")
print("=" * 60)
episode_data = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [4.8357, 45.7640]
},
"properties": {
"aasqa": "84",
"source": "ATMO Auvergne-Rhône-Alpes",
"date_maj": "2024-07-15T14:30:00",
"lib_zone": "Lyon Métropole",
"code_pol": "3",
"lib_pol": "O3",
"etat": "PROCEDURE D'ALERTE",
"date_dif": "2024-07-15",
"date_ech": "2024-07-16"
}
}
episode = EpisodePollution(episode_data)
print(f"Zone affectée: {episode.lib_zone}")
print(f"AASQA: {episode.get_aasqa_name()}")
print(f"Source: {episode.get_source()}")
print(f"A des coordonnées: {'' if episode.has_coordinates() else ''}")
if episode.has_coordinates():
print(f"Coordonnées: {episode.coordinates}")
print()
# Test des fonctions de couleur et emoji (niveau fictif pour démonstration)
print("Fonctions de couleur et emoji (exemple avec niveau 4 - Alerte):")
test_level = 4 # Niveau d'alerte
couleur_hex, couleur_rgb = episode.get_color_by_level(test_level)
emoji_round = episode.get_emoji_by_level(test_level, "round")
emoji_square = episode.get_emoji_by_level(test_level, "square")
print(f" - Couleur hex: {couleur_hex}")
print(f" - Couleur RGB: {couleur_rgb}")
print(f" - Emoji rond: {emoji_round}")
print(f" - Emoji carré: {emoji_square}")
print()
def demo_comparative_analysis():
"""Démonstration d'une analyse comparative entre épisodes"""
print("=" * 60)
print("ANALYSE COMPARATIVE DES ÉPISODES")
print("=" * 60)
episodes_data = [
{
"type": "Feature",
"properties": {
"aasqa": "18", "lib_zone": "Paris", "code_pol": "5", "lib_pol": "PM10",
"etat": "PROCEDURE D'INFORMATION ET DE RECOMMANDATION", "date_dif": "2024-01-15"
}
},
{
"type": "Feature",
"properties": {
"aasqa": "84", "lib_zone": "Lyon", "code_pol": "3", "lib_pol": "O3",
"etat": "PROCEDURE D'ALERTE", "date_dif": "2024-07-20"
}
},
{
"type": "Feature",
"properties": {
"aasqa": "13", "lib_zone": "Marseille", "code_pol": "6", "lib_pol": "PM2.5",
"etat": "ALERTE NIVEAU 1", "date_dif": "2024-03-10"
}
},
{
"type": "Feature",
"properties": {
"aasqa": "90", "lib_zone": "Strasbourg", "code_pol": "1", "lib_pol": "NO2",
"etat": "PAS DE DEPASSEMENT", "date_dif": "2024-05-05"
}
}
]
episodes = [EpisodePollution(ep) for ep in episodes_data]
print("Comparaison des épisodes de pollution:")
print("-" * 70)
print(f"{'Zone':<15} {'Polluant':<8} {'Niveau':<12} {'Alerte':<8} {'État'}")
print("-" * 70)
for episode in episodes:
alerte_status = "" if episode.is_alert_active() else ""
niveau = episode.get_alert_level()
print(f"{episode.lib_zone:<15} {episode.lib_pol:<8} {niveau:<12} {alerte_status:<8} {episode.etat}")
print()
# Statistiques
total_episodes = len(episodes)
alertes_actives = sum(1 for ep in episodes if ep.is_alert_active())
print(f"Statistiques:")
print(f" - Total d'épisodes: {total_episodes}")
print(f" - Alertes actives: {alertes_actives}")
print(f" - Pourcentage d'alertes: {(alertes_actives/total_episodes)*100:.1f}%")
# Répartition par polluant
polluants = {}
for episode in episodes:
polluant = episode.get_polluant_code()
polluants[polluant] = polluants.get(polluant, 0) + 1
print(f" - Répartition par polluant:")
for polluant, count in polluants.items():
print(f" * {polluant}: {count} épisode(s)")
print()
def demo_edge_cases():
"""Démonstration de la gestion des cas particuliers"""
print("=" * 60)
print("GESTION DES CAS PARTICULIERS")
print("=" * 60)
# Cas 1: Épisode sans état défini
print("1. Épisode sans état défini:")
episode_no_state = EpisodePollution({
"type": "Feature",
"properties": {
"aasqa": "99", "lib_zone": "Zone test", "code_pol": "5", "lib_pol": "PM10",
"etat": "", "date_dif": "2024-01-15"
}
})
print(f" État: '{episode_no_state.etat}'")
print(f" Alerte active: {'' if episode_no_state.is_alert_active() else ''}")
print(f" Niveau d'alerte: {episode_no_state.get_alert_level()}")
print()
# Cas 2: Code polluant non standard
print("2. Code polluant non référencé:")
episode_unknown_pollutant = EpisodePollution({
"type": "Feature",
"properties": {
"aasqa": "99", "lib_zone": "Zone test", "code_pol": "99", "lib_pol": "Polluant inconnu",
"etat": "PROCEDURE D'INFORMATION", "date_dif": "2024-01-15"
}
})
print(f" Code polluant original: {episode_unknown_pollutant.code_pol}")
print(f" Code polluant normalisé: {episode_unknown_pollutant.get_polluant_code()}")
print(f" Nom polluant: {episode_unknown_pollutant.lib_pol}")
print()
# Cas 3: Géométrie malformée
print("3. Géométrie non standard:")
episode_no_geometry = EpisodePollution({
"type": "Feature",
"geometry": None,
"properties": {
"aasqa": "99", "lib_zone": "Zone sans géométrie", "code_pol": "3", "lib_pol": "O3",
"etat": "PROCEDURE D'ALERTE", "date_dif": "2024-01-15"
}
})
print(f" Géométrie: {episode_no_geometry.geometry}")
print(f" Type géométrie: {episode_no_geometry.geometry.get('type', 'Non défini') if episode_no_geometry.geometry else 'None'}")
print(f" Géométrie complexe: {'' if episode_no_geometry.is_geometry_complex() else ''}")
print(f" A des coordonnées: {'' if episode_no_geometry.has_coordinates() else ''}")
print()
def main():
"""Fonction principale de démonstration"""
print("SCRIPT DE DÉMONSTRATION - CLASSE EPISODEPOLLUTION")
print("=" * 60)
print("Ce script teste toutes les fonctionnalités de la classe EpisodePollution")
print("pour l'analyse des épisodes de pollution atmosphérique.")
print()
try:
# Exécution de toutes les démonstrations
demo_basic_properties()
demo_alert_analysis()
demo_pollutant_analysis()
demo_geometry_analysis()
demo_temporal_analysis()
demo_regional_analysis()
demo_inherited_methods()
demo_comparative_analysis()
demo_edge_cases()
print("=" * 60)
print("RÉCAPITULATIF DES MÉTHODES TESTÉES")
print("=" * 60)
print("Méthodes spécifiques à EpisodePollution:")
print("✓ get_polluant_code()")
print("✓ is_alert_active()")
print("✓ get_alert_level()")
print("✓ is_geometry_complex()")
print()
print("Méthodes héritées de AtmoDataBase:")
print("✓ get_aasqa_name()")
print("✓ get_source()")
print("✓ has_coordinates()")
print("✓ get_emoji_by_level(level, style)")
print("✓ get_color_by_level(level)")
print()
print("Propriétés testées:")
print("✓ Codes et noms des polluants")
print("✓ États et niveaux d'alerte")
print("✓ Zones géographiques affectées")
print("✓ Géométries (Point, MultiPolygon)")
print("✓ Informations temporelles (dates)")
print("✓ Gestion des cas particuliers")
print()
print("✅ TOUTES LES FONCTIONNALITÉS ONT ÉTÉ TESTÉES AVEC SUCCÈS")
except Exception as e:
print(f"❌ ERREUR lors de l'exécution: {e}")
raise
if __name__ == "__main__":
main()