File size: 6,059 Bytes
448e634
b359d50
4fcf8ef
 
 
ff71114
4fcf8ef
b359d50
4fcf8ef
 
 
b359d50
 
4fcf8ef
 
 
2eccf6c
4fcf8ef
2eccf6c
b359d50
4fcf8ef
2eccf6c
b359d50
 
2eccf6c
 
 
 
4fcf8ef
 
 
 
 
 
 
 
2eccf6c
4fcf8ef
 
 
 
 
 
 
2eccf6c
4fcf8ef
 
 
 
2eccf6c
4fcf8ef
86764f2
2eccf6c
 
6becbe6
4fcf8ef
 
2eccf6c
 
6becbe6
4fcf8ef
 
 
2eccf6c
4fcf8ef
86764f2
4fcf8ef
 
 
 
 
 
 
 
 
 
2eccf6c
4fcf8ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2eccf6c
 
4fcf8ef
 
 
2eccf6c
 
 
 
 
 
 
 
 
4fcf8ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86764f2
2eccf6c
4fcf8ef
 
 
 
 
 
 
 
 
 
 
 
2013310
4fcf8ef
 
2eccf6c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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
@st.cache_data
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

@st.cache_data
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.")