display earthquake information
To upload files, please first save the app
import streamlit as st
import pandas as pd
import requests
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
st.set_page_config(page_title="Earthquake Information Dashboard", page_icon="π", layout="wide")
st.title("π Global Earthquake Information Dashboard")
st.markdown("Real-time earthquake data from the USGS Earthquake Catalog")
@st.cache_data(ttl=300) # Cache for 5 minutes
def fetch_earthquake_data(timeframe="day"):
"""Fetch earthquake data from USGS API"""
try:
# NOTE: Using corsproxy.io because we're in a WASM environment. If running locally,
# you can remove the corsproxy.io prefix. Some websites don't work with the proxy,
# in those cases try removing the proxy prefix.
base_url = "https://corsproxy.io/?https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/"
timeframe_options = {
"hour": "all_hour.geojson",
"day": "all_day.geojson",
"week": "all_week.geojson",
"month": "all_month.geojson"
}
url = base_url + timeframe_options.get(timeframe, "all_day.geojson")
response = requests.get(url)
if response.status_code == 200:
data = response.json()
earthquakes = []
for feature in data['features']:
props = feature['properties']
coords = feature['geometry']['coordinates']
earthquakes.append({
'id': props.get('ids', ''),
'magnitude': props.get('mag'),
'place': props.get('place', 'Unknown'),
'time': datetime.fromtimestamp(props.get('time', 0) / 1000),
'updated': datetime.fromtimestamp(props.get('updated', 0) / 1000),
'longitude': coords[0] if len(coords) > 0 else None,
'latitude': coords[1] if len(coords) > 1 else None,
'depth': coords[2] if len(coords) > 2 else None,
'url': props.get('url', ''),
'detail': props.get('detail', ''),
'felt': props.get('felt'),
'cdi': props.get('cdi'),
'mmi': props.get('mmi'),
'alert': props.get('alert'),
'status': props.get('status', ''),
'tsunami': props.get('tsunami', 0),
'sig': props.get('sig'),
'net': props.get('net', ''),
'code': props.get('code', ''),
'ids': props.get('ids', ''),
'sources': props.get('sources', ''),
'types': props.get('types', ''),
'nst': props.get('nst'),
'dmin': props.get('dmin'),
'rms': props.get('rms'),
'gap': props.get('gap'),
'magType': props.get('magType', ''),
'type': props.get('type', '')
})
return pd.DataFrame(earthquakes)
else:
st.error(f"Failed to fetch data: HTTP {response.status_code}")
return pd.DataFrame()
except Exception as e:
st.error(f"Error fetching earthquake data: {str(e)}")
return pd.DataFrame()
# Sidebar controls
st.sidebar.header("βοΈ Settings")
timeframe = st.sidebar.selectbox(
"Select timeframe:",
options=["hour", "day", "week", "month"],
index=1,
help="Choose the time period for earthquake data"
)
min_magnitude = st.sidebar.slider(
"Minimum magnitude:",
min_value=0.0,
max_value=10.0,
value=2.5,
step=0.1,
help="Filter earthquakes by minimum magnitude"
)
# Fetch data
with st.spinner("Loading earthquake data..."):
df = fetch_earthquake_data(timeframe)
if df.empty:
st.error("No earthquake data available. Please try again later.")
st.stop()
# Filter data
df_filtered = df[df['magnitude'] >= min_magnitude].copy()
# Main content
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Earthquakes", len(df_filtered))
with col2:
if not df_filtered.empty:
st.metric("Largest Magnitude", f"{df_filtered['magnitude'].max():.1f}")
else:
st.metric("Largest Magnitude", "N/A")
with col3:
if not df_filtered.empty:
st.metric("Average Magnitude", f"{df_filtered['magnitude'].mean():.1f}")
else:
st.metric("Average Magnitude", "N/A")
with col4:
tsunami_count = len(df_filtered[df_filtered['tsunami'] == 1])
st.metric("Tsunami Warnings", tsunami_count)
# Tabs for different views
tab1, tab2, tab3, tab4 = st.tabs(["πΊοΈ Map View", "π Analysis", "π Data Table", "β οΈ Alerts"])
with tab1:
st.subheader("Global Earthquake Map")
if not df_filtered.empty:
# Create map
fig = px.scatter_mapbox(
df_filtered,
lat='latitude',
lon='longitude',
size='magnitude',
color='magnitude',
hover_name='place',
hover_data={
'magnitude': ':.1f',
'depth': ':.1f',
'time': '|%Y-%m-%d %H:%M:%S',
'latitude': ':.2f',
'longitude': ':.2f'
},
color_continuous_scale='Reds',
size_max=20,
zoom=1,
height=600,
title=f"Earthquakes (Magnitude β₯ {min_magnitude}) - Last {timeframe.title()}"
)
fig.update_layout(
mapbox_style="open-street-map",
margin={"r":0,"t":50,"l":0,"b":0}
)
st.plotly_chart(fig, use_container_width=True)
else:
st.info("No earthquakes found matching the selected criteria.")
with tab2:
st.subheader("Earthquake Analysis")
if not df_filtered.empty:
col1, col2 = st.columns(2)
with col1:
# Magnitude distribution
fig_hist = px.histogram(
df_filtered,
x='magnitude',
nbins=20,
title="Magnitude Distribution",
labels={'magnitude': 'Magnitude', 'count': 'Number of Earthquakes'}
)
st.plotly_chart(fig_hist, use_container_width=True)
with col2:
# Depth distribution
fig_depth = px.histogram(
df_filtered,
x='depth',
nbins=20,
title="Depth Distribution",
labels={'depth': 'Depth (km)', 'count': 'Number of Earthquakes'}
)
st.plotly_chart(fig_depth, use_container_width=True)
# Time series
if len(df_filtered) > 1:
df_time = df_filtered.copy()
df_time['hour'] = df_time['time'].dt.hour
hourly_counts = df_time.groupby('hour').size().reset_index(name='count')
fig_time = px.bar(
hourly_counts,
x='hour',
y='count',
title="Earthquakes by Hour of Day",
labels={'hour': 'Hour of Day', 'count': 'Number of Earthquakes'}
)
st.plotly_chart(fig_time, use_container_width=True)
else:
st.info("No data available for analysis with current filters.")
with tab3:
st.subheader("Earthquake Data Table")
if not df_filtered.empty:
# Display options
col1, col2 = st.columns(2)
with col1:
show_columns = st.multiselect(
"Select columns to display:",
options=['magnitude', 'place', 'time', 'depth', 'latitude', 'longitude', 'alert', 'tsunami'],
default=['magnitude', 'place', 'time', 'depth']
)
with col2:
sort_by = st.selectbox(
"Sort by:",
options=['magnitude', 'time', 'depth'],
index=0
)
if show_columns:
display_df = df_filtered[show_columns].copy()
display_df = display_df.sort_values(sort_by, ascending=False)
# Format time column if present
if 'time' in display_df.columns:
display_df['time'] = display_df['time'].dt.strftime('%Y-%m-%d %H:%M:%S')
st.dataframe(
display_df,
use_container_width=True,
hide_index=True
)
# Download button
csv = display_df.to_csv(index=False)
st.download_button(
label="π₯ Download as CSV",
data=csv,
file_name=f"earthquakes_{timeframe}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
mime="text/csv"
)
else:
st.info("No data to display with current filters.")
with tab4:
st.subheader("Alert Status & Significant Events")
if not df_filtered.empty:
# High magnitude earthquakes
high_mag = df_filtered[df_filtered['magnitude'] >= 6.0]
if not high_mag.empty:
st.error("π¨ **High Magnitude Earthquakes (β₯6.0):**")
for _, row in high_mag.iterrows():
st.write(f"**M{row['magnitude']:.1f}** - {row['place']} - {row['time'].strftime('%Y-%m-%d %H:%M:%S')}")
# Tsunami warnings
tsunami_events = df_filtered[df_filtered['tsunami'] == 1]
if not tsunami_events.empty:
st.warning("π **Tsunami Warnings:**")
for _, row in tsunami_events.iterrows():
st.write(f"**M{row['magnitude']:.1f}** - {row['place']} - {row['time'].strftime('%Y-%m-%d %H:%M:%S')}")
# Recent significant earthquakes
significant = df_filtered[df_filtered['sig'] >= 600] # USGS significance threshold
if not significant.empty:
st.info("β‘ **Significant Earthquakes:**")
for _, row in significant.head(10).iterrows():
significance = row['sig'] if pd.notna(row['sig']) else 'N/A'
st.write(f"**M{row['magnitude']:.1f}** - {row['place']} - Significance: {significance}")
if high_mag.empty and tsunami_events.empty and significant.empty:
st.success("β
No major alerts or significant events in the selected timeframe.")
else:
st.info("No alert data available with current filters.")
# Footer
st.markdown("---")
st.markdown("""
**Data Source:** [USGS Earthquake Hazards Program](https://earthquake.usgs.gov/)
**Update Frequency:** Real-time (data cached for 5 minutes)
**Note:** Earthquake data is provided by the United States Geological Survey (USGS) and is updated continuously.
""")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?