cwadayi commited on
Commit
b3cbfc1
·
verified ·
1 Parent(s): 9048d79

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -60
app.py CHANGED
@@ -1,27 +1,54 @@
1
  import requests
2
  import gradio as gr
 
 
3
  from datetime import datetime, timedelta
4
 
5
  # USGS API 的基礎 URL
6
  USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
7
 
8
- def format_earthquake_info_markdown(data):
9
  """
10
- 將從 API 獲取的地震資料格式化為 Markdown 字串。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  """
12
  if not data or 'features' not in data or not data['features']:
13
- return "在指定的條件下,找不到任何地震資料。"
14
 
15
  count = data['metadata']['count']
16
  title = data['metadata']['title']
17
 
18
- # 建立 Markdown 表格的標頭
19
  md_output = f"## {title}\n\n"
20
  md_output += f"**查詢成功,共找到 {count} 筆地震資料。**\n\n"
21
  md_output += "| 時間 (UTC) | 地點 | 芮氏規模 | 深度 (公里) | 經緯度 |\n"
22
  md_output += "|---|---|---|---|---|\n"
23
 
24
- # 填入每一筆地震資料
 
 
25
  for feature in data['features']:
26
  properties = feature['properties']
27
  geometry = feature['geometry']
@@ -30,12 +57,26 @@ def format_earthquake_info_markdown(data):
30
  time_str = event_time_utc.strftime('%Y-%m-%d %H:%M:%S')
31
 
32
  magnitude = properties['mag']
33
- place = properties['place'].replace('|', '\|') # 避免 Markdown 表格語法衝突
34
  longitude, latitude, depth = geometry['coordinates']
35
 
36
- md_output += f"| {time_str} | {place} | {magnitude:.2f} | {depth:.2f} | ({latitude:.4f}, {longitude:.4f}) |\n"
 
 
37
 
38
- return md_output
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
  def query_earthquakes(
41
  starttime_str: str,
@@ -46,110 +87,106 @@ def query_earthquakes(
46
  min_lon: float, max_lon: float
47
  ):
48
  """
49
- 接收來自 Gradio UI 的輸入,查詢並回傳格式化的地震資料。
50
  """
51
  params = {
52
  "format": "geojson",
53
  "starttime": starttime_str,
54
  "endtime": endtime_str,
55
  "minmagnitude": min_magnitude,
56
- "limit": 500, # 限制回傳數量,避免 UI 過於壅塞
57
  "orderby": "time"
58
  }
59
 
60
- # 如果使用者勾選了 "指定地理範圍"
61
  if use_geo:
62
- # 簡單的驗證
63
- if not all([min_lat, max_lat, min_lon, max_lon]):
64
- return "錯誤:若要指定地理範圍,則經緯度四個欄位皆須填寫。"
65
  if min_lat >= max_lat or min_lon >= max_lon:
66
- return "錯誤:最小經緯度必須小於最大經緯度。"
67
-
68
  params.update({
69
- "minlatitude": min_lat,
70
- "maxlatitude": max_lat,
71
- "minlongitude": min_lon,
72
- "maxlongitude": max_lon
73
  })
74
 
75
  try:
76
  response = requests.get(USGS_API_BASE_URL, params=params, timeout=20)
77
  response.raise_for_status()
78
  data = response.json()
79
- return format_earthquake_info_markdown(data)
 
 
 
 
 
 
 
 
 
 
80
  except requests.exceptions.RequestException as e:
81
- return f"錯誤:網路請求失敗: {e}"
82
  except Exception as e:
83
- return f"發生未知錯誤: {e}"
84
 
85
  # --- Gradio 介面設計 ---
86
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
87
- gr.Markdown("# 🌎 USGS 地震資料查詢工具")
88
- gr.Markdown("透過此工具,您可以查詢美國地質調查局 (USGS) 的全球地震紀錄。")
89
 
90
  with gr.Row():
91
- with gr.Column(scale=1):
92
  gr.Markdown("### 1. 設定查詢條件")
93
- # 預設查詢時間為過去 7 天
94
  today = datetime.now()
95
  seven_days_ago = today - timedelta(days=7)
96
 
97
  starttime_input = gr.Textbox(label="查詢開始日期", value=seven_days_ago.strftime('%Y-%m-%d'))
98
  endtime_input = gr.Textbox(label="查詢結束日期", value=today.strftime('%Y-%m-%d'))
99
 
100
- magnitude_slider = gr.Slider(
101
- minimum=0, maximum=10, step=0.1,
102
- label="最小芮氏規模",
103
- value=4.5
104
- )
105
-
106
- gr.Markdown("### 2. (可選) 指定地理範圍")
107
- use_geo_checkbox = gr.Checkbox(label="啟用地理範圍篩選", value=False)
108
-
109
- with gr.Row():
110
- min_lat_input = gr.Number(label="最小緯度 (南)", value=21.5)
111
- max_lat_input = gr.Number(label="最大緯度 (北)", value=25.5)
112
- with gr.Row():
113
- min_lon_input = gr.Number(label="最小經度 (西)", value=120.0)
114
- max_lon_input = gr.Number(label="最大經度 (東)", value=122.5)
115
 
116
  submit_btn = gr.Button("開始查詢", variant="primary")
117
 
118
- with gr.Column(scale=3):
119
  gr.Markdown("### 查詢結果")
120
- output_markdown = gr.Markdown("點擊「開始查詢」後,結果將會顯示於此。")
 
 
 
 
 
121
 
122
  # 綁定按鈕點擊事件
123
  submit_btn.click(
124
  fn=query_earthquakes,
125
  inputs=[
126
- starttime_input,
127
- endtime_input,
128
- magnitude_slider,
129
- use_geo_checkbox,
130
- min_lat_input, max_lat_input,
131
- min_lon_input, max_lon_input
132
  ],
133
- outputs=output_markdown
134
  )
135
 
136
  gr.Examples(
137
  examples=[
138
- [seven_days_ago.strftime('%Y-%m-%d'), today.strftime('%Y-%m-%d'), 6.0, False, 0, 0, 0, 0],
139
  ["2024-04-02", "2024-04-04", 5.5, True, 23.0, 25.0, 121.0, 122.5],
140
  ["2024-01-01", "2024-01-02", 7.0, True, 35.0, 38.0, 136.0, 140.0],
141
  ],
142
  inputs=[
143
- starttime_input,
144
- endtime_input,
145
- magnitude_slider,
146
- use_geo_checkbox,
147
- min_lat_input, max_lat_input,
148
- min_lon_input, max_lon_input
149
  ],
150
- outputs=output_markdown,
151
  fn=query_earthquakes,
152
- cache_examples=True # 讓範例結果快取,加速載入
153
  )
154
 
155
  if __name__ == "__main__":
 
1
  import requests
2
  import gradio as gr
3
+ import pandas as pd
4
+ import plotly.express as px
5
  from datetime import datetime, timedelta
6
 
7
  # USGS API 的基礎 URL
8
  USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
9
 
10
+ def create_earthquake_map(df):
11
  """
12
+ 使用 Plotly Express 根據 DataFrame 建立地震分佈圖。
13
+ """
14
+ fig = px.scatter_geo(
15
+ df,
16
+ lat='latitude',
17
+ lon='longitude',
18
+ color='magnitude', # 顏色深淺代表規模
19
+ size='magnitude', # 點的大小也代表規模
20
+ hover_name='place', # 滑鼠懸停時顯示地點
21
+ hover_data=['time_str', 'depth'],
22
+ projection="natural earth",
23
+ title="地震分佈圖",
24
+ color_continuous_scale=px.colors.sequential.Plasma # 設定顏色主題
25
+ )
26
+
27
+ # 調整地圖外觀
28
+ fig.update_layout(
29
+ margin={"r":0,"t":40,"l":0,"b":0}
30
+ )
31
+ return fig
32
+
33
+ def process_earthquake_data(data):
34
+ """
35
+ 將從 API 獲取的原始資料轉換為 Markdown 和 DataFrame。
36
  """
37
  if not data or 'features' not in data or not data['features']:
38
+ return "在指定的條件下,找不到任何地震資料。", None
39
 
40
  count = data['metadata']['count']
41
  title = data['metadata']['title']
42
 
43
+ # 準備 Markdown 輸出
44
  md_output = f"## {title}\n\n"
45
  md_output += f"**查詢成功,共找到 {count} 筆地震資料。**\n\n"
46
  md_output += "| 時間 (UTC) | 地點 | 芮氏規模 | 深度 (公里) | 經緯度 |\n"
47
  md_output += "|---|---|---|---|---|\n"
48
 
49
+ # 準備 DataFrame 的資料列表
50
+ earthquake_list = []
51
+
52
  for feature in data['features']:
53
  properties = feature['properties']
54
  geometry = feature['geometry']
 
57
  time_str = event_time_utc.strftime('%Y-%m-%d %H:%M:%S')
58
 
59
  magnitude = properties['mag']
60
+ place = properties['place']
61
  longitude, latitude, depth = geometry['coordinates']
62
 
63
+ # 加入到 Markdown 字串
64
+ place_md = place.replace('|', '\|') # 避免 Markdown 表格語法衝突
65
+ md_output += f"| {time_str} | {place_md} | {magnitude:.2f} | {depth:.2f} | ({latitude:.4f}, {longitude:.4f}) |\n"
66
 
67
+ # 加入到 list 中,用於建立 DataFrame
68
+ earthquake_list.append({
69
+ 'latitude': latitude,
70
+ 'longitude': longitude,
71
+ 'magnitude': magnitude,
72
+ 'depth': depth,
73
+ 'place': place,
74
+ 'time_str': time_str
75
+ })
76
+
77
+ df = pd.DataFrame(earthquake_list)
78
+ return md_output, df
79
+
80
 
81
  def query_earthquakes(
82
  starttime_str: str,
 
87
  min_lon: float, max_lon: float
88
  ):
89
  """
90
+ 接收來自 Gradio UI 的輸入,查詢並回傳格式化的地震資料及地圖。
91
  """
92
  params = {
93
  "format": "geojson",
94
  "starttime": starttime_str,
95
  "endtime": endtime_str,
96
  "minmagnitude": min_magnitude,
97
+ "limit": 1000, # 可以適度增加 limit
98
  "orderby": "time"
99
  }
100
 
 
101
  if use_geo:
102
+ if not all([min_lat is not None, max_lat is not None, min_lon is not None, max_lon is not None]):
103
+ return "錯誤:若要指定地理範圍,則經緯度四個欄位皆須填寫。", None
 
104
  if min_lat >= max_lat or min_lon >= max_lon:
105
+ return "錯誤:最小經緯度必須小於最大經緯度。", None
 
106
  params.update({
107
+ "minlatitude": min_lat, "maxlatitude": max_lat,
108
+ "minlongitude": min_lon, "maxlongitude": max_lon
 
 
109
  })
110
 
111
  try:
112
  response = requests.get(USGS_API_BASE_URL, params=params, timeout=20)
113
  response.raise_for_status()
114
  data = response.json()
115
+
116
+ # 處理資料
117
+ markdown_result, df_result = process_earthquake_data(data)
118
+
119
+ # 如果有資料,就建立地圖
120
+ if df_result is not None and not df_result.empty:
121
+ map_figure = create_earthquake_map(df_result)
122
+ return markdown_result, map_figure
123
+ else:
124
+ return markdown_result, None # 沒有資料時,回傳空的 map
125
+
126
  except requests.exceptions.RequestException as e:
127
+ return f"錯誤:網路請求失敗: {e}", None
128
  except Exception as e:
129
+ return f"發生未知錯誤: {e}", None
130
 
131
  # --- Gradio 介面設計 ---
132
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
133
+ gr.Markdown("# 🌎 USGS 地震資料查詢與視覺化工具")
134
+ gr.Markdown("透過此工具,您可以查詢並在地圖上視覺化美國地質調查局 (USGS) 的全球地震紀錄。")
135
 
136
  with gr.Row():
137
+ with gr.Column(scale=2):
138
  gr.Markdown("### 1. 設定查詢條件")
 
139
  today = datetime.now()
140
  seven_days_ago = today - timedelta(days=7)
141
 
142
  starttime_input = gr.Textbox(label="查詢開始日期", value=seven_days_ago.strftime('%Y-%m-%d'))
143
  endtime_input = gr.Textbox(label="查詢結束日期", value=today.strftime('%Y-%m-%d'))
144
 
145
+ magnitude_slider = gr.Slider(minimum=0, maximum=10, step=0.1, label="最小芮氏規模", value=4.5)
146
+
147
+ with gr.Accordion("可選:指定地理範圍", open=False): # 使用 Accordion 讓介面更簡潔
148
+ use_geo_checkbox = gr.Checkbox(label="啟用地理範圍篩選", value=False)
149
+ with gr.Row():
150
+ min_lat_input = gr.Number(label="最小緯度 (南)", value=21.5)
151
+ max_lat_input = gr.Number(label="最大緯度 ()", value=25.5)
152
+ with gr.Row():
153
+ min_lon_input = gr.Number(label="最小經度 (西)", value=120.0)
154
+ max_lon_input = gr.Number(label="最大經度 (東)", value=122.5)
 
 
 
 
 
155
 
156
  submit_btn = gr.Button("開始查詢", variant="primary")
157
 
158
+ with gr.Column(scale=5):
159
  gr.Markdown("### 查詢結果")
160
+ # 建立 Tabs 來切換地圖和詳細資料
161
+ with gr.Tabs():
162
+ with gr.TabItem("地震分佈圖"):
163
+ map_plot = gr.Plot()
164
+ with gr.TabItem("詳細資料列表"):
165
+ output_markdown = gr.Markdown("點擊「開始查詢」後,結果將會顯示於此。")
166
 
167
  # 綁定按鈕點擊事件
168
  submit_btn.click(
169
  fn=query_earthquakes,
170
  inputs=[
171
+ starttime_input, endtime_input, magnitude_slider,
172
+ use_geo_checkbox, min_lat_input, max_lat_input, min_lon_input, max_lon_input
 
 
 
 
173
  ],
174
+ outputs=[output_markdown, map_plot] # 同時更新 Markdown 和 Plot
175
  )
176
 
177
  gr.Examples(
178
  examples=[
179
+ ["2025-08-09", today.strftime('%Y-%m-%d'), 6.0, False, 21.5, 25.5, 120.0, 122.5],
180
  ["2024-04-02", "2024-04-04", 5.5, True, 23.0, 25.0, 121.0, 122.5],
181
  ["2024-01-01", "2024-01-02", 7.0, True, 35.0, 38.0, 136.0, 140.0],
182
  ],
183
  inputs=[
184
+ starttime_input, endtime_input, magnitude_slider,
185
+ use_geo_checkbox, min_lat_input, max_lat_input, min_lon_input, max_lon_input
 
 
 
 
186
  ],
187
+ outputs=[output_markdown, map_plot],
188
  fn=query_earthquakes,
189
+ cache_examples=True
190
  )
191
 
192
  if __name__ == "__main__":