Spaces:
Running
Running
Commit
·
14b5ab9
1
Parent(s):
f202901
docs: refactor event handling functions and streamline output parameters
Browse files
app.py
CHANGED
|
@@ -1003,27 +1003,6 @@ def create_intensity_map(pga_list, target_names, epicenter_lat=None, epicenter_l
|
|
| 1003 |
hoverinfo='skip'
|
| 1004 |
))
|
| 1005 |
|
| 1006 |
-
# 如果有震央位置,標記震央(紅色大點)
|
| 1007 |
-
if epicenter_lat and epicenter_lon:
|
| 1008 |
-
fig.add_trace(go.Scattermap(
|
| 1009 |
-
lat=[epicenter_lat],
|
| 1010 |
-
lon=[epicenter_lon],
|
| 1011 |
-
mode='markers',
|
| 1012 |
-
marker=dict(size=25, color='red'),
|
| 1013 |
-
text=[f'震央<br>({epicenter_lat:.3f}, {epicenter_lon:.3f})'],
|
| 1014 |
-
hovertemplate='%{text}<extra></extra>',
|
| 1015 |
-
name='震央',
|
| 1016 |
-
showlegend=True
|
| 1017 |
-
))
|
| 1018 |
-
|
| 1019 |
-
fig.add_trace(go.Scattermap(
|
| 1020 |
-
lat=[epicenter_lat],
|
| 1021 |
-
lon=[epicenter_lon],
|
| 1022 |
-
mode='markers',
|
| 1023 |
-
marker=dict(size=10, color='white'),
|
| 1024 |
-
showlegend=False
|
| 1025 |
-
))
|
| 1026 |
-
|
| 1027 |
# 設置地圖佈局
|
| 1028 |
fig.update_layout(
|
| 1029 |
map=dict(
|
|
@@ -1072,41 +1051,6 @@ def load_observed_intensity_image(event_name):
|
|
| 1072 |
return None
|
| 1073 |
|
| 1074 |
|
| 1075 |
-
def on_event_select(event_name):
|
| 1076 |
-
"""當選擇事件時立即載入實際觀測震度圖"""
|
| 1077 |
-
observed_intensity_path = load_observed_intensity_image(event_name)
|
| 1078 |
-
if observed_intensity_path:
|
| 1079 |
-
return observed_intensity_path
|
| 1080 |
-
else:
|
| 1081 |
-
return None
|
| 1082 |
-
|
| 1083 |
-
|
| 1084 |
-
def on_event_change(event_name, start_time, duration, epicenter_lon, epicenter_lat):
|
| 1085 |
-
"""
|
| 1086 |
-
當選擇事件時,同時更新波形地圖、波形圖、實際觀測圖
|
| 1087 |
-
|
| 1088 |
-
Returns:
|
| 1089 |
-
(station_map, waveform_plot, info_text, observed_intensity_path)
|
| 1090 |
-
"""
|
| 1091 |
-
try:
|
| 1092 |
-
# 同時更新波形地圖
|
| 1093 |
-
station_map, waveform_plot, info_text, _ = load_and_display_waveform(
|
| 1094 |
-
event_name, start_time, duration, epicenter_lon, epicenter_lat
|
| 1095 |
-
)
|
| 1096 |
-
|
| 1097 |
-
# 同時更新實際觀測圖
|
| 1098 |
-
observed_intensity_path = load_observed_intensity_image(event_name)
|
| 1099 |
-
|
| 1100 |
-
return station_map, waveform_plot, info_text, observed_intensity_path
|
| 1101 |
-
|
| 1102 |
-
except Exception as e:
|
| 1103 |
-
logger.error(f"事件切換時發生錯誤: {e}")
|
| 1104 |
-
return None, None, f"錯誤: {str(e)}", None
|
| 1105 |
-
|
| 1106 |
-
|
| 1107 |
-
|
| 1108 |
-
|
| 1109 |
-
|
| 1110 |
def load_and_display_waveform(event_name, start_time, duration):
|
| 1111 |
"""載入並顯示波形,讓使用者確認範圍
|
| 1112 |
|
|
@@ -1139,24 +1083,14 @@ def load_and_display_waveform(event_name, start_time, duration):
|
|
| 1139 |
# 4. 創建輸入測站地圖
|
| 1140 |
station_map = create_input_station_map(selected_stations, epicenter_lat, epicenter_lon)
|
| 1141 |
|
| 1142 |
-
# 明示實際用站數(少於 25 站時顯示警告)
|
| 1143 |
-
info_text = f"✅ 已載入波形資料\n"
|
| 1144 |
-
info_text += f"開始時間: {start_time:.1f} 秒\n"
|
| 1145 |
-
info_text += f"時間長度: {duration:.1f} 秒 ({start_time:.1f} - {end_time:.1f})\n"
|
| 1146 |
-
info_text += f"震央位置: ({epicenter_lon:.4f}, {epicenter_lat:.4f})\n"
|
| 1147 |
-
info_text += f"選擇了 {len(selected_stations)} 個最近的測站"
|
| 1148 |
-
if len(selected_stations) < 25:
|
| 1149 |
-
info_text += f" ⚠️(目標 25 個,實際 {len(selected_stations)} 個)"
|
| 1150 |
-
info_text += "\n請確認波形範圍後,點擊「執行預測」按鈕"
|
| 1151 |
-
|
| 1152 |
logger.info("波形載入完成")
|
| 1153 |
-
return station_map, waveform_plot,
|
| 1154 |
|
| 1155 |
except Exception as e:
|
| 1156 |
logger.error(f"波形載入發生錯誤: {e}")
|
| 1157 |
import traceback
|
| 1158 |
traceback.print_exc()
|
| 1159 |
-
return None, None,
|
| 1160 |
|
| 1161 |
|
| 1162 |
def predict_intensity(event_name, start_time, duration):
|
|
@@ -1267,26 +1201,14 @@ def predict_intensity(event_name, start_time, duration):
|
|
| 1267 |
# 載入實際觀測震度圖(filepath;左側以 800 高顯示)
|
| 1268 |
observed_intensity_path = load_observed_intensity_image(event_name)
|
| 1269 |
|
| 1270 |
-
# 統計資訊(包含目標點數)
|
| 1271 |
-
max_intensity = max([calculate_intensity(pga, label=True) for pga in pga_list])
|
| 1272 |
-
stats = f"✅ 預測完成!\n"
|
| 1273 |
-
stats += f"開始時間: {start_time:.1f} 秒\n"
|
| 1274 |
-
stats += f"時間長度: {duration:.1f} 秒 ({start_time:.1f} - {end_time:.1f})\n"
|
| 1275 |
-
stats += f"震央位置: ({epicenter_lon:.4f}, {epicenter_lat:.4f})\n"
|
| 1276 |
-
stats += f"使用測站數: {len(waveforms)} / 25\n"
|
| 1277 |
-
if missing_components_count > 0:
|
| 1278 |
-
stats += f"⚠️ 缺少 N/E 分量測站數: {missing_components_count} (已以 Z 分量代替)\n"
|
| 1279 |
-
stats += f"預測目標點數: {len(all_target_names)}\n"
|
| 1280 |
-
stats += f"預測最大震度: {max_intensity}"
|
| 1281 |
-
|
| 1282 |
logger.info("預測完成!")
|
| 1283 |
-
return observed_intensity_path, intensity_map
|
| 1284 |
|
| 1285 |
except Exception as e:
|
| 1286 |
logger.error(f"預測過程發生錯誤: {e}")
|
| 1287 |
import traceback
|
| 1288 |
traceback.print_exc()
|
| 1289 |
-
return None, None
|
| 1290 |
|
| 1291 |
|
| 1292 |
def on_full_workflow(event_name, start_time, duration):
|
|
@@ -1296,7 +1218,7 @@ def on_full_workflow(event_name, start_time, duration):
|
|
| 1296 |
此函數用於首次應用加載與事件切換時自動執行完整流程
|
| 1297 |
|
| 1298 |
返回所有必要的 UI 組件輸出:
|
| 1299 |
-
(station_map, waveform_plot,
|
| 1300 |
|
| 1301 |
spec #2:測站選擇上限 (25 站)、波形取樣率 (100 Hz)、時間窗長度 (30 秒)
|
| 1302 |
spec #3:推論流程、PGA → 震度轉換
|
|
@@ -1306,27 +1228,27 @@ def on_full_workflow(event_name, start_time, duration):
|
|
| 1306 |
|
| 1307 |
# 步驟 1: 載入波形
|
| 1308 |
logger.info(f"[on_full_workflow] 步驟 1/3: 波形載入...")
|
| 1309 |
-
station_map, waveform_plot,
|
| 1310 |
event_name, start_time, duration
|
| 1311 |
)
|
| 1312 |
|
| 1313 |
if station_map is None:
|
| 1314 |
logger.error("[on_full_workflow] 波形載入失敗")
|
| 1315 |
-
return None, None,
|
| 1316 |
|
| 1317 |
# 步驟 2: 執行推論
|
| 1318 |
logger.info(f"[on_full_workflow] 步驟 2/3: 模型推論...")
|
| 1319 |
-
observed_img, predicted_map
|
| 1320 |
event_name, start_time, duration
|
| 1321 |
)
|
| 1322 |
|
| 1323 |
if predicted_map is None:
|
| 1324 |
logger.error("[on_full_workflow] 推論失敗")
|
| 1325 |
-
return station_map, waveform_plot,
|
| 1326 |
|
| 1327 |
logger.info(f"[on_full_workflow] 步驟 3/3: 完成")
|
| 1328 |
|
| 1329 |
-
return station_map, waveform_plot,
|
| 1330 |
|
| 1331 |
except Exception as e:
|
| 1332 |
logger.error(f"[on_full_workflow] 完整工作流發生錯誤: {e}")
|
|
@@ -1354,8 +1276,7 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1354 |
系統會自動選擇距離震央最近的 25 個測站
|
| 1355 |
""")
|
| 1356 |
|
| 1357 |
-
|
| 1358 |
-
stats_output = gr.Textbox(label="預測統計", lines=4, interactive=False)
|
| 1359 |
|
| 1360 |
# 右上:輸入參數
|
| 1361 |
with gr.Column(scale=1):
|
|
@@ -1416,19 +1337,19 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1416 |
*on_full_workflow(event_name, start_time, duration),
|
| 1417 |
),
|
| 1418 |
inputs=[event_dropdown, start_slider, duration_slider],
|
| 1419 |
-
outputs=[input_station_map, waveform_plot,
|
| 1420 |
)
|
| 1421 |
|
| 1422 |
load_waveform_btn.click(
|
| 1423 |
fn=load_and_display_waveform,
|
| 1424 |
inputs=[event_dropdown, start_slider, duration_slider],
|
| 1425 |
-
outputs=[input_station_map, waveform_plot,
|
| 1426 |
)
|
| 1427 |
|
| 1428 |
predict_btn.click(
|
| 1429 |
fn=predict_intensity,
|
| 1430 |
inputs=[event_dropdown, start_slider, duration_slider],
|
| 1431 |
-
outputs=[observed_intensity_image, predicted_intensity_map
|
| 1432 |
)
|
| 1433 |
|
| 1434 |
# 應用啟動時自動執行完整工作流
|
|
@@ -1437,7 +1358,7 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1437 |
*on_full_workflow(event_name, start_time, duration),
|
| 1438 |
),
|
| 1439 |
inputs=[event_dropdown, start_slider, duration_slider],
|
| 1440 |
-
outputs=[input_station_map, waveform_plot,
|
| 1441 |
)
|
| 1442 |
|
| 1443 |
demo.launch()
|
|
|
|
| 1003 |
hoverinfo='skip'
|
| 1004 |
))
|
| 1005 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1006 |
# 設置地圖佈局
|
| 1007 |
fig.update_layout(
|
| 1008 |
map=dict(
|
|
|
|
| 1051 |
return None
|
| 1052 |
|
| 1053 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1054 |
def load_and_display_waveform(event_name, start_time, duration):
|
| 1055 |
"""載入並顯示波形,讓使用者確認範圍
|
| 1056 |
|
|
|
|
| 1083 |
# 4. 創建輸入測站地圖
|
| 1084 |
station_map = create_input_station_map(selected_stations, epicenter_lat, epicenter_lon)
|
| 1085 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1086 |
logger.info("波形載入完成")
|
| 1087 |
+
return station_map, waveform_plot, gr.update(interactive=True)
|
| 1088 |
|
| 1089 |
except Exception as e:
|
| 1090 |
logger.error(f"波形載入發生錯誤: {e}")
|
| 1091 |
import traceback
|
| 1092 |
traceback.print_exc()
|
| 1093 |
+
return None, None, gr.update(interactive=False)
|
| 1094 |
|
| 1095 |
|
| 1096 |
def predict_intensity(event_name, start_time, duration):
|
|
|
|
| 1201 |
# 載入實際觀測震度圖(filepath;左側以 800 高顯示)
|
| 1202 |
observed_intensity_path = load_observed_intensity_image(event_name)
|
| 1203 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1204 |
logger.info("預測完成!")
|
| 1205 |
+
return observed_intensity_path, intensity_map
|
| 1206 |
|
| 1207 |
except Exception as e:
|
| 1208 |
logger.error(f"預測過程發生錯誤: {e}")
|
| 1209 |
import traceback
|
| 1210 |
traceback.print_exc()
|
| 1211 |
+
return None, None
|
| 1212 |
|
| 1213 |
|
| 1214 |
def on_full_workflow(event_name, start_time, duration):
|
|
|
|
| 1218 |
此函數用於首次應用加載與事件切換時自動執行完整流程
|
| 1219 |
|
| 1220 |
返回所有必要的 UI 組件輸出:
|
| 1221 |
+
(station_map, waveform_plot, predicted_map, observed_img)
|
| 1222 |
|
| 1223 |
spec #2:測站選擇上限 (25 站)、波形取樣率 (100 Hz)、時間窗長度 (30 秒)
|
| 1224 |
spec #3:推論流程、PGA → 震度轉換
|
|
|
|
| 1228 |
|
| 1229 |
# 步驟 1: 載入波形
|
| 1230 |
logger.info(f"[on_full_workflow] 步驟 1/3: 波形載入...")
|
| 1231 |
+
station_map, waveform_plot, _ = load_and_display_waveform(
|
| 1232 |
event_name, start_time, duration
|
| 1233 |
)
|
| 1234 |
|
| 1235 |
if station_map is None:
|
| 1236 |
logger.error("[on_full_workflow] 波形載入失敗")
|
| 1237 |
+
return None, None, None, None
|
| 1238 |
|
| 1239 |
# 步驟 2: 執行推論
|
| 1240 |
logger.info(f"[on_full_workflow] 步驟 2/3: 模型推論...")
|
| 1241 |
+
observed_img, predicted_map = predict_intensity(
|
| 1242 |
event_name, start_time, duration
|
| 1243 |
)
|
| 1244 |
|
| 1245 |
if predicted_map is None:
|
| 1246 |
logger.error("[on_full_workflow] 推論失敗")
|
| 1247 |
+
return station_map, waveform_plot, None, observed_img
|
| 1248 |
|
| 1249 |
logger.info(f"[on_full_workflow] 步驟 3/3: 完成")
|
| 1250 |
|
| 1251 |
+
return station_map, waveform_plot, predicted_map, observed_img
|
| 1252 |
|
| 1253 |
except Exception as e:
|
| 1254 |
logger.error(f"[on_full_workflow] 完整工作流發生錯誤: {e}")
|
|
|
|
| 1276 |
系統會自動選擇距離震央最近的 25 個測站
|
| 1277 |
""")
|
| 1278 |
|
| 1279 |
+
|
|
|
|
| 1280 |
|
| 1281 |
# 右上:輸入參數
|
| 1282 |
with gr.Column(scale=1):
|
|
|
|
| 1337 |
*on_full_workflow(event_name, start_time, duration),
|
| 1338 |
),
|
| 1339 |
inputs=[event_dropdown, start_slider, duration_slider],
|
| 1340 |
+
outputs=[input_station_map, waveform_plot, predicted_intensity_map, observed_intensity_image]
|
| 1341 |
)
|
| 1342 |
|
| 1343 |
load_waveform_btn.click(
|
| 1344 |
fn=load_and_display_waveform,
|
| 1345 |
inputs=[event_dropdown, start_slider, duration_slider],
|
| 1346 |
+
outputs=[input_station_map, waveform_plot, predict_btn]
|
| 1347 |
)
|
| 1348 |
|
| 1349 |
predict_btn.click(
|
| 1350 |
fn=predict_intensity,
|
| 1351 |
inputs=[event_dropdown, start_slider, duration_slider],
|
| 1352 |
+
outputs=[observed_intensity_image, predicted_intensity_map]
|
| 1353 |
)
|
| 1354 |
|
| 1355 |
# 應用啟動時自動執行完整工作流
|
|
|
|
| 1358 |
*on_full_workflow(event_name, start_time, duration),
|
| 1359 |
),
|
| 1360 |
inputs=[event_dropdown, start_slider, duration_slider],
|
| 1361 |
+
outputs=[input_station_map, waveform_plot, predicted_intensity_map, observed_intensity_image]
|
| 1362 |
)
|
| 1363 |
|
| 1364 |
demo.launch()
|