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 from plotly.subplots import make_subplots # Define utility functions first def download_csv(df, filename): """Generate a download link for a dataframe""" csv = df.to_csv(index=False) b64 = base64.b64encode(csv.encode()).decode() href = f'Download {filename} CSV' return href # 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 download_csv(df, filename): """Generate a download link for a dataframe""" csv = df.to_csv(index=False) b64 = base64.b64encode(csv.encode()).decode() href = f'Download {filename} CSV' return href def create_3d_visualization(earthquake_data=None, weather_data=None): """Create an interactive 3D visualization of Pakistan""" threejs_html = """

Pakistan Terrain Map

🔴 Cities | 🟡 Earthquake Events

""" components.html(threejs_html, height=600) # Update the show_disaster_monitor function def show_disaster_monitor(data_collector): st.header("Advanced Disaster Monitoring System 🚨") earthquake_data = data_collector.fetch_usgs_earthquake_data() if earthquake_data: st.subheader("3D Terrain Visualization") create_3d_visualization(earthquake_data) # Using the new visualization function # ... rest of your existing show_disaster_monitor function code ... # The rest of your existing code remains the same, but make sure to remove any references # to create_cesium_component and replace them with create_3d_visualization 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}
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 } }); """ 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_3d_visualization(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) st.markdown("---") st.markdown("""

Created by Muhammad Shaheer

""", unsafe_allow_html=True) if __name__ == "__main__": main()