|
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', |
|
'#f46d43', |
|
'#fdae61', |
|
'#fee08b', |
|
'#d9ef8b', |
|
'#a6d96a', |
|
'#66bd63', |
|
'#1a9850' |
|
] |
|
|
|
def create_interactive_plots(self, df): |
|
"""Create enhanced interactive Plotly visualizations""" |
|
if df is None or df.empty: |
|
return go.Figure() |
|
|
|
fig = make_subplots( |
|
rows=4, cols=1, |
|
subplot_titles=( |
|
'<b>Temperature Pattern (°C)</b>', |
|
'<b>Humidity Pattern (%)</b>', |
|
'<b>Rainfall Pattern (mm/day)</b>', |
|
'<b>Vegetation & Suitability Indices</b>' |
|
), |
|
vertical_spacing=0.08, |
|
row_heights=[0.28, 0.24, 0.24, 0.24] |
|
) |
|
|
|
|
|
self.add_temperature_plot(fig, df) |
|
|
|
|
|
self.add_humidity_plot(fig, df) |
|
|
|
|
|
self.add_rainfall_plot(fig, df) |
|
|
|
|
|
self.add_combined_indices_plot(fig, df) |
|
|
|
|
|
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) |
|
) |
|
|
|
|
|
self.add_season_shading(fig, df) |
|
|
|
|
|
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""" |
|
|
|
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 |
|
) |
|
|
|
|
|
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 |
|
) |
|
|
|
|
|
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 |
|
) |
|
|
|
|
|
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 |
|
) |
|
|
|
|
|
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)', |
|
'Early': 'rgba(255,255,0,0.1)', |
|
'Late': 'rgba(255,165,0,0.1)', |
|
'Dry': 'rgba(255,0,0,0.1)' |
|
} |
|
|
|
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) |
|
|
|
|
|
plugins.MeasureControl(position='topright').add_to(m) |
|
plugins.Fullscreen().add_to(m) |
|
|
|
|
|
folium.Marker( |
|
[lat, lon], |
|
popup='Analysis Location', |
|
icon=folium.Icon(color='red', icon='info-sign') |
|
).add_to(m) |
|
|
|
|
|
ndvi_colormap = cm.LinearColormap( |
|
colors=self.ndvi_colors, |
|
vmin=-1, |
|
vmax=1, |
|
caption='Vegetation Index (NDVI)' |
|
) |
|
|
|
|
|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
minimap = plugins.MiniMap() |
|
m.add_child(minimap) |
|
|
|
|
|
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' |