jimmy60504 commited on
Commit
0d04cf4
·
1 Parent(s): 6d209c3

feat: enhance intensity map visualization by adjusting station opacity based on P-wave arrival time

Browse files
Files changed (1) hide show
  1. app.py +80 -34
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
- input_station_lats = []
742
- input_station_lons = []
743
- input_station_texts = []
 
 
 
744
 
745
  for station_data in selected_stations:
746
- input_station_lats.append(station_data["latitude"])
747
- input_station_lons.append(station_data["longitude"])
748
- input_station_texts.append(
749
- f"{station_data['station']}<br>"
 
 
 
 
 
 
 
 
750
  f"輸入測站<br>"
751
- f"位置: ({station_data['latitude']:.3f}, {station_data['longitude']:.3f})"
 
752
  )
753
 
754
- fig.add_trace(
755
- go.Scattermap(
756
- lat=input_station_lats,
757
- lon=input_station_lons,
758
- mode="markers",
759
- marker=dict(
760
- size=8,
761
- color="rgba(128, 128, 128, 0.7)", # 半透明灰色
762
- ),
763
- text=input_station_texts,
764
- hovertemplate="%{text}<extra></extra>",
765
- name="輸入測站",
766
- showlegend=True,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # 生成警報文字報告 (duration from waveform length: 3000 samples / 100 Hz = 30 seconds)
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