streamlit / src /streamlit_app.py
cwadayi's picture
Update src/streamlit_app.py
b47d08c verified
import streamlit as st
import pandas as pd
import plotly.express as px
from datetime import date, timedelta
# Import ObsPy for live data fetching
import obspy
from obspy.clients.fdsn import Client
from obspy import UTCDateTime
# --- Page Configuration ---
st.set_page_config(
page_title="Live Earthquake Explorer (ObsPy)",
page_icon="πŸ›°οΈ",
layout="wide",
initial_sidebar_state="expanded"
)
# --- Live Data Fetching Function using ObsPy ---
@st.cache_data(ttl=900) # Cache the data for 15 minutes
def fetch_live_data(start_date, end_date, min_mag):
"""
Fetches live earthquake data from the IRIS FDSN web service using ObsPy.
"""
client = Client("IRIS")
start_time = UTCDateTime(start_date)
end_time = UTCDateTime(end_date) + timedelta(days=1)
try:
catalog = client.get_events(
starttime=start_time,
endtime=end_time,
minmagnitude=min_mag
)
except Exception as e:
st.error(f"Could not retrieve data from IRIS FDSN service: {e}")
return pd.DataFrame()
event_list = []
for event in catalog:
if not (event.preferred_origin() and event.preferred_magnitude()):
continue
origin = event.preferred_origin()
magnitude = event.preferred_magnitude()
# Get location description safely.
description = "N/A"
if hasattr(event, 'descriptions'):
if event.descriptions and event.descriptions[0].text:
description = event.descriptions[0].text
event_list.append({
"latitude": origin.latitude,
"longitude": origin.longitude,
"depth": origin.depth / 1000,
"magnitude": magnitude.mag,
"time": origin.time.datetime,
"place": description,
})
if not event_list:
return pd.DataFrame()
return pd.DataFrame(event_list)
# --- UI Design ---
st.title("πŸ›°οΈ Live Earthquake Explorer with ObsPy")
st.markdown("Data is fetched in real-time from the **IRIS FDSN** web service.")
with st.sidebar:
st.header("πŸ” Search Parameters")
today = date.today()
thirty_days_ago = today - timedelta(days=30)
start_date = st.date_input("Start Date", value=thirty_days_ago)
end_date = st.date_input("End Date", value=today)
min_magnitude = st.slider("Minimum Magnitude", 0.0, 10.0, 5.0, 0.1)
search_button = st.button("Search Earthquakes", type="primary", use_container_width=True)
# --- Main Content Area ---
if search_button:
if start_date > end_date:
st.error("Error: Start date cannot be after end date.")
else:
with st.spinner(f"Fetching earthquakes from {start_date} to {end_date}..."):
df = fetch_live_data(start_date, end_date, min_magnitude)
if not df.empty:
st.success(f"Found {len(df)} earthquakes matching your criteria.")
st.subheader("πŸ“Š Summary Statistics")
col1, col2, col3 = st.columns(3)
col1.metric("Total Earthquakes", f"{len(df):,}")
col2.metric("Largest Magnitude", f"{df['magnitude'].max():.1f}")
col3.metric("Deepest Event (km)", f"{df['depth'].max():.1f}")
st.markdown("---")
st.subheader("🌍 Interactive Earthquake Map")
st.markdown("Hover over points for details. Circle size represents magnitude.")
fig = px.scatter_geo(
df,
lat='latitude',
lon='longitude',
size='magnitude',
color='depth',
hover_name='place',
hover_data={'latitude': ':.2f', 'longitude': ':.2f', 'time': '|%Y-%m-%d %H:%M', 'magnitude': ':.1f', 'depth': ':.1f km'},
projection="natural earth",
title=f"Live Earthquakes from {start_date} to {end_date} (Magnitude > {min_magnitude})",
color_continuous_scale=px.colors.sequential.Plasma_r
)
fig.update_layout(margin={"r":0,"t":40,"l":0,"b":0}, coloraxis_colorbar_title_text='Depth (km)')
st.plotly_chart(fig, use_container_width=True)
# --- Show Earthquakes in a Table ---
st.subheader("πŸ“‹ Detailed Earthquake Data")
# This line is changed to show latitude and longitude instead of place.
display_df = df[['time', 'latitude', 'longitude', 'magnitude', 'depth']].copy()
st.dataframe(
display_df.sort_values(by='time', ascending=False),
use_container_width=True,
height=400,
column_config={"time": st.column_config.DatetimeColumn("Time (UTC)", format="YYYY-MM-DD HH:mm")}
)
else:
st.warning("No earthquakes were found for the specified criteria. Try expanding the date range or lowering the magnitude.")
else:
st.info("Set your desired parameters in the sidebar and click 'Search Earthquakes' to begin.")