|
import streamlit as st |
|
import pandas as pd |
|
import plotly.express as px |
|
from datetime import date, timedelta |
|
|
|
|
|
import obspy |
|
from obspy.clients.fdsn import Client |
|
from obspy import UTCDateTime |
|
|
|
|
|
st.set_page_config( |
|
page_title="Live Earthquake Explorer (ObsPy)", |
|
page_icon="π°οΈ", |
|
layout="wide", |
|
initial_sidebar_state="expanded" |
|
) |
|
|
|
|
|
@st.cache_data(ttl=900) |
|
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() |
|
|
|
|
|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
st.subheader("π Detailed Earthquake Data") |
|
|
|
|
|
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.") |