Spaces:
Sleeping
Sleeping
Commit
·
d03ae4c
1
Parent(s):
23eb3ea
docs: refactor waveform loading and processing steps for improved caching and error handling
Browse files
app.py
CHANGED
|
@@ -806,112 +806,116 @@ def load_observed_intensity_image(event_name):
|
|
| 806 |
return None
|
| 807 |
|
| 808 |
|
| 809 |
-
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
|
| 814 |
-
|
| 815 |
"""
|
| 816 |
try:
|
| 817 |
-
# 從全域 earthquake_metadata 讀取震央座標
|
| 818 |
epicenter_lat = earthquake_metadata[event_name]["epicenter_lat"]
|
| 819 |
epicenter_lon = earthquake_metadata[event_name]["epicenter_lon"]
|
| 820 |
mseed_file = earthquake_metadata[event_name]["mseed_file"]
|
| 821 |
-
first_pick = earthquake_metadata[event_name]["first_pick"]
|
| 822 |
-
|
| 823 |
-
# 計算結束時間(用於顯示資訊)
|
| 824 |
|
| 825 |
-
|
| 826 |
-
# 1. 載入完整的 mseed 檔案
|
| 827 |
-
logger.info(f"載入地震事件: {event_name}")
|
| 828 |
st = read(mseed_file)
|
| 829 |
logger.info(f"載入了 {len(st)} 個 trace")
|
| 830 |
|
| 831 |
-
#
|
| 832 |
logger.info(f"選擇距離震央 ({epicenter_lat}, {epicenter_lon}) 最近的測站...")
|
| 833 |
selected_stations = select_nearest_stations(
|
| 834 |
st, epicenter_lat, epicenter_lon, n_stations=25
|
| 835 |
)
|
| 836 |
|
| 837 |
if len(selected_stations) == 0:
|
| 838 |
-
|
| 839 |
-
|
| 840 |
-
None,
|
| 841 |
-
"錯誤:找不到有效的測站資料",
|
| 842 |
-
gr.update(interactive=False),
|
| 843 |
-
)
|
| 844 |
-
|
| 845 |
-
waveform_plot = plot_waveform(st, selected_stations, first_pick, duration)
|
| 846 |
|
| 847 |
-
#
|
| 848 |
station_map = create_input_station_map(
|
| 849 |
selected_stations, epicenter_lat, epicenter_lon
|
| 850 |
)
|
| 851 |
|
| 852 |
-
logger.info("
|
| 853 |
-
return station_map,
|
| 854 |
|
| 855 |
except Exception as e:
|
| 856 |
-
logger.error(f"
|
| 857 |
import traceback
|
| 858 |
-
|
| 859 |
traceback.print_exc()
|
| 860 |
-
return None, None, gr.update(interactive=False)
|
| 861 |
|
| 862 |
|
| 863 |
-
|
|
|
|
| 864 |
"""
|
| 865 |
-
|
| 866 |
-
|
| 867 |
-
介面改以 start+duration,使用固定高度 800 的地圖輸出
|
| 868 |
-
從全域 earthquake_metadata 讀取震央座標
|
| 869 |
|
| 870 |
-
|
| 871 |
-
|
| 872 |
"""
|
| 873 |
try:
|
| 874 |
-
|
| 875 |
-
|
| 876 |
-
|
| 877 |
-
mseed_file = earthquake_metadata[event_name]["mseed_file"]
|
| 878 |
-
|
| 879 |
-
|
| 880 |
-
# 1. 載入完整的 mseed 檔案
|
| 881 |
-
logger.info(f"載入地震事件: {event_name}")
|
| 882 |
-
st = read(mseed_file)
|
| 883 |
-
logger.info(f"載入了 {len(st)} 個 trace")
|
| 884 |
|
| 885 |
-
|
| 886 |
-
logger.info(f"選擇距離震央 ({epicenter_lat}, {epicenter_lon}) 最近的測站...")
|
| 887 |
-
selected_stations = select_nearest_stations(
|
| 888 |
-
st, epicenter_lat, epicenter_lon, n_stations=25
|
| 889 |
-
)
|
| 890 |
|
| 891 |
-
|
| 892 |
-
return None, None, "錯誤:找不到有效的測站資料"
|
| 893 |
|
| 894 |
-
#
|
| 895 |
-
logger.info(
|
| 896 |
-
f"提取波形資料(時間範圍: 初動��� {duration} 秒)..."
|
| 897 |
-
)
|
| 898 |
waveforms, station_info_list, valid_stations, missing_components_count = (
|
| 899 |
extract_waveforms_from_stream(
|
| 900 |
-
event_name,
|
| 901 |
)
|
| 902 |
)
|
| 903 |
|
| 904 |
if len(waveforms) == 0:
|
| 905 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 906 |
|
| 907 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 908 |
max_stations = 25
|
| 909 |
waveform_padded = np.zeros((max_stations, 3000, 3))
|
| 910 |
station_info_padded = np.zeros((max_stations, 4))
|
| 911 |
|
| 912 |
-
for i in range(min(len(
|
| 913 |
-
waveform_padded[i] =
|
| 914 |
-
station_info_padded[i] =
|
| 915 |
|
| 916 |
# 準備所有目標測站資訊(分批處理)
|
| 917 |
all_pga_list = []
|
|
@@ -956,14 +960,14 @@ def predict_intensity(event_name, duration):
|
|
| 956 |
for i in range(len(target_list)):
|
| 957 |
target_padded[i] = target_list[i]
|
| 958 |
|
| 959 |
-
#
|
| 960 |
tensor_data = {
|
| 961 |
"waveform": torch.tensor(waveform_padded).unsqueeze(0).double(),
|
| 962 |
"station": torch.tensor(station_info_padded).unsqueeze(0).double(),
|
| 963 |
"target": torch.tensor(target_padded).unsqueeze(0).double(),
|
| 964 |
}
|
| 965 |
|
| 966 |
-
#
|
| 967 |
with torch.no_grad():
|
| 968 |
weight, sigma, mu = model(tensor_data)
|
| 969 |
batch_pga = (
|
|
@@ -988,65 +992,20 @@ def predict_intensity(event_name, duration):
|
|
| 988 |
pga_list, target_names, epicenter_lat, epicenter_lon
|
| 989 |
)
|
| 990 |
|
| 991 |
-
#
|
| 992 |
observed_intensity_path = load_observed_intensity_image(event_name)
|
| 993 |
|
| 994 |
-
logger.info("預測完成!")
|
| 995 |
return observed_intensity_path, intensity_map
|
| 996 |
|
| 997 |
except Exception as e:
|
| 998 |
-
logger.error(f"
|
| 999 |
import traceback
|
| 1000 |
|
| 1001 |
traceback.print_exc()
|
| 1002 |
return None, None
|
| 1003 |
|
| 1004 |
|
| 1005 |
-
def on_full_workflow(event_name, duration):
|
| 1006 |
-
"""
|
| 1007 |
-
執行完整的工作流:波形載入 → 測站選擇 → 推論 → 結果展示
|
| 1008 |
-
|
| 1009 |
-
此函數用於首次應用加載與事件切換時自動執行完整流程
|
| 1010 |
-
|
| 1011 |
-
返回所有必要的 UI 組件輸出:
|
| 1012 |
-
(station_map, waveform_plot, predicted_map, observed_img)
|
| 1013 |
-
|
| 1014 |
-
spec #2:測站選擇上限 (25 站)、波形取樣率 (100 Hz)、時間窗長度 (30 秒)
|
| 1015 |
-
spec #3:推論流程、PGA → 震度轉換
|
| 1016 |
-
"""
|
| 1017 |
-
try:
|
| 1018 |
-
logger.info(f"[on_full_workflow] 開始執行完整工作流 - 事件: {event_name}")
|
| 1019 |
-
|
| 1020 |
-
# 步驟 1: 載入波形
|
| 1021 |
-
logger.info(f"[on_full_workflow] 步驟 1/3: 波形載入...")
|
| 1022 |
-
station_map, waveform_plot, _ = load_and_display_waveform(
|
| 1023 |
-
event_name, duration
|
| 1024 |
-
)
|
| 1025 |
-
|
| 1026 |
-
if station_map is None:
|
| 1027 |
-
logger.error("[on_full_workflow] 波形載入失敗")
|
| 1028 |
-
return None, None, None, None
|
| 1029 |
-
|
| 1030 |
-
# ��驟 2: 執行推論
|
| 1031 |
-
logger.info(f"[on_full_workflow] 步驟 2/3: 模型推論...")
|
| 1032 |
-
observed_img, predicted_map = predict_intensity(
|
| 1033 |
-
event_name, duration
|
| 1034 |
-
)
|
| 1035 |
-
|
| 1036 |
-
if predicted_map is None:
|
| 1037 |
-
logger.error("[on_full_workflow] 推論失敗")
|
| 1038 |
-
return station_map, waveform_plot, None, observed_img
|
| 1039 |
-
|
| 1040 |
-
logger.info(f"[on_full_workflow] 步驟 3/3: 完成")
|
| 1041 |
-
|
| 1042 |
-
return station_map, waveform_plot, predicted_map, observed_img
|
| 1043 |
-
|
| 1044 |
-
except Exception as e:
|
| 1045 |
-
logger.error(f"[on_full_workflow] 完整工作流發生錯誤: {e}")
|
| 1046 |
-
import traceback
|
| 1047 |
-
|
| 1048 |
-
traceback.print_exc()
|
| 1049 |
-
return None, None, f"錯誤: {str(e)}", None, f"錯誤: {str(e)}", None
|
| 1050 |
|
| 1051 |
# ============ Gradio 介面 ============
|
| 1052 |
with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
@@ -1059,12 +1018,11 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1059 |
gr.Markdown("## 使用步驟")
|
| 1060 |
gr.Markdown(
|
| 1061 |
"""
|
| 1062 |
-
1.
|
| 1063 |
-
2.
|
| 1064 |
-
3.
|
| 1065 |
-
4. 確認無誤後點擊執行預測
|
| 1066 |
|
| 1067 |
-
系統會自動選擇距離震央最近的 25
|
| 1068 |
"""
|
| 1069 |
)
|
| 1070 |
with gr.Column(scale=1):
|
|
@@ -1112,44 +1070,59 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1112 |
value=load_observed_intensity_image(list(earthquake_metadata.keys())[0]),
|
| 1113 |
)
|
| 1114 |
|
| 1115 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1116 |
event_dropdown.change(
|
| 1117 |
-
fn=
|
| 1118 |
-
|
| 1119 |
-
|
| 1120 |
-
|
| 1121 |
-
|
| 1122 |
-
|
| 1123 |
-
|
| 1124 |
-
|
| 1125 |
-
|
| 1126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1127 |
)
|
| 1128 |
|
|
|
|
| 1129 |
load_waveform_btn.click(
|
| 1130 |
-
fn=
|
| 1131 |
-
inputs=[event_dropdown
|
| 1132 |
-
outputs=[input_station_map, waveform_plot, predict_btn]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1133 |
)
|
| 1134 |
|
|
|
|
| 1135 |
predict_btn.click(
|
| 1136 |
-
fn=
|
| 1137 |
-
inputs=[
|
| 1138 |
-
outputs=[observed_intensity_image, predicted_intensity_map]
|
| 1139 |
)
|
| 1140 |
|
| 1141 |
-
#
|
| 1142 |
demo.load(
|
| 1143 |
-
fn=
|
| 1144 |
-
|
| 1145 |
-
|
| 1146 |
-
|
| 1147 |
-
|
| 1148 |
-
|
| 1149 |
-
|
| 1150 |
-
predicted_intensity_map,
|
| 1151 |
-
observed_intensity_image,
|
| 1152 |
-
],
|
| 1153 |
)
|
| 1154 |
|
| 1155 |
demo.launch()
|
|
|
|
| 806 |
return None
|
| 807 |
|
| 808 |
|
| 809 |
+
# ============ 步驟 1:載入 mseed + 選擇測站(快取到 gr.State)============
|
| 810 |
+
def step1_load_mseed_and_select_stations(event_name):
|
| 811 |
+
"""
|
| 812 |
+
步驟 1:載入 mseed 檔案並選擇最近的 25 個測站
|
| 813 |
|
| 814 |
+
這一步只執行一次(切換事件時),結果會快取在 gr.State 中
|
| 815 |
"""
|
| 816 |
try:
|
|
|
|
| 817 |
epicenter_lat = earthquake_metadata[event_name]["epicenter_lat"]
|
| 818 |
epicenter_lon = earthquake_metadata[event_name]["epicenter_lon"]
|
| 819 |
mseed_file = earthquake_metadata[event_name]["mseed_file"]
|
|
|
|
|
|
|
|
|
|
| 820 |
|
| 821 |
+
logger.info(f"[步驟 1] 載入地震事件: {event_name}")
|
|
|
|
|
|
|
| 822 |
st = read(mseed_file)
|
| 823 |
logger.info(f"載入了 {len(st)} 個 trace")
|
| 824 |
|
| 825 |
+
# 選擇距離震央最近的 25 個測站
|
| 826 |
logger.info(f"選擇距離震央 ({epicenter_lat}, {epicenter_lon}) 最近的測站...")
|
| 827 |
selected_stations = select_nearest_stations(
|
| 828 |
st, epicenter_lat, epicenter_lon, n_stations=25
|
| 829 |
)
|
| 830 |
|
| 831 |
if len(selected_stations) == 0:
|
| 832 |
+
logger.error("找不到有效的測站資料")
|
| 833 |
+
return None, None, None, None, gr.update(interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 834 |
|
| 835 |
+
# 創建輸入測站地圖
|
| 836 |
station_map = create_input_station_map(
|
| 837 |
selected_stations, epicenter_lat, epicenter_lon
|
| 838 |
)
|
| 839 |
|
| 840 |
+
logger.info("[步驟 1] 完成 - mseed 已載入,測站已選擇")
|
| 841 |
+
return st, selected_stations, station_map, None, gr.update(interactive=False)
|
| 842 |
|
| 843 |
except Exception as e:
|
| 844 |
+
logger.error(f"[步驟 1] 發生錯誤: {e}")
|
| 845 |
import traceback
|
|
|
|
| 846 |
traceback.print_exc()
|
| 847 |
+
return None, None, None, None, gr.update(interactive=False)
|
| 848 |
|
| 849 |
|
| 850 |
+
# ============ 步驟 2:提取波形(使用快取的 stream + stations)============
|
| 851 |
+
def step2_extract_and_plot_waveforms(cached_stream, cached_stations, event_name, duration):
|
| 852 |
"""
|
| 853 |
+
步驟 2:根據時間範圍提取波形並繪圖
|
|
|
|
|
|
|
|
|
|
| 854 |
|
| 855 |
+
使用快取的 stream 和 selected_stations,避免重複讀檔
|
| 856 |
+
用戶調整時間範圍時會重複執行此步驟
|
| 857 |
"""
|
| 858 |
try:
|
| 859 |
+
if cached_stream is None or cached_stations is None:
|
| 860 |
+
logger.warning("[步驟 2] 快取資料不存在,請先載入波形")
|
| 861 |
+
return None, None, None, gr.update(interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 862 |
|
| 863 |
+
first_pick = earthquake_metadata[event_name]["first_pick"]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 864 |
|
| 865 |
+
logger.info(f"[步驟 2] 提取波形資料(P 波後 {duration} 秒)...")
|
|
|
|
| 866 |
|
| 867 |
+
# 提取波形資料
|
|
|
|
|
|
|
|
|
|
| 868 |
waveforms, station_info_list, valid_stations, missing_components_count = (
|
| 869 |
extract_waveforms_from_stream(
|
| 870 |
+
event_name, cached_stream, cached_stations, duration, vs30_input=600
|
| 871 |
)
|
| 872 |
)
|
| 873 |
|
| 874 |
if len(waveforms) == 0:
|
| 875 |
+
logger.error("[步驟 2] 無法提取波形資料")
|
| 876 |
+
return None, None, None, gr.update(interactive=False)
|
| 877 |
+
|
| 878 |
+
# 繪製波形圖
|
| 879 |
+
waveform_plot = plot_waveform(cached_stream, cached_stations, first_pick, duration)
|
| 880 |
+
|
| 881 |
+
logger.info(f"[步驟 2] 完成 - 已提取 {len(waveforms)} 個測站的波形")
|
| 882 |
+
return waveforms, station_info_list, waveform_plot, gr.update(interactive=True)
|
| 883 |
+
|
| 884 |
+
except Exception as e:
|
| 885 |
+
logger.error(f"[步驟 2] 發生錯誤: {e}")
|
| 886 |
+
import traceback
|
| 887 |
+
traceback.print_exc()
|
| 888 |
+
return None, None, None, gr.update(interactive=False)
|
| 889 |
+
|
| 890 |
+
|
| 891 |
+
# ============ 步驟 3:執行模型推論(使用快取的波形)============
|
| 892 |
+
def step3_predict_intensity(cached_waveforms, cached_station_info, event_name):
|
| 893 |
+
"""
|
| 894 |
+
步驟 3:執行震度預測
|
| 895 |
+
|
| 896 |
+
直接使用快取的波形資料和測站資訊,無需重新讀檔或提取波形
|
| 897 |
|
| 898 |
+
spec #2:測站選擇上限 (25 站)、波形取樣率 (100 Hz)、時間窗長度 (30 秒)
|
| 899 |
+
spec #3:推論流程、PGA → 震度轉換
|
| 900 |
+
"""
|
| 901 |
+
try:
|
| 902 |
+
if cached_waveforms is None or cached_station_info is None:
|
| 903 |
+
logger.warning("[步驟 3] 快取資料不存在,請先載入並提取波形")
|
| 904 |
+
return None, None
|
| 905 |
+
|
| 906 |
+
epicenter_lat = earthquake_metadata[event_name]["epicenter_lat"]
|
| 907 |
+
epicenter_lon = earthquake_metadata[event_name]["epicenter_lon"]
|
| 908 |
+
|
| 909 |
+
logger.info("[步驟 3] 開始模型推論...")
|
| 910 |
+
|
| 911 |
+
# Padding 到 25 個測站(模型要求)
|
| 912 |
max_stations = 25
|
| 913 |
waveform_padded = np.zeros((max_stations, 3000, 3))
|
| 914 |
station_info_padded = np.zeros((max_stations, 4))
|
| 915 |
|
| 916 |
+
for i in range(min(len(cached_waveforms), max_stations)):
|
| 917 |
+
waveform_padded[i] = cached_waveforms[i]
|
| 918 |
+
station_info_padded[i] = cached_station_info[i]
|
| 919 |
|
| 920 |
# 準備所有目標測站資訊(分批處理)
|
| 921 |
all_pga_list = []
|
|
|
|
| 960 |
for i in range(len(target_list)):
|
| 961 |
target_padded[i] = target_list[i]
|
| 962 |
|
| 963 |
+
# 組合成 torch tensor
|
| 964 |
tensor_data = {
|
| 965 |
"waveform": torch.tensor(waveform_padded).unsqueeze(0).double(),
|
| 966 |
"station": torch.tensor(station_info_padded).unsqueeze(0).double(),
|
| 967 |
"target": torch.tensor(target_padded).unsqueeze(0).double(),
|
| 968 |
}
|
| 969 |
|
| 970 |
+
# 執行預測
|
| 971 |
with torch.no_grad():
|
| 972 |
weight, sigma, mu = model(tensor_data)
|
| 973 |
batch_pga = (
|
|
|
|
| 992 |
pga_list, target_names, epicenter_lat, epicenter_lon
|
| 993 |
)
|
| 994 |
|
| 995 |
+
# 載入實際觀測震度圖
|
| 996 |
observed_intensity_path = load_observed_intensity_image(event_name)
|
| 997 |
|
| 998 |
+
logger.info("[步驟 3] 預測完成!")
|
| 999 |
return observed_intensity_path, intensity_map
|
| 1000 |
|
| 1001 |
except Exception as e:
|
| 1002 |
+
logger.error(f"[步驟 3] 發生錯誤: {e}")
|
| 1003 |
import traceback
|
| 1004 |
|
| 1005 |
traceback.print_exc()
|
| 1006 |
return None, None
|
| 1007 |
|
| 1008 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1009 |
|
| 1010 |
# ============ Gradio 介面 ============
|
| 1011 |
with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
|
|
| 1018 |
gr.Markdown("## 使用步驟")
|
| 1019 |
gr.Markdown(
|
| 1020 |
"""
|
| 1021 |
+
1. 選擇地震事件(自動載入測站)
|
| 1022 |
+
2. 調整 P 波後時間範圍(即時更新波形)
|
| 1023 |
+
3. 點擊「執行預測」查看震度分布
|
|
|
|
| 1024 |
|
| 1025 |
+
系統會自動選擇距離震央最近的 25 個測站,並快取資料避免重複讀檔。
|
| 1026 |
"""
|
| 1027 |
)
|
| 1028 |
with gr.Column(scale=1):
|
|
|
|
| 1070 |
value=load_observed_intensity_image(list(earthquake_metadata.keys())[0]),
|
| 1071 |
)
|
| 1072 |
|
| 1073 |
+
# ========== 隱藏的 State 變數(用於快取中間結果)==========
|
| 1074 |
+
cached_stream = gr.State(None) # ObsPy Stream object
|
| 1075 |
+
cached_stations = gr.State(None) # 選中的 25 個測站列表
|
| 1076 |
+
cached_waveforms = gr.State(None) # 提取的波形資料
|
| 1077 |
+
cached_station_info = gr.State(None) # 測站資訊列表
|
| 1078 |
+
|
| 1079 |
+
# ========== 事件綁定(使用鏈式觸發 + gr.State 快取)==========
|
| 1080 |
+
|
| 1081 |
+
# 【觸發點 1】事件切換:重新執行步驟 1 → 步驟 2
|
| 1082 |
event_dropdown.change(
|
| 1083 |
+
fn=step1_load_mseed_and_select_stations,
|
| 1084 |
+
inputs=[event_dropdown],
|
| 1085 |
+
outputs=[cached_stream, cached_stations, input_station_map, waveform_plot, predict_btn]
|
| 1086 |
+
).then( # 鏈式觸發步驟 2
|
| 1087 |
+
fn=step2_extract_and_plot_waveforms,
|
| 1088 |
+
inputs=[cached_stream, cached_stations, event_dropdown, duration_slider],
|
| 1089 |
+
outputs=[cached_waveforms, cached_station_info, waveform_plot, predict_btn]
|
| 1090 |
+
)
|
| 1091 |
+
|
| 1092 |
+
# 【觸發點 2】時間範圍調整:只重新執行步驟 2(快速,不重新讀檔)
|
| 1093 |
+
duration_slider.change(
|
| 1094 |
+
fn=step2_extract_and_plot_waveforms,
|
| 1095 |
+
inputs=[cached_stream, cached_stations, event_dropdown, duration_slider],
|
| 1096 |
+
outputs=[cached_waveforms, cached_station_info, waveform_plot, predict_btn]
|
| 1097 |
)
|
| 1098 |
|
| 1099 |
+
# 【觸發點 3】載入波形按鈕:手動觸發步驟 1 → 步驟 2
|
| 1100 |
load_waveform_btn.click(
|
| 1101 |
+
fn=step1_load_mseed_and_select_stations,
|
| 1102 |
+
inputs=[event_dropdown],
|
| 1103 |
+
outputs=[cached_stream, cached_stations, input_station_map, waveform_plot, predict_btn]
|
| 1104 |
+
).then(
|
| 1105 |
+
fn=step2_extract_and_plot_waveforms,
|
| 1106 |
+
inputs=[cached_stream, cached_stations, event_dropdown, duration_slider],
|
| 1107 |
+
outputs=[cached_waveforms, cached_station_info, waveform_plot, predict_btn]
|
| 1108 |
)
|
| 1109 |
|
| 1110 |
+
# 【觸發點 4】執行預測:使用快取的波形執行步驟 3
|
| 1111 |
predict_btn.click(
|
| 1112 |
+
fn=step3_predict_intensity,
|
| 1113 |
+
inputs=[cached_waveforms, cached_station_info, event_dropdown],
|
| 1114 |
+
outputs=[observed_intensity_image, predicted_intensity_map]
|
| 1115 |
)
|
| 1116 |
|
| 1117 |
+
# 【冷啟動】應用載入時自動執行步驟 1 → 步驟 2
|
| 1118 |
demo.load(
|
| 1119 |
+
fn=step1_load_mseed_and_select_stations,
|
| 1120 |
+
inputs=[event_dropdown],
|
| 1121 |
+
outputs=[cached_stream, cached_stations, input_station_map, waveform_plot, predict_btn]
|
| 1122 |
+
).then(
|
| 1123 |
+
fn=step2_extract_and_plot_waveforms,
|
| 1124 |
+
inputs=[cached_stream, cached_stations, event_dropdown, duration_slider],
|
| 1125 |
+
outputs=[cached_waveforms, cached_station_info, waveform_plot, predict_btn]
|
|
|
|
|
|
|
|
|
|
| 1126 |
)
|
| 1127 |
|
| 1128 |
demo.launch()
|