import requests import gradio as gr from datetime import datetime, timedelta # USGS API 的基礎 URL USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query" def format_earthquake_info_markdown(data): """ 將從 API 獲取的地震資料格式化為 Markdown 字串。 """ if not data or 'features' not in data or not data['features']: return "在指定的條件下,找不到任何地震資料。" count = data['metadata']['count'] title = data['metadata']['title'] # 建立 Markdown 表格的標頭 md_output = f"## {title}\n\n" md_output += f"**查詢成功,共找到 {count} 筆地震資料。**\n\n" md_output += "| 時間 (UTC) | 地點 | 芮氏規模 | 深度 (公里) | 經緯度 |\n" md_output += "|---|---|---|---|---|\n" # 填入每一筆地震資料 for feature in data['features']: properties = feature['properties'] geometry = feature['geometry'] event_time_utc = datetime.fromtimestamp(properties['time'] / 1000) time_str = event_time_utc.strftime('%Y-%m-%d %H:%M:%S') magnitude = properties['mag'] place = properties['place'].replace('|', '\|') # 避免 Markdown 表格語法衝突 longitude, latitude, depth = geometry['coordinates'] md_output += f"| {time_str} | {place} | {magnitude:.2f} | {depth:.2f} | ({latitude:.4f}, {longitude:.4f}) |\n" return md_output def query_earthquakes( starttime_str: str, endtime_str: str, min_magnitude: float, use_geo: bool, min_lat: float, max_lat: float, min_lon: float, max_lon: float ): """ 接收來自 Gradio UI 的輸入,查詢並回傳格式化的地震資料。 """ params = { "format": "geojson", "starttime": starttime_str, "endtime": endtime_str, "minmagnitude": min_magnitude, "limit": 500, # 限制回傳數量,避免 UI 過於壅塞 "orderby": "time" } # 如果使用者勾選了 "指定地理範圍" if use_geo: # 簡單的驗證 if not all([min_lat, max_lat, min_lon, max_lon]): return "錯誤:若要指定地理範圍,則經緯度四個欄位皆須填寫。" if min_lat >= max_lat or min_lon >= max_lon: return "錯誤:最小經緯度必須小於最大經緯度。" params.update({ "minlatitude": min_lat, "maxlatitude": max_lat, "minlongitude": min_lon, "maxlongitude": max_lon }) try: response = requests.get(USGS_API_BASE_URL, params=params, timeout=20) response.raise_for_status() data = response.json() return format_earthquake_info_markdown(data) except requests.exceptions.RequestException as e: return f"錯誤:網路請求失敗: {e}" except Exception as e: return f"發生未知錯誤: {e}" # --- Gradio 介面設計 --- with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown("# 🌎 USGS 地震資料查詢工具") gr.Markdown("透過此工具,您可以查詢美國地質調查局 (USGS) 的全球地震紀錄。") with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 1. 設定查詢條件") # 預設查詢時間為過去 7 天 today = datetime.now() seven_days_ago = today - timedelta(days=7) starttime_input = gr.Textbox(label="查詢開始日期", value=seven_days_ago.strftime('%Y-%m-%d')) endtime_input = gr.Textbox(label="查詢結束日期", value=today.strftime('%Y-%m-%d')) magnitude_slider = gr.Slider( minimum=0, maximum=10, step=0.1, label="最小芮氏規模", value=4.5 ) gr.Markdown("### 2. (可選) 指定地理範圍") use_geo_checkbox = gr.Checkbox(label="啟用地理範圍篩選", value=False) with gr.Row(): min_lat_input = gr.Number(label="最小緯度 (南)", value=21.5) max_lat_input = gr.Number(label="最大緯度 (北)", value=25.5) with gr.Row(): min_lon_input = gr.Number(label="最小經度 (西)", value=120.0) max_lon_input = gr.Number(label="最大經度 (東)", value=122.5) submit_btn = gr.Button("開始查詢", variant="primary") with gr.Column(scale=3): gr.Markdown("### 查詢結果") output_markdown = gr.Markdown("點擊「開始查詢」後,結果將會顯示於此。") # 綁定按鈕點擊事件 submit_btn.click( fn=query_earthquakes, inputs=[ starttime_input, endtime_input, magnitude_slider, use_geo_checkbox, min_lat_input, max_lat_input, min_lon_input, max_lon_input ], outputs=output_markdown ) gr.Examples( examples=[ [seven_days_ago.strftime('%Y-%m-%d'), today.strftime('%Y-%m-%d'), 6.0, False, 0, 0, 0, 0], ["2024-04-02", "2024-04-04", 5.5, True, 23.0, 25.0, 121.0, 122.5], ["2024-01-01", "2024-01-02", 7.0, True, 35.0, 38.0, 136.0, 140.0], ], inputs=[ starttime_input, endtime_input, magnitude_slider, use_geo_checkbox, min_lat_input, max_lat_input, min_lon_input, max_lon_input ], outputs=output_markdown, fn=query_earthquakes, cache_examples=True # 讓範例結果快取,加速載入 ) if __name__ == "__main__": demo.launch()