Spaces:
Running
Running
Commit
·
0d04cf4
1
Parent(s):
6d209c3
feat: enhance intensity map visualization by adjusting station opacity based on P-wave arrival time
Browse files
app.py
CHANGED
|
@@ -694,9 +694,14 @@ def get_intensity_color(intensity):
|
|
| 694 |
|
| 695 |
def create_intensity_map(
|
| 696 |
pga_list, target_names, epicenter_lat=None, epicenter_lon=None,
|
| 697 |
-
selected_stations=None
|
| 698 |
):
|
| 699 |
-
"""使用 Plotly 創建互動式震度分布地圖(合併輸入測站與預測震度)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 700 |
|
| 701 |
# 按震度等級分組資料
|
| 702 |
intensity_groups = {
|
|
@@ -736,36 +741,77 @@ def create_intensity_map(
|
|
| 736 |
# 創建 Plotly 地圖
|
| 737 |
fig = go.Figure()
|
| 738 |
|
| 739 |
-
#
|
| 740 |
if selected_stations:
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
|
|
|
|
|
|
|
|
|
|
| 744 |
|
| 745 |
for station_data in selected_stations:
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 750 |
f"輸入測站<br>"
|
| 751 |
-
f"
|
|
|
|
| 752 |
)
|
| 753 |
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 767 |
)
|
| 768 |
-
)
|
| 769 |
|
| 770 |
# 【頂層】添加各震度等級的測站(預測結果)
|
| 771 |
intensity_labels = ["0", "1", "2", "3", "4", "5-", "5+", "6-", "6+", "7"]
|
|
@@ -945,7 +991,7 @@ def step2_extract_and_plot_waveforms(cached_stream, cached_stations, event_name,
|
|
| 945 |
|
| 946 |
# ============ 步驟 3:執行模型推論(使用快取的波形)============
|
| 947 |
def step3_predict_intensity(cached_waveforms, cached_station_info, cached_stations,
|
| 948 |
-
event_name):
|
| 949 |
"""
|
| 950 |
步驟 3:執行震度預測
|
| 951 |
|
|
@@ -963,6 +1009,7 @@ def step3_predict_intensity(cached_waveforms, cached_station_info, cached_statio
|
|
| 963 |
|
| 964 |
epicenter_lat = earthquake_metadata[event_name]["epicenter_lat"]
|
| 965 |
epicenter_lon = earthquake_metadata[event_name]["epicenter_lon"]
|
|
|
|
| 966 |
|
| 967 |
logger.info("[步驟 3] 開始模型推論...")
|
| 968 |
|
|
@@ -1046,15 +1093,13 @@ def step3_predict_intensity(cached_waveforms, cached_station_info, cached_statio
|
|
| 1046 |
target_names = all_target_names
|
| 1047 |
|
| 1048 |
# 繪製互動式地圖(固定高度 800)- 合併輸入測站與預測震度
|
|
|
|
| 1049 |
intensity_map = create_intensity_map(
|
| 1050 |
pga_list, target_names, epicenter_lat, epicenter_lon,
|
| 1051 |
-
selected_stations=cached_stations
|
| 1052 |
)
|
| 1053 |
|
| 1054 |
-
# 生成警報文字報告
|
| 1055 |
-
duration = cached_waveforms[0].shape[
|
| 1056 |
-
0] / 100 if cached_waveforms is not None and len(
|
| 1057 |
-
cached_waveforms) > 0 else 30
|
| 1058 |
alert_report = generate_earthquake_alert_report(
|
| 1059 |
pga_list, target_names, event_name, duration
|
| 1060 |
)
|
|
@@ -1100,6 +1145,7 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1100 |
max_lines=7,
|
| 1101 |
interactive=False,
|
| 1102 |
show_copy_button=False,
|
|
|
|
| 1103 |
)
|
| 1104 |
|
| 1105 |
waveform_plot = gr.Plot(
|
|
@@ -1140,7 +1186,7 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1140 |
outputs=[cached_waveforms, cached_station_info, waveform_plot]
|
| 1141 |
).then( # 鏈式觸發步驟 3
|
| 1142 |
fn=step3_predict_intensity,
|
| 1143 |
-
inputs=[cached_waveforms, cached_station_info, cached_stations, event_dropdown],
|
| 1144 |
outputs=[predicted_intensity_map, alert_textbox]
|
| 1145 |
)
|
| 1146 |
|
|
@@ -1151,7 +1197,7 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1151 |
outputs=[cached_waveforms, cached_station_info, waveform_plot]
|
| 1152 |
).then( # 鏈式觸發步驟 3
|
| 1153 |
fn=step3_predict_intensity,
|
| 1154 |
-
inputs=[cached_waveforms, cached_station_info, cached_stations, event_dropdown],
|
| 1155 |
outputs=[predicted_intensity_map, alert_textbox]
|
| 1156 |
)
|
| 1157 |
|
|
@@ -1170,7 +1216,7 @@ with gr.Blocks(title="TTSAM 震度預測系統", fill_height=True) as demo:
|
|
| 1170 |
outputs=[cached_waveforms, cached_station_info, waveform_plot]
|
| 1171 |
).then(
|
| 1172 |
fn=step3_predict_intensity,
|
| 1173 |
-
inputs=[cached_waveforms, cached_station_info, cached_stations, event_dropdown],
|
| 1174 |
outputs=[predicted_intensity_map, alert_textbox]
|
| 1175 |
)
|
| 1176 |
|
|
|
|
| 694 |
|
| 695 |
def create_intensity_map(
|
| 696 |
pga_list, target_names, epicenter_lat=None, epicenter_lon=None,
|
| 697 |
+
selected_stations=None, duration=None, first_pick=None
|
| 698 |
):
|
| 699 |
+
"""使用 Plotly 創建互動式震度分布地圖(合併輸入測站與預測震度)
|
| 700 |
+
|
| 701 |
+
輸入測站的透明度根據 P 波到時是否在時間窗內調整:
|
| 702 |
+
- P 波在時間窗內:較不透明 (opacity=0.9)
|
| 703 |
+
- P 波在時間窗外:較透明 (opacity=0.3)
|
| 704 |
+
"""
|
| 705 |
|
| 706 |
# 按震度等級分組資料
|
| 707 |
intensity_groups = {
|
|
|
|
| 741 |
# 創建 Plotly 地圖
|
| 742 |
fig = go.Figure()
|
| 743 |
|
| 744 |
+
# 【底層】添加輸入測站(根據 P 波時間點是否在時間窗內調整透明度)
|
| 745 |
if selected_stations:
|
| 746 |
+
# 分離 P 波在時間窗內和時間窗外的測站
|
| 747 |
+
stations_in_window = {"lat": [], "lon": [], "text": []}
|
| 748 |
+
stations_out_window = {"lat": [], "lon": [], "text": []}
|
| 749 |
+
|
| 750 |
+
# 計算時間窗範圍
|
| 751 |
+
end_time = first_pick + duration if first_pick is not None and duration is not None else None
|
| 752 |
|
| 753 |
for station_data in selected_stations:
|
| 754 |
+
lat = station_data["latitude"]
|
| 755 |
+
lon = station_data["longitude"]
|
| 756 |
+
station_name = station_data["station"]
|
| 757 |
+
p_arrival_time = station_data.get("p_arrival_time")
|
| 758 |
+
|
| 759 |
+
# 判斷 P 波是否在時間窗內
|
| 760 |
+
in_window = False
|
| 761 |
+
if end_time is not None and p_arrival_time is not None:
|
| 762 |
+
in_window = (0 <= p_arrival_time <= end_time)
|
| 763 |
+
|
| 764 |
+
hover_text = (
|
| 765 |
+
f"{station_name}<br>"
|
| 766 |
f"輸入測站<br>"
|
| 767 |
+
f"P 波到時: {p_arrival_time:.2f}s<br>" if p_arrival_time is not None else f"{station_name}<br>輸入測站<br>"
|
| 768 |
+
f"位置: ({lat:.3f}, {lon:.3f})"
|
| 769 |
)
|
| 770 |
|
| 771 |
+
if in_window:
|
| 772 |
+
stations_in_window["lat"].append(lat)
|
| 773 |
+
stations_in_window["lon"].append(lon)
|
| 774 |
+
stations_in_window["text"].append(hover_text)
|
| 775 |
+
else:
|
| 776 |
+
stations_out_window["lat"].append(lat)
|
| 777 |
+
stations_out_window["lon"].append(lon)
|
| 778 |
+
stations_out_window["text"].append(hover_text)
|
| 779 |
+
|
| 780 |
+
# 添加時間窗內的測站(較不透明)
|
| 781 |
+
if stations_in_window["lat"]:
|
| 782 |
+
fig.add_trace(
|
| 783 |
+
go.Scattermap(
|
| 784 |
+
lat=stations_in_window["lat"],
|
| 785 |
+
lon=stations_in_window["lon"],
|
| 786 |
+
mode="markers",
|
| 787 |
+
marker=dict(
|
| 788 |
+
size=8,
|
| 789 |
+
color="rgba(128, 128, 128, 0.9)", # 較不透明
|
| 790 |
+
),
|
| 791 |
+
text=stations_in_window["text"],
|
| 792 |
+
hovertemplate="%{text}<extra></extra>",
|
| 793 |
+
name="輸入測站 (P波在窗內)",
|
| 794 |
+
showlegend=True,
|
| 795 |
+
)
|
| 796 |
+
)
|
| 797 |
+
|
| 798 |
+
# 添加時間窗外的測站(較透明)
|
| 799 |
+
if stations_out_window["lat"]:
|
| 800 |
+
fig.add_trace(
|
| 801 |
+
go.Scattermap(
|
| 802 |
+
lat=stations_out_window["lat"],
|
| 803 |
+
lon=stations_out_window["lon"],
|
| 804 |
+
mode="markers",
|
| 805 |
+
marker=dict(
|
| 806 |
+
size=8,
|
| 807 |
+
color="rgba(128, 128, 128, 0.3)", # 較透明
|
| 808 |
+
),
|
| 809 |
+
text=stations_out_window["text"],
|
| 810 |
+
hovertemplate="%{text}<extra></extra>",
|
| 811 |
+
name="輸入測站 (P波在窗外)",
|
| 812 |
+
showlegend=True,
|
| 813 |
+
)
|
| 814 |
)
|
|
|
|
| 815 |
|
| 816 |
# 【頂層】添加各震度等級的測站(預測結果)
|
| 817 |
intensity_labels = ["0", "1", "2", "3", "4", "5-", "5+", "6-", "6+", "7"]
|
|
|
|
| 991 |
|
| 992 |
# ============ 步驟 3:執行模型推論(使用快取的波形)============
|
| 993 |
def step3_predict_intensity(cached_waveforms, cached_station_info, cached_stations,
|
| 994 |
+
event_name, duration):
|
| 995 |
"""
|
| 996 |
步驟 3:執行震度預測
|
| 997 |
|
|
|
|
| 1009 |
|
| 1010 |
epicenter_lat = earthquake_metadata[event_name]["epicenter_lat"]
|
| 1011 |
epicenter_lon = earthquake_metadata[event_name]["epicenter_lon"]
|
| 1012 |
+
first_pick = earthquake_metadata[event_name]["first_pick"]
|
| 1013 |
|
| 1014 |
logger.info("[步驟 3] 開始模型推論...")
|
| 1015 |
|
|
|
|
| 1093 |
target_names = all_target_names
|
| 1094 |
|
| 1095 |
# 繪製互動式地圖(固定高度 800)- 合併輸入測站與預測震度
|
| 1096 |
+
# 根據 P 波到時是否在時間窗內調整輸入測站透明度
|
| 1097 |
intensity_map = create_intensity_map(
|
| 1098 |
pga_list, target_names, epicenter_lat, epicenter_lon,
|
| 1099 |
+
selected_stations=cached_stations, duration=duration, first_pick=first_pick
|
| 1100 |
)
|
| 1101 |
|
| 1102 |
+
# 生成警報文字報告
|
|
|
|
|
|
|
|
|
|
| 1103 |
alert_report = generate_earthquake_alert_report(
|
| 1104 |
pga_list, target_names, event_name, duration
|
| 1105 |
)
|
|
|
|
| 1145 |
max_lines=7,
|
| 1146 |
interactive=False,
|
| 1147 |
show_copy_button=False,
|
| 1148 |
+
autoscroll=False,
|
| 1149 |
)
|
| 1150 |
|
| 1151 |
waveform_plot = gr.Plot(
|
|
|
|
| 1186 |
outputs=[cached_waveforms, cached_station_info, waveform_plot]
|
| 1187 |
).then( # 鏈式觸發步驟 3
|
| 1188 |
fn=step3_predict_intensity,
|
| 1189 |
+
inputs=[cached_waveforms, cached_station_info, cached_stations, event_dropdown, duration_slider],
|
| 1190 |
outputs=[predicted_intensity_map, alert_textbox]
|
| 1191 |
)
|
| 1192 |
|
|
|
|
| 1197 |
outputs=[cached_waveforms, cached_station_info, waveform_plot]
|
| 1198 |
).then( # 鏈式觸發步驟 3
|
| 1199 |
fn=step3_predict_intensity,
|
| 1200 |
+
inputs=[cached_waveforms, cached_station_info, cached_stations, event_dropdown, duration_slider],
|
| 1201 |
outputs=[predicted_intensity_map, alert_textbox]
|
| 1202 |
)
|
| 1203 |
|
|
|
|
| 1216 |
outputs=[cached_waveforms, cached_station_info, waveform_plot]
|
| 1217 |
).then(
|
| 1218 |
fn=step3_predict_intensity,
|
| 1219 |
+
inputs=[cached_waveforms, cached_station_info, cached_stations, event_dropdown, duration_slider],
|
| 1220 |
outputs=[predicted_intensity_map, alert_textbox]
|
| 1221 |
)
|
| 1222 |
|