Spaces:
Sleeping
Sleeping
import streamlit as st | |
import pandas as pd | |
from obspy import UTCDateTime | |
from obspy.clients.fdsn import Client | |
import matplotlib.pyplot as plt | |
# --- App Configuration --- | |
st.set_page_config( | |
page_title="Taiwan Earthquake Explorer πΉπΌ", | |
page_icon="π", | |
layout="wide" | |
) | |
# --- FDSN Client & Caching --- | |
client = Client("IRIS") | |
# CORRECTED FUNCTION | |
def search_earthquakes(start_time_str, end_time_str, min_mag, min_lat, max_lat, min_lon, max_lon): | |
""" | |
Searches for earthquake events using the ObsPy client. | |
Accepts time as strings to enable caching. | |
""" | |
try: | |
# Convert string arguments back to UTCDateTime objects inside the function | |
start_time = UTCDateTime(start_time_str) | |
end_time = UTCDateTime(end_time_str) | |
catalog = client.get_events( | |
starttime=start_time, | |
endtime=end_time, | |
minlatitude=min_lat, | |
maxlatitude=max_lat, | |
minlongitude=min_lon, | |
maxlongitude=max_lon, | |
minmagnitude=min_mag, | |
orderby="time-asc" | |
) | |
return catalog | |
except Exception as e: | |
st.error(f"Could not retrieve event data: {e}") | |
return None | |
def get_waveforms_for_event(_event): | |
""" | |
Retrieves and processes seismic waveforms for a given event. | |
_event is a tuple of event properties to make it hashable for caching. | |
""" | |
event_time_str, _, _, _ = _event | |
event_time = UTCDateTime(event_time_str) | |
t_start = event_time - 30 | |
t_end = event_time + 5 * 60 | |
try: | |
stream = client.get_waveforms( | |
network="TW", station="*", location="*", channel="BH*", | |
starttime=t_start, endtime=t_end, attach_response=True | |
) | |
if len(stream) > 0: | |
stream.detrend("linear") | |
stream.taper(max_percentage=0.05, type="cosine") | |
stream.remove_response(output="VEL") | |
return stream | |
else: | |
return None | |
except Exception: | |
return None | |
# --- Streamlit User Interface --- | |
st.title("π Taiwan Earthquake Explorer") | |
st.markdown("Search, map, and visualize seismic data from the TW network via IRIS FDSN.") | |
st.sidebar.header("π Search Parameters") | |
# --- Sidebar Controls --- | |
default_start = UTCDateTime("2024-04-02T23:00:00") | |
default_end = UTCDateTime("2024-04-03T01:00:00") | |
start_date = st.sidebar.date_input("Start Date", value=default_start.date) | |
start_time_str = st.sidebar.text_input("Start Time (UTC)", value=default_start.strftime("%H:%M:%S")) | |
end_date = st.sidebar.date_input("End Date", value=default_end.date) | |
end_time_str = st.sidebar.text_input("End Time (UTC)", value=default_end.strftime("%H:%M:%S")) | |
start_utc = UTCDateTime(f"{start_date}T{start_time_str}") | |
end_utc = UTCDateTime(f"{end_date}T{end_time_str}") | |
st.sidebar.markdown("---") | |
min_mag = st.sidebar.slider("Minimum Magnitude", 2.0, 8.0, 5.5) | |
st.sidebar.markdown("---") | |
st.sidebar.subheader("Geographical Region (Taiwan)") | |
min_lat = st.sidebar.number_input("Min Latitude", value=21.5, format="%.2f") | |
max_lat = st.sidebar.number_input("Max Latitude", value=25.5, format="%.2f") | |
min_lon = st.sidebar.number_input("Min Longitude", value=120.0, format="%.2f") | |
max_lon = st.sidebar.number_input("Max Longitude", value=122.5, format="%.2f") | |
# --- Main App Logic --- | |
if st.sidebar.button("Search for Earthquakes", type="primary"): | |
# CORRECTED FUNCTION CALL: Pass times as ISO strings | |
catalog = search_earthquakes(start_utc.isoformat(), end_utc.isoformat(), min_mag, min_lat, max_lat, min_lon, max_lon) | |
if catalog and len(catalog) > 0: | |
st.success(f"β Found {len(catalog)} earthquake(s).") | |
event_data = [{ | |
"Time (UTC)": (event.preferred_origin() or event.origins[0]).time.strftime('%Y-%m-%d %H:%M:%S'), | |
"Latitude": (event.preferred_origin() or event.origins[0]).latitude, | |
"Longitude": (event.preferred_origin() or event.origins[0]).longitude, | |
"Depth (km)": (event.preferred_origin() or event.origins[0]).depth / 1000.0, | |
"Magnitude": (event.preferred_magnitude() or event.magnitudes[0]).mag, | |
"Mag Type": (event.preferred_magnitude() or event.magnitudes[0]).magnitude_type | |
} for event in catalog] | |
event_df = pd.DataFrame(event_data) | |
st.subheader("πΊοΈ Earthquake Map") | |
st.map(event_df, latitude='Latitude', longitude='Longitude', size='Magnitude', zoom=6) | |
st.subheader("π Earthquake Catalog Table") | |
st.dataframe(event_df) | |
st.markdown("---") | |
st.subheader(" seismograph Seismic Waveform Viewer") | |
event_options = {f"{row['Time (UTC)']} - Mag: {row['Magnitude']:.1f}": index for index, row in event_df.iterrows()} | |
selected_event_str = st.selectbox("Select an event to view waveforms:", options=event_options.keys()) | |
if selected_event_str: | |
selected_index = event_options[selected_event_str] | |
selected_event = catalog[selected_index] | |
origin = selected_event.preferred_origin() or selected_event.origins[0] | |
event_tuple = (str(origin.time), origin.latitude, origin.longitude, origin.depth) | |
with st.spinner("Fetching waveforms from TW network... This may take a moment."): | |
waveforms = get_waveforms_for_event(event_tuple) | |
if waveforms and len(waveforms) > 0: | |
st.success(f"π Found and processed {len(waveforms)} waveform traces.") | |
fig, ax = plt.subplots(figsize=(12, len(waveforms) * 1.5)) | |
waveforms.plot(fig=fig, handle=True) | |
ax.set_title(f"Seismic Waveforms for Event: {selected_event_str}") | |
st.pyplot(fig) | |
else: | |
st.warning("Could not retrieve any waveform data for the selected event from the TW network.") | |
else: | |
st.warning("No earthquakes found matching your criteria. Try expanding the search window or lowering the magnitude.") | |