# part2_visualization.py 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 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""" fig = make_subplots( rows=4, cols=1, # Added one more row for NDVI subplot_titles=( 'Temperature (°C)', 'Humidity (%)', 'Rainfall (mm/day)', 'Vegetation Index (NDVI)' ), vertical_spacing=0.08, row_heights=[0.25, 0.25, 0.25, 0.25] ) # Add standard weather plots self.add_weather_plots(fig, df) # Add NDVI plot self.add_ndvi_plot(fig, df) # Update layout fig.update_layout( height=1000, # Increased height for additional plot showlegend=True, title={ 'text': "Enhanced Tobacco Growing 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) ) 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_weather_plots(self, fig, df): """Add weather-related plots""" # Temperature plot for data_type, color in [('historical', 'royalblue'), ('forecast_5day', 'firebrick'), ('forecast_extended', 'rgba(255, 165, 0, 0.5)')]: mask = df['type'] == data_type if any(mask): fig.add_trace( go.Scatter( x=df[mask]['date'], y=df[mask]['temperature'], name=f'{data_type.replace("_", " ").title()} Temperature', line=dict(color=color, width=2), mode='lines' ), row=1, col=1 ) # Add temperature rolling average fig.add_trace( go.Scatter( x=df['date'], y=df['temp_7day_avg'], name='7-day Temperature Average', line=dict(color='purple', width=1, dash='dot'), mode='lines' ), row=1, col=1 ) # Humidity plot for data_type, color in [('historical', 'royalblue'), ('forecast_5day', 'firebrick'), ('forecast_extended', 'rgba(255, 165, 0, 0.5)')]: mask = df['type'] == data_type if any(mask): fig.add_trace( go.Scatter( x=df[mask]['date'], y=df[mask]['humidity'], name=f'{data_type.replace("_", " ").title()} Humidity', line=dict(color=color, width=2), mode='lines' ), row=2, col=1 ) # Add humidity rolling average fig.add_trace( go.Scatter( x=df['date'], y=df['humidity_7day_avg'], name='7-day Humidity Average', line=dict(color='purple', width=1, dash='dot'), mode='lines' ), row=2, col=1 ) # Rainfall plot for data_type, color in [('historical', 'royalblue'), ('forecast_5day', 'firebrick'), ('forecast_extended', 'rgba(255, 165, 0, 0.5)')]: mask = df['type'] == data_type if any(mask): fig.add_trace( go.Bar( x=df[mask]['date'], y=df[mask]['rainfall'], name=f'{data_type.replace("_", " ").title()} Rainfall', marker_color=color ), row=3, col=1 ) def add_ndvi_plot(self, fig, df): """Add NDVI plot""" # Historical NDVI mask = df['type'] == 'historical' if any(mask): fig.add_trace( go.Scatter( x=df[mask]['date'], y=df[mask]['estimated_ndvi'], name='Historical NDVI', line=dict(color='green', width=2), mode='lines' ), row=4, col=1 ) # Forecast NDVI mask = df['type'].isin(['forecast_5day', 'forecast_extended']) if any(mask): fig.add_trace( go.Scatter( x=df[mask]['date'], y=df[mask]['estimated_ndvi'], name='Forecast NDVI', line=dict(color='orange', width=2, dash='dot'), mode='lines' ), row=4, col=1 ) # Add optimal NDVI range fig.add_hline(y=self.optimal_conditions['ndvi']['min'], line_dash="dash", line_color="green", annotation_text="Min Optimal NDVI", row=4, col=1) fig.add_hline(y=self.optimal_conditions['ndvi']['max'], line_dash="dash", line_color="green", annotation_text="Max Optimal NDVI", row=4, col=1) def create_enhanced_map(self, lat, lon, score, ndvi_value): """Create an interactive map with both weather and vegetation analysis""" m = folium.Map(location=[lat, lon], zoom_start=13) # Add base 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='NDVI Values' ) # Add NDVI-based circle ndvi_color = ndvi_colormap(ndvi_value) folium.Circle( radius=2000, location=[lat, lon], popup=f'NDVI: {ndvi_value:.2f}', color=ndvi_color, fill=True, fillOpacity=0.4 ).add_to(m) # Add score-based circles score_color = self.get_score_color(score) for radius in [500, 1000, 1500]: folium.Circle( radius=radius, location=[lat, lon], color=score_color, popup=f'Growing Score: {score:.2f}', fill=False, weight=2 ).add_to(m) # Add measurement tools plugins.MeasureControl(position='topright').add_to(m) # Add fullscreen option plugins.Fullscreen().add_to(m) # Add mini map minimap = plugins.MiniMap() m.add_child(minimap) # Add layer control folium.LayerControl().add_to(m) # Add colormap to map m.add_child(ndvi_colormap) return m._repr_html_() 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' 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