Spaces:
Running
Running
Commit
·
9894f5b
1
Parent(s):
12f3211
add waveform loading functionality and update Gradio interface for user interaction
Browse files
app.py
CHANGED
|
@@ -601,6 +601,40 @@ def plot_intensity_map(pga_list, target_names):
|
|
| 601 |
return fig
|
| 602 |
|
| 603 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
def predict_intensity(event_name, start_time, end_time, epicenter_lon, epicenter_lat, vs30_input):
|
| 605 |
"""執行震度預測"""
|
| 606 |
try:
|
|
@@ -614,7 +648,7 @@ def predict_intensity(event_name, start_time, end_time, epicenter_lon, epicenter
|
|
| 614 |
selected_stations = select_nearest_stations(st, epicenter_lat, epicenter_lon, n_stations=25)
|
| 615 |
|
| 616 |
if len(selected_stations) == 0:
|
| 617 |
-
return None,
|
| 618 |
|
| 619 |
# 3. 從選定的測站提取波形
|
| 620 |
logger.info(f"提取波形資料(時間範圍: {start_time}-{end_time} 秒)...")
|
|
@@ -623,7 +657,7 @@ def predict_intensity(event_name, start_time, end_time, epicenter_lon, epicenter
|
|
| 623 |
)
|
| 624 |
|
| 625 |
if len(waveforms) == 0:
|
| 626 |
-
return None,
|
| 627 |
|
| 628 |
# 4. Padding 到 25 個測站(模型要求)
|
| 629 |
max_stations = 25
|
|
@@ -660,24 +694,24 @@ def predict_intensity(event_name, start_time, end_time, epicenter_lon, epicenter
|
|
| 660 |
pga_list = torch.sum(weight * mu, dim=2).cpu().detach().numpy().flatten().tolist()
|
| 661 |
|
| 662 |
# 8. 繪製結果
|
| 663 |
-
waveform_plot = plot_waveform(st, selected_stations, start_time, end_time)
|
| 664 |
intensity_plot = plot_intensity_map(pga_list, target_names)
|
| 665 |
|
| 666 |
# 9. 統計資訊
|
| 667 |
max_intensity = max([calculate_intensity(pga, label=True) for pga in pga_list])
|
| 668 |
-
stats = f"
|
|
|
|
| 669 |
stats += f"震央位置: ({epicenter_lon:.4f}, {epicenter_lat:.4f})\n"
|
| 670 |
stats += f"使用測站數: {len(waveforms)} / 25\n"
|
| 671 |
stats += f"預測最大震度: {max_intensity}"
|
| 672 |
|
| 673 |
logger.info("預測完成!")
|
| 674 |
-
return
|
| 675 |
|
| 676 |
except Exception as e:
|
| 677 |
logger.error(f"預測過程發生錯誤: {e}")
|
| 678 |
import traceback
|
| 679 |
traceback.print_exc()
|
| 680 |
-
return None,
|
| 681 |
|
| 682 |
|
| 683 |
# ============ Gradio 介面 ============
|
|
@@ -712,32 +746,46 @@ with gr.Blocks(title="TTSAM 震度預測系統") as demo:
|
|
| 712 |
info="剪力波速度(預設 600 m/s,若有 Vs30 資料會自動覆蓋)"
|
| 713 |
)
|
| 714 |
|
| 715 |
-
|
|
|
|
| 716 |
|
| 717 |
gr.Markdown("""
|
| 718 |
-
###
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
|
|
|
|
|
|
|
|
|
| 722 |
""")
|
| 723 |
|
|
|
|
|
|
|
| 724 |
# 右側:震度分布圖
|
| 725 |
with gr.Column(scale=1):
|
| 726 |
gr.Markdown("## 預測震度分布")
|
| 727 |
intensity_plot = gr.Plot(label="震度分布圖")
|
| 728 |
-
stats_output = gr.Textbox(label="預測統計", lines=
|
| 729 |
|
| 730 |
# 下方:波形圖
|
| 731 |
with gr.Row():
|
| 732 |
gr.Markdown("## 輸入波形")
|
| 733 |
with gr.Row():
|
| 734 |
-
waveform_plot = gr.Plot(label="
|
| 735 |
|
| 736 |
# 綁定事件
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 737 |
predict_btn.click(
|
| 738 |
fn=predict_intensity,
|
| 739 |
inputs=[event_dropdown, start_slider, end_slider, epicenter_lon_input, epicenter_lat_input, vs30_input],
|
| 740 |
-
outputs=[
|
| 741 |
)
|
| 742 |
|
| 743 |
demo.launch()
|
|
|
|
| 601 |
return fig
|
| 602 |
|
| 603 |
|
| 604 |
+
def load_and_display_waveform(event_name, start_time, end_time, epicenter_lon, epicenter_lat):
|
| 605 |
+
"""載入並顯示波形,讓使用者確認範圍"""
|
| 606 |
+
try:
|
| 607 |
+
# 1. 載入完整的 mseed 檔案
|
| 608 |
+
logger.info(f"載入地震事件: {event_name}")
|
| 609 |
+
st = load_waveform(event_name)
|
| 610 |
+
logger.info(f"載入了 {len(st)} 個 trace")
|
| 611 |
+
|
| 612 |
+
# 2. 根據震央距離選擇最近的 25 個測站
|
| 613 |
+
logger.info(f"選擇距離震央 ({epicenter_lat}, {epicenter_lon}) 最近的測站...")
|
| 614 |
+
selected_stations = select_nearest_stations(st, epicenter_lat, epicenter_lon, n_stations=25)
|
| 615 |
+
|
| 616 |
+
if len(selected_stations) == 0:
|
| 617 |
+
return None, "錯誤:找不到有效的測站資料", gr.update(interactive=False)
|
| 618 |
+
|
| 619 |
+
# 3. 繪製波形
|
| 620 |
+
waveform_plot = plot_waveform(st, selected_stations, start_time, end_time)
|
| 621 |
+
|
| 622 |
+
info_text = f"✅ 已載入波形資料\n"
|
| 623 |
+
info_text += f"選取時間範圍: {start_time:.1f} - {end_time:.1f} 秒\n"
|
| 624 |
+
info_text += f"震央位置: ({epicenter_lon:.4f}, {epicenter_lat:.4f})\n"
|
| 625 |
+
info_text += f"選擇了 {len(selected_stations)} 個最近的測站\n"
|
| 626 |
+
info_text += f"請確認波形範圍後,點擊「執行預測」按鈕"
|
| 627 |
+
|
| 628 |
+
logger.info("波形載入完成")
|
| 629 |
+
return waveform_plot, info_text, gr.update(interactive=True)
|
| 630 |
+
|
| 631 |
+
except Exception as e:
|
| 632 |
+
logger.error(f"波形載入發生錯誤: {e}")
|
| 633 |
+
import traceback
|
| 634 |
+
traceback.print_exc()
|
| 635 |
+
return None, f"錯誤: {str(e)}", gr.update(interactive=False)
|
| 636 |
+
|
| 637 |
+
|
| 638 |
def predict_intensity(event_name, start_time, end_time, epicenter_lon, epicenter_lat, vs30_input):
|
| 639 |
"""執行震度預測"""
|
| 640 |
try:
|
|
|
|
| 648 |
selected_stations = select_nearest_stations(st, epicenter_lat, epicenter_lon, n_stations=25)
|
| 649 |
|
| 650 |
if len(selected_stations) == 0:
|
| 651 |
+
return None, "錯誤:找不到有效的測站資料"
|
| 652 |
|
| 653 |
# 3. 從選定的測站提取波形
|
| 654 |
logger.info(f"提取波形資料(時間範圍: {start_time}-{end_time} 秒)...")
|
|
|
|
| 657 |
)
|
| 658 |
|
| 659 |
if len(waveforms) == 0:
|
| 660 |
+
return None, "錯誤:無法提取波形資料"
|
| 661 |
|
| 662 |
# 4. Padding 到 25 個測站(模型要求)
|
| 663 |
max_stations = 25
|
|
|
|
| 694 |
pga_list = torch.sum(weight * mu, dim=2).cpu().detach().numpy().flatten().tolist()
|
| 695 |
|
| 696 |
# 8. 繪製結果
|
|
|
|
| 697 |
intensity_plot = plot_intensity_map(pga_list, target_names)
|
| 698 |
|
| 699 |
# 9. 統計資訊
|
| 700 |
max_intensity = max([calculate_intensity(pga, label=True) for pga in pga_list])
|
| 701 |
+
stats = f"✅ 預測完成!\n"
|
| 702 |
+
stats += f"選取時間範圍: {start_time:.1f} - {end_time:.1f} 秒\n"
|
| 703 |
stats += f"震央位置: ({epicenter_lon:.4f}, {epicenter_lat:.4f})\n"
|
| 704 |
stats += f"使用測站數: {len(waveforms)} / 25\n"
|
| 705 |
stats += f"預測最大震度: {max_intensity}"
|
| 706 |
|
| 707 |
logger.info("預測完成!")
|
| 708 |
+
return intensity_plot, stats
|
| 709 |
|
| 710 |
except Exception as e:
|
| 711 |
logger.error(f"預測過程發生錯誤: {e}")
|
| 712 |
import traceback
|
| 713 |
traceback.print_exc()
|
| 714 |
+
return None, f"錯誤: {str(e)}"
|
| 715 |
|
| 716 |
|
| 717 |
# ============ Gradio 介面 ============
|
|
|
|
| 746 |
info="剪力波速度(預設 600 m/s,若有 Vs30 資料會自動覆蓋)"
|
| 747 |
)
|
| 748 |
|
| 749 |
+
load_waveform_btn = gr.Button("📊 載入波形", variant="secondary")
|
| 750 |
+
predict_btn = gr.Button("🔮 執行預測", variant="primary", interactive=False)
|
| 751 |
|
| 752 |
gr.Markdown("""
|
| 753 |
+
### 使用步驟
|
| 754 |
+
1. 選擇地震事件和時間範圍
|
| 755 |
+
2. 輸入震央位置
|
| 756 |
+
3. 點擊「載入波形」確認波形範圍
|
| 757 |
+
4. 確認無誤後,點擊「執行預測」
|
| 758 |
+
|
| 759 |
+
ℹ️ 系統會自動選擇距離震央最近的 25 個測站
|
| 760 |
""")
|
| 761 |
|
| 762 |
+
info_output = gr.Textbox(label="狀態資訊", lines=5, interactive=False)
|
| 763 |
+
|
| 764 |
# 右側:震度分布圖
|
| 765 |
with gr.Column(scale=1):
|
| 766 |
gr.Markdown("## 預測震度分布")
|
| 767 |
intensity_plot = gr.Plot(label="震度分布圖")
|
| 768 |
+
stats_output = gr.Textbox(label="預測統計", lines=4)
|
| 769 |
|
| 770 |
# 下方:波形圖
|
| 771 |
with gr.Row():
|
| 772 |
gr.Markdown("## 輸入波形")
|
| 773 |
with gr.Row():
|
| 774 |
+
waveform_plot = gr.Plot(label="地震波形(前 10 個最近測站)")
|
| 775 |
|
| 776 |
# 綁定事件
|
| 777 |
+
# 第一步:載入波形
|
| 778 |
+
load_waveform_btn.click(
|
| 779 |
+
fn=load_and_display_waveform,
|
| 780 |
+
inputs=[event_dropdown, start_slider, end_slider, epicenter_lon_input, epicenter_lat_input],
|
| 781 |
+
outputs=[waveform_plot, info_output, predict_btn]
|
| 782 |
+
)
|
| 783 |
+
|
| 784 |
+
# 第二步:執行預測
|
| 785 |
predict_btn.click(
|
| 786 |
fn=predict_intensity,
|
| 787 |
inputs=[event_dropdown, start_slider, end_slider, epicenter_lon_input, epicenter_lat_input, vs30_input],
|
| 788 |
+
outputs=[intensity_plot, stats_output]
|
| 789 |
)
|
| 790 |
|
| 791 |
demo.launch()
|