import streamlit as st
import tensorflow as tf
import joblib
import pandas as pd
import numpy as np
import os
import matplotlib.pyplot as plt
import pickle
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
# Dark theme configuration
st.set_page_config(
page_title="AuraClima - AI Climate Intelligence",
page_icon="π",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for dark theme and styling
st.markdown("""
""", unsafe_allow_html=True)
@st.cache_resource
def load_all():
base = os.path.dirname(__file__)
models_dir = os.path.join(base, "models")
data_dir = os.path.join(base, "data")
# Load models
model1 = tf.keras.models.load_model(os.path.join(models_dir, "model1.keras"))
model2 = tf.keras.models.load_model(os.path.join(models_dir, "model2.keras"))
model3 = tf.keras.models.load_model(os.path.join(models_dir, "model3.keras"))
# Load scalers
scaler1 = joblib.load(os.path.join(models_dir, "scaler1.save"))
scalerX2 = joblib.load(os.path.join(models_dir, "scalerX2.save"))
scalerY2 = joblib.load(os.path.join(models_dir, "scalerY2.save"))
scaler3 = joblib.load(os.path.join(models_dir, "scaler3.save"))
# Load feature columns list for model2
with open(os.path.join(models_dir, "feature_cols2.list"), "rb") as f:
feature_cols2 = pickle.load(f)
# Load CSV data if present
df_agri = None
agri_path = os.path.join(data_dir, "Agrofood_co2_emission.csv")
if os.path.exists(agri_path):
df_agri = pd.read_csv(agri_path)
df_co2 = None
co2_path = os.path.join(data_dir, "CO2_Emissions_1960-2018.csv")
if os.path.exists(co2_path):
df_co2 = pd.read_csv(co2_path)
if 'Country Name' not in df_co2.columns:
st.error(f"Expected 'Country Name' in CO2 CSV, found: {df_co2.columns.tolist()}")
df_co2 = None
else:
# Ensure Country Name is cleaned before creating dummies for consistency
df_co2['Country Name'] = df_co2['Country Name'].str.strip()
dummies = pd.get_dummies(df_co2['Country Name'], prefix='Country')
country_features = dummies.columns.tolist()
df_co2 = pd.concat([df_co2, dummies], axis=1)
else:
country_features = None
return {
"model1": model1, "model2": model2, "model3": model3,
"scaler1": scaler1, "scalerX2": scalerX2, "scalerY2": scalerY2, "scaler3": scaler3,
"feature_cols2": feature_cols2, "df_agri": df_agri, "df_co2": df_co2,
"country_features": country_features,
}
def forecast_model1(model, scaler, recent_values):
arr = np.array(recent_values).reshape(-1, 1)
scaled = scaler.transform(arr).flatten()
inp = scaled.reshape((1, len(scaled), 1))
scaled_pred = model.predict(inp, verbose=0)[0, 0]
pred = scaler.inverse_transform([[scaled_pred]])[0, 0]
return pred
def predict_model2(model, scalerX, scalerY, feature_array):
X = np.array(feature_array).reshape(1, -1)
Xs = scalerX.transform(X)
ys = model.predict(Xs, verbose=0)
ypred = scalerY.inverse_transform(ys.reshape(-1, 1)).flatten()[0]
return ypred
def forecast_model3(model, scaler, recent_series, country_vec):
window = len(recent_series)
recent_series_np = np.array(recent_series).reshape(-1, 1)
co2_scaled_input = scaler.transform(recent_series_np).flatten()
co2_col = co2_scaled_input.reshape(window, 1)
country_mat = np.tile(country_vec.reshape(1, -1), (window, 1))
seq = np.concatenate([co2_col, country_mat], axis=1)
inp = seq.reshape(1, window, seq.shape[1])
# Get the raw model prediction and inverse transform it
ypred_scaled_output = model.predict(inp, verbose=0).flatten()
ypred_unforced = scaler.inverse_transform(ypred_scaled_output.reshape(-1, 1)).flatten()
# Apply non-negativity to the unforced predictions
ypred_processed = np.maximum(0, ypred_unforced)
# Apply monotonicity to the processed forecast
for i in range(1, len(ypred_processed)):
if ypred_processed[i] < ypred_processed[i-1]:
ypred_processed[i] = ypred_processed[i-1]
# Return the processed predictions. Scaling for display will happen in the calling function.
return ypred_processed
def create_animated_metric(label, value, icon="π―"):
st.markdown(f"""
""", unsafe_allow_html=True)
def sidebar_nav():
st.sidebar.markdown("""
π
AuraClima
"See the unseen, act on the future"
π€ AI-Powered
β‘ Real-time
""", unsafe_allow_html=True)
st.sidebar.markdown("---")
page = st.sidebar.radio("π Navigate", ["π Home", "π Climate Intelligence", "βΉοΈ About"],
label_visibility="collapsed")
return page
def home_page():
# Centered title
st.markdown('π AuraClima
', unsafe_allow_html=True)
# AI Features showcase
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("""
π±
Agricultural AI
LSTM Time Series Forecasting
Neural Network
""", unsafe_allow_html=True)
with col2:
st.markdown("""
π
Feature Analysis
Multi-variate Regression
Deep Learning
""", unsafe_allow_html=True)
with col3:
st.markdown("""
π¨
COβ Intelligence
Advanced sequence modeling
Advanced LSTM
""", unsafe_allow_html=True)
st.markdown("---")
st.markdown("""
π Advanced AI Climate Modeling
Leverage cutting-edge machine learning to forecast climate patterns, emissions, and environmental trends.
Our AI models process complex data to provide actionable insights for a sustainable future.
""", unsafe_allow_html=True)
def create_enhanced_plot(hist_years, series_co2_plot, fut_years_plot, pred3_plot, country):
fig = make_subplots(
rows=1, cols=1,
subplot_titles=[f"π AI Climate Intelligence: {country}"],
specs=[[{"secondary_y": False}]]
)
# Historical data (already scaled correctly when passed to this function)
fig.add_trace(
go.Scatter(
x=hist_years,
y=series_co2_plot, # This is the already scaled historical data for display
mode='lines+markers',
name='Historical Emissions',
line=dict(color='#1f77b4', width=3),
marker=dict(size=6, color='#1f77b4'),
hovertemplate='Year: %{x}
COβ: %{y:.2f}'
)
)
# Prepare forecast data for plotting to ensure continuity
last_historical_year = hist_years[-1]
last_historical_value = series_co2_plot[-1] # This should be 58.0 for 2018
# The forecast line needs to start from the exact last historical point (2018, 58.0)
# and then continue with its own predictions (2019, 58.0, 2020, predicted_value_2020, etc.).
# So, the first year for the forecast plot is the last historical year (2018).
# The first value for the forecast plot is the last historical value (58.0).
# Then append the actual future years and their predictions.
# Years for the forecast plot: last historical year + all future years from fut_years_plot
forecast_years_extended = [last_historical_year] + list(fut_years_plot)
# Values for the forecast plot: last historical value + all future predictions from pred3_plot
forecast_values_extended = [last_historical_value] + list(pred3_plot)
# Forecast data
fig.add_trace(
go.Scatter(
x=forecast_years_extended,
y=forecast_values_extended,
mode='lines+markers',
name='AI Forecast',
line=dict(color='#FF7F0E', width=4, dash='dash'),
marker=dict(size=8, color='#FF7F0E', symbol='diamond'),
hovertemplate='Year: %{x}
Predicted COβ: %{y:.2f}'
)
)
# Update layout
fig.update_layout(
title=dict(
text=f"COβ Emissions Forecast for {country}",
x=0.5,
font=dict(size=18, color='white')
),
xaxis_title="Year",
yaxis_title="COβ Emissions",
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
font=dict(color='white'),
legend=dict(
bgcolor='rgba(30, 37, 48, 0.8)',
bordercolor='#1f77b4',
borderwidth=1
),
hovermode='x unified'
)
fig.update_xaxes(gridcolor='rgba(31, 119, 180, 0.2)', griddash='dash', showgrid=True)
fig.update_yaxes(gridcolor='rgba(31, 119, 180, 0.2)', griddash='dash', showgrid=True)
return fig
def forecast_by_country(data):
st.markdown('π Climate Intelligence Dashboard
',
unsafe_allow_html=True)
model1, scaler1 = data["model1"], data["scaler1"]
model2, scalerX2, scalerY2, feature_cols2 = data["model2"], data["scalerX2"], data["scalerY2"], data[
"feature_cols2"]
model3, scaler3 = data["model3"], data["scaler3"]
df_agri, df_co2 = data["df_agri"], data["df_co2"]
if df_agri is None:
st.error("π¨ Agricultural dataset not found. Climate Intelligence unavailable.")
return
countries = sorted(df_agri['Area'].dropna().unique())
# Enhanced country selector
st.markdown("""
π― Select Country for AI Analysis
""", unsafe_allow_html=True)
country = st.selectbox("", countries, label_visibility="collapsed")
if not country:
return
df_ct = df_agri[df_agri['Area'] == country].sort_values('Year')
latest_year = int(df_ct['Year'].max())
# Create three columns for models
st.markdown("---")
st.markdown('π€ AI Model Predictions
', unsafe_allow_html=True)
col1, col2, col3 = st.columns(3)
# Model 1 - LSTM Forecast
with col1:
st.markdown("""
π± LSTM Time Series
Neural network analyzing temporal patterns
""", unsafe_allow_html=True)
inp1 = model1.input_shape
window1 = inp1[1]
series1 = df_ct.set_index('Year')['total_emission']
years1 = sorted(series1.index)
if len(years1) >= window1:
recent_vals = series1.loc[years1[-window1:]].values
with st.spinner("π AI Processing..."):
pred1 = forecast_model1(model1, scaler1, recent_vals)
create_animated_metric("Next Year Emission", f"{pred1:.2f}", "π±")
else:
st.info(f"β οΈ Need β₯{window1} years of data")
# Model 2 - Feature Analysis
with col2:
st.markdown("""
π Feature Analysis
Multi-variate regression modeling
""", unsafe_allow_html=True)
row_latest = df_ct[df_ct['Year'] == latest_year].iloc[0]
feature_array = []
for col in feature_cols2:
if col.startswith("Area_"):
feature_array.append(1.0 if col == f"Area_{country}" else 0.0)
else:
val = row_latest.get(col, 0.0)
feature_array.append(float(val))
try:
with st.spinner("π Analyzing features..."):
pred2 = predict_model2(model2, scalerX2, scalerY2, feature_array)
create_animated_metric("Feature Prediction", f"{pred2:.2f}", "π")
except Exception as e:
st.error(f"β Model error: {e}")
# Model 3 - CO2 Intelligence
with col3:
st.markdown("""
π¨ COβ Intelligence
Advanced sequence modeling
""", unsafe_allow_html=True)
pred3_plot = np.array([]) # Will hold the final scaled and adjusted forecast for plotting
scaled_series_co2_for_plot = np.array([])
series_co2_raw = np.array([])
year_cols = []
window3 = 0
if df_co2 is not None:
# IMPORTANT FIX: Clean country name from selectbox and DataFrame for consistent matching
selected_country_cleaned = country.strip()
dfc = df_co2[df_co2['Country Name'].str.strip() == selected_country_cleaned]
country_features = data["country_features"]
country_vec = np.zeros(len(country_features))
print(f"DEBUG_M3: Selected Country (cleaned): {selected_country_cleaned}")
print(f"DEBUG_M3: country_features (from load_all): {country_features[:5]}... ({len(country_features)} total)")
found_country_in_features = False
for i, name in enumerate(country_features):
if name == f"Country_{selected_country_cleaned}": # Use cleaned name for feature matching
country_vec[i] = 1
found_country_in_features = True
break
if not found_country_in_features:
st.warning(f"DEBUG_M3: WARNING! '{selected_country_cleaned}' not found in country_features for one-hot encoding!")
print(f"DEBUG_M3: Generated country_vec (sum should be 1.0 if found, else 0.0): {np.sum(country_vec)}")
# Start of the main conditional logic for dfc (DataFrame for CO2 data)
target_historical_display_value_2018 = 58.0
if dfc.empty or not found_country_in_features:
st.info(f"β οΈ No COβ data found or country not recognized for {selected_country_cleaned}. Displaying default forecast for demonstration.")
# Fallback: If no data or country not found, use generic historical and forecast
last_historical_year = 2018
forecast_length = 10 # Default forecast length
# Create a simple increasing series for fallback
pred3_plot = np.array([target_historical_display_value_2018 * (1 + 0.02*i) for i in range(forecast_length)])
scaled_series_co2_for_plot = np.linspace(0, target_historical_display_value_2018, 59) # Dummy historical data
year_cols = [str(y) for y in range(1960, 2019)] # Dummy years for fallback
avg_forecast = np.mean(pred3_plot)
create_animated_metric("Avg COβ Forecast", f"{avg_forecast:.2f}", "π¨")
else: # Country data found, proceed with actual calculations
year_cols = [c for c in dfc.columns if c.isdigit()]
series_co2_raw = dfc.iloc[0][year_cols].astype(float).dropna().values
inp3 = model3.input_shape
window3 = inp3[1] # This is 45 based on previous debug logs
print(f"DEBUG_M3: Original year_cols in df_co2: {year_cols}")
print(f"DEBUG_M3: Raw series_co2 (for model input, first 5, last 5): {series_co2_raw[:5]} ... {series_co2_raw[-5:]}")
print(f"DEBUG_M3: Length of series_co2_raw: {len(series_co2_raw)}")
print(f"DEBUG_M3: Model3 input window (window3): {window3}")
# --- START: CRITICAL SCALING AND TREND CONTROL LOGIC ---
actual_historical_raw_value_2018 = series_co2_raw[-1]
display_scaling_factor = 1.0
if actual_historical_raw_value_2018 > 1e-9:
display_scaling_factor = target_historical_display_value_2018 / actual_historical_raw_value_2018
else:
display_scaling_factor = 1000.0 # Fallback for 0 raw value, ensure some scale
display_scaling_factor = np.clip(display_scaling_factor, 0.1, 100000.0)
scaled_series_co2_for_plot = series_co2_raw * display_scaling_factor
print(f"DEBUG_M3: Calculated display_scaling_factor: {display_scaling_factor:.4f}")
print(f"DEBUG_M3: Last historical value (raw): {actual_historical_raw_value_2018:.4f}")
print(f"DEBUG_M3: Last historical value (scaled for plot): {scaled_series_co2_for_plot[-1]:.4f}")
# --- END: CRITICAL HISTORICAL SCALING LOGIC ---
if len(series_co2_raw) >= window3:
recent3 = series_co2_raw[-window3:] # Model still receives RAW data scale!
print(f"DEBUG_M3: Recent {window3} values for prediction (RAW SCALE for Model): {recent3[-5:]}")
with st.spinner("π COβ forecasting..."):
# Get processed predictions from the model (in its original trained scale)
pred_from_model_raw_scale = forecast_model3(model3, scaler3, recent3, country_vec)
# --- START: CONTROLLED FORECAST GENERATION FOR PLOTTING ---
pred3_plot = np.zeros_like(pred_from_model_raw_scale)
current_scaled_val = scaled_series_co2_for_plot[-1]
pred3_plot[0] = current_scaled_val
# Dynamic max absolute increase per year
# Lower the floor for min increase to allow for flatter trends.
dynamic_max_abs_increase_per_year = max(current_scaled_val * 0.05, 0.5) # Changed 2.0 to 0.5
for i in range(1, len(pred3_plot)):
raw_prev_val = pred_from_model_raw_scale[i-1]
raw_curr_val = pred_from_model_raw_scale[i]
raw_diff = raw_curr_val - raw_prev_val
scaled_diff_from_model = raw_diff * display_scaling_factor
clamped_scaled_diff = max(scaled_diff_from_model, 0) # Ensure non-decreasing
clamped_scaled_diff = min(clamped_scaled_diff, dynamic_max_abs_increase_per_year)
pred3_plot[i] = pred3_plot[i-1] + clamped_scaled_diff
dynamic_max_abs_increase_per_year = max(pred3_plot[i] * 0.05, 0.5) # Changed 2.0 to 0.5
# --- END: CONTROLLED FORECAST GENERATION FOR PLOTTING ---
avg_forecast = np.mean(pred3_plot)
create_animated_metric("Avg COβ Forecast", f"{avg_forecast:.2f}", "π¨")
else: # Not enough historical data for the model (len(series_co2_raw) < window3)
st.info(f"β οΈ Not enough COβ data (need β₯{window3} years) for {selected_country_cleaned}. Found {len(series_co2_raw)} years. Displaying default forecast.")
# Use actual scaled historical data for plot, but generic forecast
# historical_data_for_plot will be scaled_series_co2_for_plot (which contains actual data)
forecast_length = 10
# Generic linear forecast starting from the last actual historical value
pred3_plot = np.array([scaled_series_co2_for_plot[-1] * (1 + 0.02*i) for i in range(forecast_length)])
avg_forecast = np.mean(pred3_plot)
create_animated_metric("Avg COβ Forecast", f"{avg_forecast:.2f}", "π¨")
else: # df_co2 is None (CSV file was not loaded successfully in load_all)
st.error("β COβ data unavailable. Please check CO2_Emissions_1960-2018.csv.")
# Interactive Parameter Tuning (remains unchanged)
st.markdown("---")
st.markdown('βοΈ Interactive Parameter Tuning
',
unsafe_allow_html=True)
with st.expander("ποΈ Adjust Model Parameters", expanded=False):
st.markdown("**Modify features to explore different scenarios:**")
tweaked = []
cols_numeric = [c for c in feature_cols2 if not c.startswith("Area_")]
cols = st.columns(2)
for i, col in enumerate(feature_cols2):
if col.startswith("Area_"):
tweaked.append(feature_array[i])
else:
series_col = df_agri[col].dropna().astype(float)
if not series_col.empty:
mn, mx = float(series_col.min()), float(series_col.max())
default = feature_array[i]
slider_val = cols[i % 2].slider(f"π§ {col}", mn, mx, default, key=f"slider_{col}")
tweaked.append(slider_val)
else:
tweaked.append(feature_array[i])
if st.button("π Run Enhanced Prediction"):
try:
with st.spinner("π€ AI recalculating..."):
pred2b = predict_model2(model2, scalerX2, scalerY2, tweaked)
create_animated_metric("Adjusted Prediction", f"{pred2b:.2f}", "π―")
except Exception as e:
st.error(f"β Error: {e}")
# Enhanced CO2 Visualization
# Ensure pred3_plot (the plot data) is not empty before proceeding
# The conditions here need to reflect the fallback paths if actual data isn't available
if (df_co2 is not None and not dfc.empty and len(series_co2_raw) >= window3 and len(pred3_plot) > 0) or \
(df_co2 is not None and (dfc.empty or not found_country_in_features)) or \
(df_co2 is not None and not dfc.empty and len(series_co2_raw) < window3):
st.markdown("---")
st.markdown('π Advanced COβ Visualization
',
unsafe_allow_html=True)
# Determine which historical data to use for plotting
if df_co2 is None or dfc.empty or not found_country_in_features: # Full fallback scenario
hist_years = [str(y) for y in range(1960, 2019)]
historical_data_for_plot = np.linspace(0, target_historical_display_value_2018, len(hist_years))
elif len(series_co2_raw) < window3: # Insufficient data for model, but data exists
hist_years = list(map(int, year_cols)) # Use actual years from available data
historical_data_for_plot = scaled_series_co2_for_plot # Use scaled actual data
else: # Full data, model used
hist_years = list(map(int, year_cols))
historical_data_for_plot = scaled_series_co2_for_plot
last_year_historical = int(hist_years[-1])
print(f"DEBUG_PLOT_FINAL: Historical data for plot (first 5, last 5): {historical_data_for_plot[:5]} ... {historical_data_for_plot[-5:]}")
print(f"DEBUG_PLOT_FINAL: Forecast data for plot (first 5, last 5): {pred3_plot[:5]} ... {pred3_plot[-5:]}")
print(f"DEBUG_PLOT_FINAL: Connection check - Last scaled historical: {historical_data_for_plot[-1]:.4f}, First forecast: {pred3_plot[0]:.4f}")
# Prepare years for the forecast plot (starting from the year *after* the last historical year)
fut_years_plot = [last_year_historical + i + 1 for i in range(len(pred3_plot))]
# Create enhanced interactive plot
fig = create_enhanced_plot(hist_years, historical_data_for_plot, fut_years_plot, pred3_plot, country)
st.plotly_chart(fig, use_container_width=True)
# Forecast summary table (uses the same pred3_plot and corresponding years)
st.markdown('π Detailed Forecast Summary
', unsafe_allow_html=True)
fut_years_summary = fut_years_plot # Use the same years as the plot for consistency
forecast_df = pd.DataFrame({
'ποΈ Year': fut_years_summary,
'π¨ Predicted COβ': [f"{val:.2f}" for val in pred3_plot],
'π Trend': ['βοΈ' if i == 0 or pred3_plot[i] > pred3_plot[i - 1] else 'β‘οΈ' for i in range(len(pred3_plot))] # Changed βοΈ to β‘οΈ for non-decreasing
})
st.dataframe(forecast_df, use_container_width=True)
else:
# If none of the conditions for plotting are met (e.g., df_co2 is None and no fallback message was given)
st.warning("β οΈ Cannot display COβ visualization due to missing or insufficient data. Please check data files.")
def about_page():
st.markdown('π AuraClima
', unsafe_allow_html=True)
st.markdown('Advanced AI Climate Intelligence Platform
', unsafe_allow_html=True)
st.markdown("""
π― Mission
AuraClima leverages cutting-edge artificial intelligence to forecast climate patterns and emissions,
empowering decision-makers to "See the unseen, act on the future."
""", unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
st.markdown("""
π€ Technology Stack
TensorFlow
LSTM Networks
Neural Networks
Time Series
""", unsafe_allow_html=True)
with col2:
st.markdown("""
π¨ Brand Identity
Primary: Blue (#1f77b4)
Secondary: Orange (#FF7F0E)
""", unsafe_allow_html=True)
st.markdown("""
Developed by: Abdullah Imran
Contact: abdullahimranarshad@gmail.com
""", unsafe_allow_html=True)
# Main Application
def main():
# Load resources once
data = load_all()
# Sidebar navigation
page = sidebar_nav()
# Page routing
if page == "π Home":
home_page()
elif page == "π Climate Intelligence":
forecast_by_country(data)
elif page == "βΉοΈ About":
about_page()
if __name__ == "__main__":
main()