data / visualizations.py
Tracy André
updated
27281c3
raw
history blame
15.1 kB
"""
Module de visualisation des données agricoles
"""
import plotly.express as px
import plotly.graph_objects as go
from config import RISK_COLORS, PLOT_CONFIG
class AgricultureVisualizer:
"""Classe responsable de la création des visualisations"""
def __init__(self, data=None, risk_analysis=None):
self.df = data
self.risk_analysis = risk_analysis
def set_data(self, data, risk_analysis=None):
"""Définit les données à visualiser"""
self.df = data
if risk_analysis is not None:
self.risk_analysis = risk_analysis
def _create_error_plot(self, title, message):
"""Crée un graphique d'erreur standardisé"""
try:
fig = px.scatter(title=title)
fig.add_annotation(
text=message,
xref="paper", yref="paper",
x=0.5, y=0.5,
showarrow=False,
font=dict(size=14, color="red")
)
fig.update_layout(
width=PLOT_CONFIG.get("width", 700),
height=PLOT_CONFIG.get("height", 400)
)
return fig
except Exception as e:
print(f"❌ Erreur lors de la création du graphique d'erreur: {e}")
# Retourner un graphique minimal en cas d'échec total
return go.Figure().add_annotation(text="Erreur critique", x=0.5, y=0.5)
def create_risk_visualization(self):
"""Crée la visualisation des risques avec gestion d'erreur robuste"""
try:
if self.risk_analysis is None or len(self.risk_analysis) == 0:
return self._create_error_plot(
"❌ Aucune donnée d'analyse des risques disponible",
"Veuillez charger et analyser les données d'abord"
)
try:
risk_df = self.risk_analysis.reset_index()
# Vérifier les colonnes requises
required_cols = ['surfparc', 'IFT_herbicide_approx', 'Risque_adventice', 'Nb_herbicides']
missing_cols = [col for col in required_cols if col not in risk_df.columns]
if missing_cols:
return self._create_error_plot(
f"❌ Colonnes manquantes: {missing_cols}",
"Les données ne contiennent pas toutes les colonnes nécessaires"
)
# Vérifier quelles colonnes sont disponibles pour hover_data
available_hover_cols = []
for col in ['nomparc', 'libelleusag']:
if col in risk_df.columns:
available_hover_cols.append(col)
# Nettoyer les données pour éviter les erreurs de plotting
risk_df = risk_df.dropna(subset=required_cols)
if len(risk_df) == 0:
return self._create_error_plot(
"❌ Aucune donnée valide après nettoyage",
"Toutes les données contiennent des valeurs manquantes"
)
fig = px.scatter(
risk_df,
x='surfparc',
y='IFT_herbicide_approx',
color='Risque_adventice',
size='Nb_herbicides',
hover_data=available_hover_cols if available_hover_cols else None,
color_discrete_map=RISK_COLORS,
title="🎯 Analyse du Risque Adventice par Parcelle",
labels={
'surfparc': 'Surface de la parcelle (ha)',
'IFT_herbicide_approx': 'IFT Herbicide (approximatif)',
'Risque_adventice': 'Niveau de risque'
}
)
fig.update_layout(
width=PLOT_CONFIG["width"],
height=PLOT_CONFIG["height"],
title_font_size=PLOT_CONFIG["title_font_size"]
)
return fig
except Exception as e:
print(f"❌ Erreur lors de la création du graphique de risque: {e}")
return self._create_error_plot(
"❌ Erreur lors de la création du graphique",
f"Erreur technique: {str(e)[:100]}..."
)
except Exception as e:
print(f"❌ Erreur critique dans create_risk_visualization: {e}")
return self._create_error_plot(
"❌ Erreur critique",
"Impossible de créer la visualisation des risques"
)
def create_culture_analysis(self):
"""Analyse par type de culture avec gestion d'erreur robuste"""
try:
if self.df is None or len(self.df) == 0:
return self._create_error_plot(
"❌ Aucune donnée disponible",
"Veuillez charger les données d'abord"
)
if 'libelleusag' not in self.df.columns:
return self._create_error_plot(
"❌ Colonne 'libelleusag' non disponible",
"Les données de culture ne sont pas disponibles"
)
try:
# Nettoyer les données de culture
culture_data = self.df['libelleusag'].dropna()
if len(culture_data) == 0:
return self._create_error_plot(
"❌ Aucune donnée de culture valide",
"Toutes les valeurs de culture sont manquantes"
)
culture_counts = culture_data.value_counts()
if len(culture_counts) == 0:
return self._create_error_plot(
"❌ Aucune culture détectée",
"Aucune donnée de culture trouvée après nettoyage"
)
fig = px.pie(
values=culture_counts.values,
names=culture_counts.index,
title="🌱 Répartition des Cultures"
)
fig.update_layout(width=700, height=500)
return fig
except Exception as e:
print(f"❌ Erreur lors de la création du graphique de culture: {e}")
return self._create_error_plot(
"❌ Erreur lors de la création du graphique",
f"Erreur technique: {str(e)[:100]}..."
)
except Exception as e:
print(f"❌ Erreur critique dans create_culture_analysis: {e}")
return self._create_error_plot(
"❌ Erreur critique",
"Impossible de créer l'analyse des cultures"
)
def create_risk_distribution(self):
"""Distribution des niveaux de risque avec gestion d'erreur robuste"""
try:
if self.risk_analysis is None or len(self.risk_analysis) == 0:
return self._create_error_plot(
"❌ Aucune analyse des risques disponible",
"Veuillez charger et analyser les données d'abord"
)
if 'Risque_adventice' not in self.risk_analysis.columns:
return self._create_error_plot(
"❌ Colonne 'Risque_adventice' manquante",
"L'analyse des risques est incomplète"
)
try:
# Nettoyer les données de risque
risk_data = self.risk_analysis['Risque_adventice'].dropna()
if len(risk_data) == 0:
return self._create_error_plot(
"❌ Aucune donnée de risque valide",
"Toutes les valeurs de risque sont manquantes"
)
risk_counts = risk_data.value_counts()
if len(risk_counts) == 0:
return self._create_error_plot(
"❌ Aucun niveau de risque détecté",
"Aucune donnée de risque trouvée après nettoyage"
)
fig = px.bar(
x=risk_counts.index,
y=risk_counts.values,
color=risk_counts.index,
color_discrete_map=RISK_COLORS,
title="📊 Distribution des Niveaux de Risque Adventice",
labels={'x': 'Niveau de risque', 'y': 'Nombre de parcelles'}
)
fig.update_layout(width=700, height=500, showlegend=False)
return fig
except Exception as e:
print(f"❌ Erreur lors de la création du graphique de distribution: {e}")
return self._create_error_plot(
"❌ Erreur lors de la création du graphique",
f"Erreur technique: {str(e)[:100]}..."
)
except Exception as e:
print(f"❌ Erreur critique dans create_risk_distribution: {e}")
return self._create_error_plot(
"❌ Erreur critique",
"Impossible de créer la distribution des risques"
)
def create_herbicide_timeline(self):
"""Crée un graphique de l'évolution temporelle des herbicides avec gestion d'erreur"""
try:
if self.df is None or len(self.df) == 0:
return self._create_error_plot(
"❌ Aucune donnée disponible",
"Veuillez charger les données d'abord"
)
required_cols = ['millesime', 'familleprod']
missing_cols = [col for col in required_cols if col not in self.df.columns]
if missing_cols:
return self._create_error_plot(
f"❌ Colonnes manquantes: {missing_cols}",
"Les données temporelles ne sont pas disponibles"
)
try:
# Filtrer les herbicides et grouper par année
herbicides_df = self.df[self.df['familleprod'] == 'Herbicides']
if len(herbicides_df) == 0:
return self._create_error_plot(
"❌ Aucune donnée d'herbicide disponible",
"Aucune intervention herbicide trouvée dans les données"
)
agg_dict = {'numparcell': 'nunique'}
if 'quantitetot' in herbicides_df.columns:
agg_dict['quantitetot'] = 'sum'
yearly_herbicides = herbicides_df.groupby('millesime').agg(agg_dict).reset_index()
if len(yearly_herbicides) == 0:
return self._create_error_plot(
"❌ Aucune donnée temporelle valide",
"Impossible de grouper les données par année"
)
y_col = 'quantitetot' if 'quantitetot' in yearly_herbicides.columns else 'numparcell'
y_label = 'Quantité totale d\'herbicides' if y_col == 'quantitetot' else 'Nombre de parcelles traitées'
fig = px.line(
yearly_herbicides,
x='millesime',
y=y_col,
title="📈 Évolution de l'Usage des Herbicides par Année",
labels={
'millesime': 'Année',
y_col: y_label
}
)
fig.update_layout(width=700, height=400)
return fig
except Exception as e:
print(f"❌ Erreur lors de la création du graphique temporel: {e}")
return self._create_error_plot(
"❌ Erreur lors de la création du graphique",
f"Erreur technique: {str(e)[:100]}..."
)
except Exception as e:
print(f"❌ Erreur critique dans create_herbicide_timeline: {e}")
return self._create_error_plot(
"❌ Erreur critique",
"Impossible de créer l'évolution temporelle"
)
def create_surface_analysis(self):
"""Analyse de la distribution des surfaces avec gestion d'erreur"""
try:
if self.df is None or len(self.df) == 0:
return self._create_error_plot(
"❌ Aucune donnée disponible",
"Veuillez charger les données d'abord"
)
if 'surfparc' not in self.df.columns:
return self._create_error_plot(
"❌ Colonne 'surfparc' manquante",
"Les données de surface ne sont pas disponibles"
)
try:
# Nettoyer les données de surface
surface_data = self.df['surfparc'].dropna()
if len(surface_data) == 0:
return self._create_error_plot(
"❌ Aucune donnée de surface valide",
"Toutes les valeurs de surface sont manquantes"
)
fig = px.histogram(
x=surface_data,
nbins=20,
title="📏 Distribution des Surfaces de Parcelles",
labels={
'x': 'Surface (ha)',
'count': 'Nombre de parcelles'
}
)
fig.update_layout(width=700, height=400)
return fig
except Exception as e:
print(f"❌ Erreur lors de la création du graphique de surface: {e}")
return self._create_error_plot(
"❌ Erreur lors de la création du graphique",
f"Erreur technique: {str(e)[:100]}..."
)
except Exception as e:
print(f"❌ Erreur critique dans create_surface_analysis: {e}")
return self._create_error_plot(
"❌ Erreur critique",
"Impossible de créer l'analyse des surfaces"
)