File size: 6,210 Bytes
65e9345
4d6fe89
 
d34efc0
4d6fe89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d34efc0
 
6709c71
d34efc0
 
6709c71
 
 
d34efc0
 
 
 
 
 
 
 
6709c71
 
 
d34efc0
 
 
4d6fe89
 
 
 
 
 
d34efc0
4d6fe89
 
 
d34efc0
 
4d6fe89
 
d34efc0
 
 
 
 
 
 
 
 
ee7277b
d34efc0
ee7277b
 
4d6fe89
 
d34efc0
4d6fe89
 
d34efc0
4d6fe89
 
 
 
 
 
 
d34efc0
4d6fe89
 
 
 
 
 
 
d34efc0
4d6fe89
 
 
 
 
 
 
 
 
 
 
 
d34efc0
4d6fe89
 
 
 
 
 
65e9345
d34efc0
4d6fe89
 
 
 
 
 
d34efc0
4d6fe89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d34efc0
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
import streamlit as st
from obspy import UTCDateTime
import pandas as pd
from datetime import datetime, timedelta

# 匯入我們自己寫的模組
import earthquake_fetcher
import earthquake_analyzer
import visualizer

# --- 頁面設定 ---
st.set_page_config(
    page_title="即時地震監測儀表板",
    page_icon="🌊",
    layout="wide"
)

st.title("即時地震監測與定位儀表板")
st.markdown("""
本應用程式從 [IRIS](http://ds.iris.edu/ds/nodes/dmc/) FDSN 服務獲取台灣 (TW) 測站的即時地震波形資料,
自動偵測 P 波並使用 Geiger 方法定位震源。
""")

# --- 側邊欄控制項 (FR6) ---
with st.sidebar:
    st.header("⚙️ 分析參數設定")

    # --- START OF CHANGE ---
    # FR1: 時間範圍設定 (使用固定的預設值,並增加提示)
    st.subheader("時間範圍設定")

    # 設定一個固定的、可說明的預設時間範圍,避免使用者誤解
    default_start_datetime = datetime(2025, 7, 20, 7, 0, 0)
    default_end_datetime = datetime(2025, 7, 20, 7, 15, 0)

    col1, col2 = st.columns(2)
    with col1:
        start_date = st.date_input("起始日期", value=default_start_datetime.date())
        start_time_input = st.time_input("起始時間", value=default_start_datetime.time())
    with col2:
        end_date = st.date_input("終止日期", value=default_end_datetime.date())
        end_time_input = st.time_input("終止時間", value=default_end_datetime.time())
    
    # 增加提示文字,確保使用者了解其靈活性
    st.caption("提示:您可以自由調整上方的日期與時間,查詢任何時間範圍的資料。")
    # --- END OF CHANGE ---

    # FR2: STA/LTA 參數設定
    st.subheader("P波撿拾 (STA/LTA) 參數")
    sta_window = st.slider("STA 窗長 (秒)", min_value=0.1, max_value=5.0, value=1.0, step=0.1)
    lta_window = st.slider("LTA 窗長 (秒)", min_value=5.0, max_value=60.0, value=10.0, step=1.0)
    trigger_threshold = st.slider("觸發門檻值", min_value=1.5, max_value=10.0, value=3.0, step=0.5)
    off_threshold = st.slider("關閉門檻值", min_value=0.1, max_value=2.0, value=1.5, step=0.1)

    # 初始化 session state
    if 'analysis_results' not in st.session_state:
        st.session_state.analysis_results = None

    # 執行按鈕
    if st.button("獲取指定範圍地震資料並分析", type="primary"):
        with st.spinner("正在執行分析...請稍候..."):
            
            # 將日期和時間輸入組合成 datetime 物件
            start_datetime_local = datetime.combine(start_date, start_time_input)
            end_datetime_local = datetime.combine(end_date, end_time_input)

            # 轉換為 obspy 的 UTCDateTime 物件
            start_time = UTCDateTime(start_datetime_local)
            end_time = UTCDateTime(end_datetime_local)

            # 為了快取,將 UTCDateTime 物件轉為字串傳遞
            stream, inventory = earthquake_fetcher.get_earthquake_data(
                start_time_str=str(start_time),
                end_time_str=str(end_time)
            )
            
            if stream and inventory:
                # 2. P波自動撿拾 (FR2)
                p_picks = earthquake_analyzer.pick_p_waves(stream, sta_window, lta_window, trigger_threshold, off_threshold)
                
                # 4. 震源定位 (FR4)
                location_result = None
                if len(p_picks) >= 4:
                    st.write(f"找到 {len(p_picks)} 個 P 波觸發,開始定位...")
                    location_result = earthquake_analyzer.locate_hypocenter(p_picks, inventory)
                else:
                    st.write(f"P 波觸發測站數 ({len(p_picks)}) 不足 4 個,無法進行定位。")
                
                # 將所有結果存儲到 session state
                st.session_state.analysis_results = {
                    "stream": stream,
                    "inventory": inventory,
                    "p_picks": p_picks,
                    "location_result": location_result
                }
            else:
                 st.session_state.analysis_results = None # 清除舊結果
        
        st.success("分析完成!")

# --- 主畫面結果展示 ---
if st.session_state.analysis_results:
    results = st.session_state.analysis_results
    p_picks = results["p_picks"]
    location_result = results["location_result"]
    inventory = results["inventory"]

    st.header("📊 分析結果")

    # 3. 波形視覺化 (FR3)
    if p_picks:
        st.subheader("測站波形與 P 波初達")
        waveform_fig = visualizer.plot_waveforms_with_picks(p_picks)
        st.plotly_chart(waveform_fig, use_container_width=True)
    else:
        st.info("在所有獲取的波形中,均未成功觸發 P 波。請嘗試調整 STA/LTA 參數或擴大時間範圍。")

    # 5. 定位結果視覺化 (FR5)
    st.subheader("震源定位結果")
    if len(p_picks) < 4:
        st.warning("P 波撿拾測站不足(少於4個),無法進行定位。")
    elif not location_result:
        st.error("定位計算失敗,可能是測站分佈不佳或資料品質問題。")
    else:
        col1, col2 = st.columns([2, 1]) # 地圖佔2/3,文字佔1/3
        with col1:
            st.pydeck_chart(visualizer.plot_event_map(inventory, p_picks, location_result))
        
        with col2:
            st.markdown("#### **定位資訊**")
            origin_utc = UTCDateTime(location_result['origin_time'])
            st.metric(label="發震時刻 (UTC)", value=origin_utc.strftime("%Y-%m-%d %H:%M:%S"))
            st.metric(label="震央位置", value=f"{location_result['latitude']:.3f}°N, {location_result['longitude']:.3f}°E")
            st.metric(label="深度", value=f"{location_result['depth_km']:.1f} km")
            st.metric(label="使用測站數", value=len(p_picks))
            
            st.markdown("---")
            st.markdown("#### **參與定位測站**")
            station_list = [p['station'] for p in p_picks]
            st.text(", ".join(station_list))
else:
    st.info("請在左側設定時間範圍與參數,然後點擊按鈕以開始分析。")