shaheerawan3's picture
Update app.py
f0aab4e verified
raw
history blame
28.4 kB
import streamlit as st
import pandas as pd
import numpy as np
import requests
from bs4 import BeautifulSoup
import folium
from streamlit_folium import folium_static
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
import json
from io import StringIO
import streamlit.components.v1 as components
import base64
from sklearn.ensemble import RandomForestRegressor
from prophet import Prophet
import tensorflow as tf
from xgboost import XGBRegressor
import seaborn as sns
# Page configuration
st.set_page_config(layout="wide", page_title="Pakistan Climate & Disaster Monitor", page_icon="🌍")
class DataCollector:
def __init__(self):
self.cities = {
'Islamabad': {'lat': 33.7294, 'lon': 73.0931},
'Karachi': {'lat': 24.8607, 'lon': 67.0011},
'Lahore': {'lat': 31.5204, 'lon': 74.3587},
'Peshawar': {'lat': 34.0151, 'lon': 71.5249},
'Quetta': {'lat': 30.1798, 'lon': 66.9750},
'Multan': {'lat': 30.1575, 'lon': 71.5249},
'Faisalabad': {'lat': 31.4504, 'lon': 73.1350},
'Rawalpindi': {'lat': 33.6007, 'lon': 73.0679},
'Gwadar': {'lat': 25.1216, 'lon': 62.3254},
'Hyderabad': {'lat': 25.3960, 'lon': 68.3578}
}
def fetch_weather_data(self):
"""Fetch weather data from OpenMeteo"""
weather_data = []
for city, coords in self.cities.items():
try:
url = f"https://api.open-meteo.com/v1/forecast?latitude={coords['lat']}&longitude={coords['lon']}&hourly=temperature_2m,relativehumidity_2m,precipitation,windspeed_10m&daily=temperature_2m_max,temperature_2m_min,precipitation_sum&timezone=auto&past_days=7"
response = requests.get(url)
data = response.json()
# Hourly data
hourly_df = pd.DataFrame({
'datetime': pd.to_datetime(data['hourly']['time']),
'temperature': data['hourly']['temperature_2m'],
'humidity': data['hourly']['relativehumidity_2m'],
'precipitation': data['hourly']['precipitation'],
'wind_speed': data['hourly']['windspeed_10m']
})
# Daily data
daily_df = pd.DataFrame({
'date': pd.to_datetime(data['daily']['time']),
'temp_max': data['daily']['temperature_2m_max'],
'temp_min': data['daily']['temperature_2m_min'],
'precipitation_sum': data['daily']['precipitation_sum']
})
weather_data.append({
'city': city,
'hourly': hourly_df,
'daily': daily_df,
'coords': coords
})
except Exception as e:
st.error(f"Error fetching weather data for {city}: {e}")
continue
return weather_data if weather_data else None
def fetch_usgs_earthquake_data(self):
"""Fetch earthquake data from USGS website"""
try:
# USGS API endpoint for past month's earthquakes
url = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.geojson"
response = requests.get(url)
data = response.json()
# Filter for Pakistan region
pakistan_data = {
"type": "FeatureCollection",
"features": [
feature for feature in data["features"]
if 60.878 <= feature["geometry"]["coordinates"][0] <= 77.840
and 23.692 <= feature["geometry"]["coordinates"][1] <= 37.097
]
}
return pakistan_data
except Exception as e:
st.error(f"Error fetching earthquake data: {e}")
return None
def fetch_air_quality_data(self):
"""Fetch air quality data from OpenMeteo"""
aqi_data = []
for city, coords in self.cities.items():
try:
url = f"https://air-quality-api.open-meteo.com/v1/air-quality?latitude={coords['lat']}&longitude={coords['lon']}&hourly=pm10,pm2_5,carbon_monoxide,nitrogen_dioxide,ozone&timezone=auto&past_days=7"
response = requests.get(url)
data = response.json()
df = pd.DataFrame({
'datetime': pd.to_datetime(data['hourly']['time']),
'PM10': data['hourly']['pm10'],
'PM2.5': data['hourly']['pm2_5'],
'CO': data['hourly']['carbon_monoxide'],
'NO2': data['hourly']['nitrogen_dioxide'],
'O3': data['hourly']['ozone'],
'city': city
})
aqi_data.append(df)
except Exception as e:
st.error(f"Error fetching AQI data for {city}: {e}")
continue
return pd.concat(aqi_data, ignore_index=True) if aqi_data else None
def create_ml_features(self, weather_data):
"""Create features for ML predictions"""
features_df = pd.DataFrame()
for city_data in weather_data:
df = city_data['hourly'].copy()
df['city'] = city_data['city']
# Create time-based features
df['hour'] = df['datetime'].dt.hour
df['day'] = df['datetime'].dt.day
df['month'] = df['datetime'].dt.month
df['day_of_week'] = df['datetime'].dt.dayofweek
# Create lag features
df['temp_lag_1'] = df['temperature'].shift(1)
df['temp_lag_24'] = df['temperature'].shift(24)
# Create rolling means
df['temp_rolling_mean_6h'] = df['temperature'].rolling(window=6).mean()
df['temp_rolling_mean_24h'] = df['temperature'].rolling(window=24).mean()
features_df = pd.concat([features_df, df])
return features_df.dropna()
def create_cesium_component(earthquake_data=None, weather_data=None):
"""Enhanced Cesium 3D visualization"""
cesium_html = """
<div id="cesiumContainer" style="width: 100%; height: 600px;"></div>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<script>
Cesium.Ion.defaultAccessToken = 'your-access-token';
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain(),
timeline: true,
animation: true,
baseLayerPicker: true,
scene3DOnly: false,
navigationHelpButton: true,
navigationInstructionsInitiallyVisible: false,
selectionIndicator: true,
infoBox: true
});
// Add Pakistan terrain
viewer.scene.globe.enableLighting = true;
viewer.scene.globe.terrainExaggeration = 1.5;
// Add weather visualization
const weatherEntities = new Cesium.CustomDataSource('Weather');
viewer.dataSources.add(weatherEntities);
"""
# Add earthquake data if available
if earthquake_data:
cesium_html += """
// Add earthquake visualization
const earthquakeEntities = new Cesium.CustomDataSource('Earthquakes');
"""
for eq in earthquake_data['features']:
coords = eq['geometry']['coordinates']
mag = eq['properties']['mag']
cesium_html += f"""
earthquakeEntities.entities.add({{
position: Cesium.Cartesian3.fromDegrees({coords[0]}, {coords[1]}, {coords[2]}),
point: {{
pixelSize: {mag * 5},
color: Cesium.Color.RED.withAlpha(0.8),
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
}},
description: `Magnitude: {mag}<br>Depth: {coords[2]} km`
}});
"""
cesium_html += """
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(69.3451, 30.3753, 1000000.0),
orientation: {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-45.0),
roll: 0.0
}
});
</script>
"""
components.html(cesium_html, height=600)
def train_weather_model(features_df, city):
"""Train ML model for weather predictions"""
city_data = features_df[features_df['city'] == city].copy()
# Prepare features
feature_cols = ['hour', 'day', 'month', 'day_of_week',
'temp_lag_1', 'temp_lag_24',
'temp_rolling_mean_6h', 'temp_rolling_mean_24h']
X = city_data[feature_cols]
y = city_data['temperature']
# Split data
split_idx = int(len(X) * 0.8)
X_train, X_test = X[:split_idx], X[split_idx:]
y_train, y_test = y[:split_idx], y[split_idx:]
# Train model
model = XGBRegressor(n_estimators=100)
model.fit(X_train, y_train)
return model, X_test, y_test
def show_weather_analysis(data_collector):
st.header("Advanced Weather Analysis 🌀️")
weather_data = data_collector.fetch_weather_data()
if weather_data:
selected_city = st.selectbox(
"Select City",
options=[data['city'] for data in weather_data]
)
city_data = next(data for data in weather_data if data['city'] == selected_city)
# Add download button for data
st.markdown(download_csv(city_data['hourly'], f"{selected_city}_weather_data"), unsafe_allow_html=True)
tabs = st.tabs(["Temperature Analysis", "Precipitation Insights",
"Wind Patterns", "Humidity Trends", "ML Predictions"])
with tabs[0]:
col1, col2 = st.columns(2)
with col1:
# Enhanced temperature visualization
fig = go.Figure()
fig.add_trace(go.Scatter(
x=city_data['hourly']['datetime'],
y=city_data['hourly']['temperature'],
name='Temperature',
line=dict(color='red', width=2)
))
fig.update_layout(
title='Temperature Trend with Range',
template='plotly_dark',
hovermode='x unified'
)
st.plotly_chart(fig, use_container_width=True)
with col2:
# Temperature distribution
fig = px.histogram(
city_data['hourly'],
x='temperature',
nbins=30,
title='Temperature Distribution'
)
st.plotly_chart(fig, use_container_width=True)
with tabs[1]:
# Enhanced precipitation analysis
col1, col2 = st.columns(2)
with col1:
fig = px.bar(
city_data['daily'],
x='date',
y='precipitation_sum',
title='Daily Precipitation',
color='precipitation_sum',
color_continuous_scale='Blues'
)
st.plotly_chart(fig, use_container_width=True)
with col2:
# Precipitation probability calculation
precip_prob = (city_data['hourly']['precipitation'] > 0).mean() * 100
st.metric(
"Precipitation Probability",
f"{precip_prob:.1f}%",
delta=f"{precip_prob - 50:.1f}%"
)
with tabs[2]:
# Enhanced wind analysis
fig = go.Figure()
fig.add_trace(go.Scatter(
x=city_data['hourly']['datetime'],
y=city_data['hourly']['wind_speed'],
name='Wind Speed',
line=dict(color='blue', width=2)
))
fig.add_trace(go.Scatter(
x=city_data['hourly']['datetime'],
y=city_data['hourly']['wind_speed'].rolling(24).mean(),
name='24h Moving Average',
line=dict(color='red', width=2, dash='dash')
))
fig.update_layout(
title='Wind Speed Analysis',
template='plotly_dark',
hovermode='x unified'
)
st.plotly_chart(fig, use_container_width=True)
with tabs[3]:
# Enhanced humidity analysis
col1, col2 = st.columns(2)
with col1:
fig = px.line(
city_data['hourly'],
x='datetime',
y='humidity',
title='Humidity Trends',
color_discrete_sequence=['green']
)
st.plotly_chart(fig, use_container_width=True)
with col2:
# Humidity comfort zones
comfort_zones = pd.cut(
city_data['hourly']['humidity'],
bins=[0, 30, 45, 60, 100],
labels=['Dry', 'Comfortable', 'Moderate', 'Humid']
).value_counts()
fig = px.pie(
values=comfort_zones.values,
names=comfort_zones.index,
title='Humidity Comfort Zones'
)
st.plotly_chart(fig, use_container_width=True)
with tabs[4]:
st.subheader("Machine Learning Weather Predictions")
# Prepare data for ML
features_df = data_collector.create_ml_features(weather_data)
model, X_test, y_test = train_weather_model(features_df, selected_city)
# Make predictions
predictions = model.predict(X_test)
# Show predictions vs actual
fig = go.Figure()
fig.add_trace(go.Scatter(
x=X_test.index,
y=y_test,
name='Actual Temperature',
line=dict(color='blue')
))
fig.add_trace(go.Scatter(
x=X_test.index,
y=predictions,
name='Predicted Temperature',
line=dict(color='red', dash='dash')
))
fig.update_layout(
title='Temperature Predictions vs Actual',
template='plotly_dark',
hovermode='x unified'
)
st.plotly_chart(fig, use_container_width=True)
# Model metrics
mae = np.mean(np.abs(predictions - y_test))
rmse = np.sqrt(np.mean((predictions - y_test)**2))
col1, col2 = st.columns(2)
col1.metric("Mean Absolute Error", f"{mae:.2f}Β°C")
col2.metric("Root Mean Square Error", f"{rmse:.2f}Β°C")
def show_disaster_monitor(data_collector):
st.header("Advanced Disaster Monitoring System 🚨")
earthquake_data = data_collector.fetch_usgs_earthquake_data()
if earthquake_data:
# Enhanced 3D visualization
st.subheader("3D Terrain Analysis")
create_cesium_component(earthquake_data)
# Advanced earthquake analysis
st.subheader("Earthquake Analysis Dashboard")
# Create DataFrame for analysis
eq_df = pd.DataFrame([
{
'time': datetime.fromtimestamp(eq['properties']['time']/1000),
'magnitude': eq['properties']['mag'],
'location': eq['properties']['place'],
'depth': eq['geometry']['coordinates'][2],
'lat': eq['geometry']['coordinates'][1],
'lon': eq['geometry']['coordinates'][0]
}
for eq in earthquake_data['features']
])
col1, col2 = st.columns(2)
with col1:
# Magnitude distribution
fig = px.histogram(
eq_df,
x='magnitude',
nbins=20,
title='Earthquake Magnitude Distribution',
color_discrete_sequence=['red']
)
st.plotly_chart(fig, use_container_width=True)
with col2:
# Depth vs Magnitude scatter
fig = px.scatter(
eq_df,
x='depth',
y='magnitude',
title='Depth vs Magnitude',
color='magnitude',
size='magnitude',
color_continuous_scale='Viridis'
)
st.plotly_chart(fig, use_container_width=True)
# Time series analysis
st.subheader("Temporal Analysis")
eq_df# Time series analysis
eq_df['date'] = eq_df['time'].dt.date
daily_counts = eq_df.groupby('date').size().reset_index(name='count')
fig = px.line(
daily_counts,
x='date',
y='count',
title='Daily Earthquake Frequency',
line_shape='spline'
)
st.plotly_chart(fig, use_container_width=True)
# Risk assessment
st.subheader("Seismic Risk Assessment")
risk_zones = folium.Map(location=[30.3753, 69.3451], zoom_start=5)
# Create heatmap layer
heat_data = [[row['lat'], row['lon'], row['magnitude']] for _, row in eq_df.iterrows()]
folium.plugins.HeatMap(heat_data).add_to(risk_zones)
# Add fault lines (simplified example)
fault_lines = {
'Main Boundary Thrust': [[34.0151, 71.5249], [33.7294, 73.0931]],
'Chaman Fault': [[30.1798, 66.9750], [25.1216, 62.3254]],
}
for name, coords in fault_lines.items():
folium.PolyLine(
coords,
color='red',
weight=2,
popup=name
).add_to(risk_zones)
folium_static(risk_zones)
# Earthquake prediction model
st.subheader("Seismic Activity Prediction")
# Prepare time series data for prediction
daily_counts['ds'] = pd.to_datetime(daily_counts['date'])
daily_counts['y'] = daily_counts['count']
# Train Prophet model
model = Prophet(yearly_seasonality=True, weekly_seasonality=True)
model.fit(daily_counts[['ds', 'y']])
# Make future predictions
future_dates = model.make_future_dataframe(periods=30)
forecast = model.predict(future_dates)
# Plot predictions
fig = go.Figure()
fig.add_trace(go.Scatter(
x=daily_counts['ds'],
y=daily_counts['y'],
name='Actual',
line=dict(color='blue')
))
fig.add_trace(go.Scatter(
x=forecast['ds'],
y=forecast['yhat'],
name='Predicted',
line=dict(color='red', dash='dash')
))
fig.add_trace(go.Scatter(
x=forecast['ds'],
y=forecast['yhat_upper'],
fill=None,
mode='lines',
line=dict(color='rgba(255,0,0,0)'),
showlegend=False
))
fig.add_trace(go.Scatter(
x=forecast['ds'],
y=forecast['yhat_lower'],
fill='tonexty',
mode='lines',
line=dict(color='rgba(255,0,0,0)'),
name='Prediction Interval'
))
fig.update_layout(
title='Seismic Activity Forecast (30 Days)',
xaxis_title='Date',
yaxis_title='Number of Earthquakes',
template='plotly_dark'
)
st.plotly_chart(fig, use_container_width=True)
def show_environmental_data(data_collector):
st.header("Advanced Environmental Analysis 🌿")
aqi_data = data_collector.fetch_air_quality_data()
if aqi_data is not None:
selected_city = st.selectbox("Select City", aqi_data['city'].unique())
city_data = aqi_data[aqi_data['city'] == selected_city].copy()
# Add download button
st.markdown(download_csv(city_data, f"{selected_city}_air_quality_data"), unsafe_allow_html=True)
# Enhanced AQI calculation with weights
weights = {
'PM2.5': 0.3,
'PM10': 0.2,
'NO2': 0.2,
'O3': 0.2,
'CO': 0.1
}
# Normalize and calculate weighted AQI
for pollutant in weights.keys():
max_val = city_data[pollutant].max()
city_data[f'{pollutant}_normalized'] = city_data[pollutant] / max_val * 100
city_data[f'{pollutant}_weighted'] = city_data[f'{pollutant}_normalized'] * weights[pollutant]
city_data['AQI'] = sum(city_data[f'{p}_weighted'] for p in weights.keys())
tabs = st.tabs(["AQI Dashboard", "Pollutant Analysis", "Trends & Forecasting", "Health Impact"])
with tabs[0]:
col1, col2, col3 = st.columns(3)
current_aqi = city_data['AQI'].iloc[-1]
with col1:
st.metric(
"Current AQI",
f"{current_aqi:.1f}",
delta=f"{current_aqi - city_data['AQI'].iloc[-2]:.1f}"
)
# AQI categories
aqi_categories = pd.cut(
city_data['AQI'],
bins=[0, 50, 100, 150, 200, 300, float('inf')],
labels=['Good', 'Moderate', 'Unhealthy for Sensitive Groups', 'Unhealthy', 'Very Unhealthy', 'Hazardous']
).value_counts()
with col2:
fig = px.pie(
values=aqi_categories.values,
names=aqi_categories.index,
title='AQI Distribution'
)
st.plotly_chart(fig, use_container_width=True)
with col3:
# Daily pattern
hourly_avg = city_data.groupby(city_data['datetime'].dt.hour)['AQI'].mean()
fig = px.line(
x=hourly_avg.index,
y=hourly_avg.values,
title='Daily AQI Pattern',
labels={'x': 'Hour of Day', 'y': 'Average AQI'}
)
st.plotly_chart(fig, use_container_width=True)
with tabs[1]:
# Pollutant correlation analysis
pollutants = ['PM2.5', 'PM10', 'CO', 'NO2', 'O3']
corr_matrix = city_data[pollutants].corr()
fig = px.imshow(
corr_matrix,
title='Pollutant Correlation Matrix',
color_continuous_scale='RdBu'
)
st.plotly_chart(fig, use_container_width=True)
# Individual pollutant analysis
selected_pollutant = st.selectbox("Select Pollutant", pollutants)
col1, col2 = st.columns(2)
with col1:
fig = px.line(
city_data,
x='datetime',
y=selected_pollutant,
title=f'{selected_pollutant} Trend'
)
st.plotly_chart(fig, use_container_width=True)
with col2:
fig = px.box(
city_data,
y=selected_pollutant,
title=f'{selected_pollutant} Distribution'
)
st.plotly_chart(fig, use_container_width=True)
with tabs[2]:
# Time series decomposition
from statsmodels.tsa.seasonal import seasonal_decompose
# Resample to hourly data for decomposition
hourly_data = city_data.set_index('datetime')['AQI'].resample('H').mean()
decomposition = seasonal_decompose(hourly_data, period=24)
fig = make_subplots(rows=4, cols=1, subplot_titles=('Observed', 'Trend', 'Seasonal', 'Residual'))
fig.add_trace(go.Scatter(x=hourly_data.index, y=hourly_data.values, name='Observed'), row=1, col=1)
fig.add_trace(go.Scatter(x=hourly_data.index, y=decomposition.trend, name='Trend'), row=2, col=1)
fig.add_trace(go.Scatter(x=hourly_data.index, y=decomposition.seasonal, name='Seasonal'), row=3, col=1)
fig.add_trace(go.Scatter(x=hourly_data.index, y=decomposition.resid, name='Residual'), row=4, col=1)
fig.update_layout(height=800, title_text="AQI Time Series Decomposition")
st.plotly_chart(fig, use_container_width=True)
with tabs[3]:
st.subheader("Health Impact Assessment")
# Define health impact thresholds
impact_thresholds = {
'PM2.5': [12, 35.4, 55.4, 150.4],
'PM10': [54, 154, 254, 354],
'NO2': [53, 100, 360, 649],
'O3': [54, 70, 85, 105],
'CO': [4.4, 9.4, 12.4, 15.4]
}
# Calculate current health risks
current_risks = {}
for pollutant, thresholds in impact_thresholds.items():
current_val = city_data[pollutant].iloc[-1]
if current_val <= thresholds[0]:
risk = 'Low'
elif current_val <= thresholds[1]:
risk = 'Moderate'
elif current_val <= thresholds[2]:
risk = 'High'
else:
risk = 'Very High'
current_risks[pollutant] = {'value': current_val, 'risk': risk}
# Display health risks
col1, col2 = st.columns(2)
with col1:
for pollutant, data in current_risks.items():
st.metric(
f"{pollutant} Health Risk",
data['risk'],
f"{data['value']:.1f}"
)
with col2:
# Health recommendations based on current AQI
if current_aqi <= 50:
st.success("Air quality is good. Outdoor activities are safe.")
elif current_aqi <= 100:
st.info("Sensitive individuals should consider reducing prolonged outdoor exertion.")
elif current_aqi <= 150:
st.warning("Everyone should reduce prolonged outdoor exertion.")
else:
st.error("Avoid outdoor activities. Use air purifiers indoors.")
def main():
st.title("🌍 Pakistan Climate & Disaster Monitoring System")
# Add dashboard info
st.sidebar.image("https://upload.wikimedia.org/wikipedia/commons/3/32/Flag_of_Pakistan.svg", width=100)
st.sidebar.title("Dashboard Controls")
data_collector = DataCollector()
# Enhanced navigation
page = st.sidebar.radio(
"Select Module",
["Weather Analysis", "Disaster Monitor", "Environmental Data"],
format_func=lambda x: f"πŸ“Š {x}" if x == "Weather Analysis" else
f"🚨 {x}" if x == "Disaster Monitor" else
f"🌿 {x}"
)
# Add data timestamp
st.sidebar.markdown("---")
st.sidebar.markdown(f"Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
if page == "Weather Analysis":
show_weather_analysis(data_collector)
elif page == "Disaster Monitor":
show_disaster_monitor(data_collector)
elif page == "Environmental Data":
show_environmental_data(data_collector)
if __name__ == "__main__":
main()