import plotly.graph_objects as go
from plotly.subplots import make_subplots
import folium
from folium import plugins
import numpy as np
import branca.colormap as cm
from datetime import datetime
class VisualizationHandler:
def __init__(self, optimal_conditions):
self.optimal_conditions = optimal_conditions
self.ndvi_colors = [
'#d73027', # Very low vegetation
'#f46d43', # Low vegetation
'#fdae61', # Sparse vegetation
'#fee08b', # Moderate vegetation
'#d9ef8b', # Good vegetation
'#a6d96a', # High vegetation
'#66bd63', # Very high vegetation
'#1a9850' # Dense vegetation
]
def create_interactive_plots(self, df):
"""Create enhanced interactive Plotly visualizations"""
if df is None or df.empty:
return go.Figure() # Return empty figure if no data
fig = make_subplots(
rows=4, cols=1,
subplot_titles=(
'Temperature Pattern (°C)',
'Humidity Pattern (%)',
'Rainfall Pattern (mm/day)',
'Vegetation & Suitability Indices'
),
vertical_spacing=0.08,
row_heights=[0.28, 0.24, 0.24, 0.24]
)
# Add temperature visualization
self.add_temperature_plot(fig, df)
# Add humidity visualization
self.add_humidity_plot(fig, df)
# Add rainfall visualization
self.add_rainfall_plot(fig, df)
# Add vegetation and suitability visualization
self.add_combined_indices_plot(fig, df)
# Update layout
fig.update_layout(
height=1000,
showlegend=True,
title={
'text': "Agricultural Conditions Analysis",
'y':0.95,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top',
'font': dict(size=20)
},
paper_bgcolor='white',
plot_bgcolor='rgba(0,0,0,0.05)',
font=dict(size=12),
legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1
),
margin=dict(l=60, r=30, t=100, b=60)
)
# Add season shading
self.add_season_shading(fig, df)
# Update axes for all subplots
fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,0,0.1)')
fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,0,0.1)')
return fig
def add_temperature_plot(self, fig, df):
"""Add temperature visualization with range"""
# Temperature range area
if 'temp_max' in df.columns and 'temp_min' in df.columns:
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['temp_max'],
name='Max Temperature',
line=dict(color='rgba(255,0,0,0.0)'),
showlegend=False
),
row=1, col=1
)
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['temp_min'],
name='Temperature Range',
fill='tonexty',
fillcolor='rgba(255,0,0,0.1)',
line=dict(color='rgba(255,0,0,0.0)')
),
row=1, col=1
)
# Main temperature line
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['temperature'],
name='Temperature',
line=dict(color='red', width=2),
mode='lines'
),
row=1, col=1
)
# Add rolling average
if 'temp_7day_avg' in df.columns:
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['temp_7day_avg'],
name='7-Day Average',
line=dict(color='darkred', width=1, dash='dot'),
mode='lines'
),
row=1, col=1
)
# Add optimal range
for limit_type, value in self.optimal_conditions['temperature'].items():
fig.add_hline(
y=value,
line_dash="dash",
line_color="green",
annotation_text=f"Optimal {limit_type}",
row=1, col=1
)
def add_humidity_plot(self, fig, df):
"""Add humidity visualization"""
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['humidity'],
name='Humidity',
line=dict(color='blue', width=2),
mode='lines'
),
row=2, col=1
)
if 'humidity_7day_avg' in df.columns:
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['humidity_7day_avg'],
name='7-Day Average',
line=dict(color='darkblue', width=1, dash='dot'),
mode='lines'
),
row=2, col=1
)
# Add optimal range
for limit_type, value in self.optimal_conditions['humidity'].items():
fig.add_hline(
y=value,
line_dash="dash",
line_color="green",
annotation_text=f"Optimal {limit_type}",
row=2, col=1
)
def add_rainfall_plot(self, fig, df):
"""Add rainfall visualization"""
fig.add_trace(
go.Bar(
x=df['date'],
y=df['rainfall'],
name='Daily Rainfall',
marker_color='lightblue',
opacity=0.6
),
row=3, col=1
)
if 'rainfall_7day_avg' in df.columns:
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['rainfall_7day_avg'],
name='7-Day Average',
line=dict(color='blue', width=2),
mode='lines'
),
row=3, col=1
)
def add_combined_indices_plot(self, fig, df):
"""Add vegetation and suitability indices visualization"""
if 'estimated_ndvi' in df.columns:
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['estimated_ndvi'],
name='Vegetation Index',
line=dict(color='green', width=2),
mode='lines'
),
row=4, col=1
)
if 'daily_suitability' in df.columns:
fig.add_trace(
go.Scatter(
x=df['date'],
y=df['daily_suitability'],
name='Growing Suitability',
line=dict(color='purple', width=2),
mode='lines'
),
row=4, col=1
)
def add_season_shading(self, fig, df):
"""Add season shading to all plots"""
if 'season' in df.columns:
seasons = df['season'].unique()
season_colors = {
'Main': 'rgba(0,255,0,0.1)', # Green
'Early': 'rgba(255,255,0,0.1)', # Yellow
'Late': 'rgba(255,165,0,0.1)', # Orange
'Dry': 'rgba(255,0,0,0.1)' # Red
}
for season in seasons:
season_data = df[df['season'] == season]
if not season_data.empty:
for row in range(1, 5):
fig.add_vrect(
x0=season_data['date'].iloc[0],
x1=season_data['date'].iloc[-1],
fillcolor=season_colors.get(season, 'rgba(128,128,128,0.1)'),
layer="below",
line_width=0,
annotation_text=season if row == 1 else None,
annotation_position="top left",
row=row, col=1
)
def create_enhanced_map(self, lat, lon, score, ndvi_value):
"""Create an interactive map with analysis overlays"""
m = folium.Map(location=[lat, lon], zoom_start=13)
# Add measurement tools
plugins.MeasureControl(position='topright').add_to(m)
plugins.Fullscreen().add_to(m)
# Add location marker
folium.Marker(
[lat, lon],
popup='Analysis Location',
icon=folium.Icon(color='red', icon='info-sign')
).add_to(m)
# Create NDVI colormap
ndvi_colormap = cm.LinearColormap(
colors=self.ndvi_colors,
vmin=-1,
vmax=1,
caption='Vegetation Index (NDVI)'
)
# Add NDVI circle
folium.Circle(
radius=2000,
location=[lat, lon],
popup=f'NDVI: {ndvi_value:.2f}',
color=ndvi_colormap(ndvi_value),
fill=True,
fillOpacity=0.4
).add_to(m)
# Add suitability circles
score_color = self.get_score_color(score)
for radius in [500, 1000, 1500]:
folium.Circle(
radius=radius,
location=[lat, lon],
popup=f'Suitability Score: {score:.2f}',
color=score_color,
fill=False,
weight=2
).add_to(m)
# Add mini map
minimap = plugins.MiniMap()
m.add_child(minimap)
# Add layer control
folium.LayerControl().add_to(m)
m.add_child(ndvi_colormap)
return m._repr_html_()
def create_gauge_chart(self, score):
"""Create an enhanced gauge chart for the overall score"""
fig = go.Figure(go.Indicator(
mode="gauge+number+delta",
value=score,
domain={'x': [0, 1], 'y': [0, 1]},
title={
'text': "Growing Conditions Score",
'font': {'size': 24}
},
delta={
'reference': 0.8,
'increasing': {'color': "green"},
'decreasing': {'color': "red"}
},
gauge={
'axis': {'range': [None, 1], 'tickwidth': 1, 'tickcolor': "darkblue"},
'bar': {'color': "darkblue"},
'bgcolor': "white",
'borderwidth': 2,
'bordercolor': "gray",
'steps': [
{'range': [0, 0.4], 'color': 'rgba(255, 0, 0, 0.6)'},
{'range': [0.4, 0.6], 'color': 'rgba(255, 255, 0, 0.6)'},
{'range': [0.6, 0.8], 'color': 'rgba(144, 238, 144, 0.6)'},
{'range': [0.8, 1], 'color': 'rgba(0, 128, 0, 0.6)'}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 0.8
}
}
))
fig.update_layout(
height=300,
margin=dict(l=20, r=20, t=60, b=20),
paper_bgcolor="white",
font={'color': "darkblue", 'family': "Arial"}
)
return fig
def get_score_color(self, score):
"""Get color based on score"""
if score >= 0.8:
return 'green'
elif score >= 0.6:
return 'yellow'
elif score >= 0.4:
return 'orange'
return 'red'