| | 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 |
| |
|
| | |
| | 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'<a href="data:file/csv;base64,{b64}" download="{filename}.csv">Download {filename} CSV</a>' |
| | return href |
| |
|
| | |
| | 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_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_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: |
| | |
| | url = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.geojson" |
| | response = requests.get(url) |
| | data = response.json() |
| | |
| | |
| | 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'] |
| | |
| | |
| | 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 |
| | |
| | |
| | df['temp_lag_1'] = df['temperature'].shift(1) |
| | df['temp_lag_24'] = df['temperature'].shift(24) |
| | |
| | |
| | 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'<a href="data:file/csv;base64,{b64}" download="{filename}.csv">Download {filename} CSV</a>' |
| | return href |
| |
|
| | |
| | def create_3d_visualization(earthquake_data=None, weather_data=None): |
| | """Create an interactive 3D visualization of Pakistan""" |
| | threejs_html = """ |
| | <div id="visualizationContainer" style="width: 100%; height: 600px;"> |
| | <div style="position: absolute; top: 10px; left: 10px; background: rgba(255,255,255,0.8); padding: 10px; border-radius: 5px;"> |
| | <h3 style="margin: 0;">Pakistan Terrain Map</h3> |
| | <p style="margin: 5px 0;">π΄ Cities | π‘ Earthquake Events</p> |
| | </div> |
| | </div> |
| | <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
| | <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script> |
| | <script> |
| | const container = document.getElementById('visualizationContainer'); |
| | const scene = new THREE.Scene(); |
| | scene.background = new THREE.Color(0xc6e6ff); // Light blue sky |
| | const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000); |
| | const renderer = new THREE.WebGLRenderer({ antialias: true }); |
| | renderer.setSize(container.clientWidth, container.clientHeight); |
| | container.appendChild(renderer.domElement); |
| | |
| | // Pakistan outline coordinates (simplified) |
| | const pakistanOutline = [ |
| | [75.3, 35.5], [72.8, 36.5], [71.2, 34.7], [69.5, 34.0], |
| | [66.8, 31.2], [63.2, 26.8], [61.5, 25.0], [63.5, 23.5], |
| | [66.7, 24.5], [67.8, 24.8], [68.9, 27.0], [70.5, 28.5], |
| | [72.0, 30.0], [74.0, 32.5], [75.3, 35.5] |
| | ]; |
| | |
| | // Create Pakistan terrain |
| | const shape = new THREE.Shape(); |
| | pakistanOutline.forEach((point, index) => { |
| | if (index === 0) shape.moveTo(point[0] - 67, point[1] - 30); |
| | else shape.lineTo(point[0] - 67, point[1] - 30); |
| | }); |
| | |
| | const geometry = new THREE.ExtrudeGeometry(shape, { |
| | depth: 2, |
| | bevelEnabled: true, |
| | bevelSegments: 2, |
| | steps: 2, |
| | bevelSize: 0.3, |
| | bevelThickness: 0.3 |
| | }); |
| | |
| | const material = new THREE.MeshPhongMaterial({ |
| | color: 0x4CAF50, |
| | flatShading: true, |
| | side: THREE.DoubleSide |
| | }); |
| | |
| | const pakistan = new THREE.Mesh(geometry, material); |
| | scene.add(pakistan); |
| | |
| | // Add major cities |
| | const cities = { |
| | 'Islamabad': [73.0931, 33.7294], |
| | 'Karachi': [67.0011, 24.8607], |
| | 'Lahore': [74.3587, 31.5204], |
| | 'Peshawar': [71.5249, 34.0151], |
| | 'Quetta': [66.9750, 30.1798] |
| | }; |
| | |
| | Object.entries(cities).forEach(([name, coords]) => { |
| | const cityMarker = new THREE.Mesh( |
| | new THREE.SphereGeometry(0.3, 32, 32), |
| | new THREE.MeshPhongMaterial({ color: 0xff0000 }) |
| | ); |
| | cityMarker.position.set(coords[0] - 67, coords[1] - 30, 2.5); |
| | scene.add(cityMarker); |
| | |
| | // Add city label |
| | const div = document.createElement('div'); |
| | div.className = 'label'; |
| | div.textContent = name; |
| | div.style.color = 'black'; |
| | div.style.position = 'absolute'; |
| | div.style.padding = '2px'; |
| | div.style.backgroundColor = 'rgba(255,255,255,0.7)'; |
| | div.style.borderRadius = '3px'; |
| | container.appendChild(div); |
| | }); |
| | """ |
| |
|
| | if earthquake_data: |
| | threejs_html += """ |
| | // Add earthquake markers |
| | const earthquakes = """ + json.dumps(earthquake_data['features']) + """; |
| | earthquakes.forEach(quake => { |
| | const coords = quake.geometry.coordinates; |
| | const magnitude = quake.properties.mag; |
| | |
| | const quakeMarker = new THREE.Mesh( |
| | new THREE.SphereGeometry(magnitude * 0.2, 32, 32), |
| | new THREE.MeshPhongMaterial({ |
| | color: 0xFFD700, |
| | opacity: 0.8, |
| | transparent: true |
| | }) |
| | ); |
| | quakeMarker.position.set(coords[0] - 67, coords[1] - 30, 2.5); |
| | scene.add(quakeMarker); |
| | }); |
| | """ |
| |
|
| | threejs_html += """ |
| | // Lighting |
| | const ambientLight = new THREE.AmbientLight(0x404040, 1); |
| | scene.add(ambientLight); |
| | const directionalLight = new THREE.DirectionalLight(0xffffff, 1); |
| | directionalLight.position.set(5, 5, 5); |
| | scene.add(directionalLight); |
| | |
| | // Camera |
| | camera.position.set(0, 0, 20); |
| | camera.lookAt(scene.position); |
| | |
| | // Controls |
| | const controls = { |
| | rotation: true, |
| | elevation: 1.0 |
| | }; |
| | |
| | const gui = new dat.GUI({ autoPlace: false }); |
| | container.appendChild(gui.domElement); |
| | gui.add(controls, 'rotation').name('Auto Rotate'); |
| | gui.add(controls, 'elevation', 0.5, 2).name('Terrain Height'); |
| | |
| | function animate() { |
| | requestAnimationFrame(animate); |
| | if (controls.rotation) { |
| | pakistan.rotation.y += 0.005; |
| | } |
| | pakistan.scale.z = controls.elevation; |
| | renderer.render(scene, camera); |
| | } |
| | animate(); |
| | |
| | // Handle window resize |
| | window.addEventListener('resize', () => { |
| | camera.aspect = container.clientWidth / container.clientHeight; |
| | camera.updateProjectionMatrix(); |
| | renderer.setSize(container.clientWidth, container.clientHeight); |
| | }); |
| | </script> |
| | """ |
| | components.html(threejs_html, height=600) |
| |
|
| | |
| | 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) |
| | |
| | |
| |
|
| | |
| | |
| | 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() |
| | |
| | |
| | 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_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:] |
| | |
| | |
| | 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) |
| | |
| | |
| | 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: |
| | |
| | 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: |
| | |
| | fig = px.histogram( |
| | city_data['hourly'], |
| | x='temperature', |
| | nbins=30, |
| | title='Temperature Distribution' |
| | ) |
| | st.plotly_chart(fig, use_container_width=True) |
| | |
| | with tabs[1]: |
| | |
| | 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: |
| | |
| | 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]: |
| | |
| | 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]: |
| | |
| | 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: |
| | |
| | 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") |
| | |
| | |
| | features_df = data_collector.create_ml_features(weather_data) |
| | model, X_test, y_test = train_weather_model(features_df, selected_city) |
| | |
| | |
| | predictions = model.predict(X_test) |
| | |
| | |
| | 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) |
| | |
| | |
| | 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: |
| | |
| | st.subheader("3D Terrain Analysis") |
| | create_3d_visualization(earthquake_data) |
| | |
| | |
| | st.subheader("Earthquake Analysis Dashboard") |
| | |
| | |
| | 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: |
| | |
| | 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: |
| | |
| | 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) |
| | |
| | |
| | st.subheader("Temporal Analysis") |
| | eq_df |
| | 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) |
| | |
| | |
| | st.subheader("Seismic Risk Assessment") |
| | risk_zones = folium.Map(location=[30.3753, 69.3451], zoom_start=5) |
| | |
| | |
| | heat_data = [[row['lat'], row['lon'], row['magnitude']] for _, row in eq_df.iterrows()] |
| | folium.plugins.HeatMap(heat_data).add_to(risk_zones) |
| | |
| | |
| | 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) |
| | |
| | |
| | st.subheader("Seismic Activity Prediction") |
| | |
| | |
| | daily_counts['ds'] = pd.to_datetime(daily_counts['date']) |
| | daily_counts['y'] = daily_counts['count'] |
| | |
| | |
| | model = Prophet(yearly_seasonality=True, weekly_seasonality=True) |
| | model.fit(daily_counts[['ds', 'y']]) |
| | |
| | |
| | future_dates = model.make_future_dataframe(periods=30) |
| | forecast = model.predict(future_dates) |
| | |
| | |
| | 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() |
| | |
| | |
| | st.markdown(download_csv(city_data, f"{selected_city}_air_quality_data"), unsafe_allow_html=True) |
| | |
| | |
| | weights = { |
| | 'PM2.5': 0.3, |
| | 'PM10': 0.2, |
| | 'NO2': 0.2, |
| | 'O3': 0.2, |
| | 'CO': 0.1 |
| | } |
| | |
| | |
| | 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 = 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: |
| | |
| | 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]: |
| | |
| | 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) |
| | |
| | |
| | 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]: |
| | |
| | from statsmodels.tsa.seasonal import seasonal_decompose |
| | |
| | |
| | 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") |
| | |
| | |
| | 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] |
| | } |
| | |
| | |
| | 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} |
| | |
| | |
| | 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: |
| | |
| | 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") |
| | |
| | |
| | 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() |
| | |
| | |
| | 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}" |
| | ) |
| | |
| | |
| | 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(""" |
| | <div style='text-align: center'> |
| | |
| | <p>Created by Muhammad Shaheer</p> |
| | |
| | </div> |
| | """, unsafe_allow_html=True) |
| |
|
| | if __name__ == "__main__": |
| | main() |