Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -62,7 +62,7 @@ def _to_float(x):
|
|
| 62 |
"""
|
| 63 |
將各種數字表達轉成 float:
|
| 64 |
- 純數字:23.5
|
| 65 |
-
- 含單位/文字:'23.5°N'、'121.6 E'、'25.3 公里' ->
|
| 66 |
- 其他不可解析 -> None
|
| 67 |
"""
|
| 68 |
if x is None:
|
|
@@ -91,7 +91,7 @@ def parse_ea0015(obj):
|
|
| 91 |
ei = q.get("EarthquakeInfo") or q.get("earthquakeInfo") or {}
|
| 92 |
epic = ei.get("Epicenter") or ei.get("epicenter") or {}
|
| 93 |
|
| 94 |
-
# Magnitude 可能在 Magnitude 或 EarthquakeMagnitude
|
| 95 |
mago = (
|
| 96 |
ei.get("Magnitude") or ei.get("magnitude")
|
| 97 |
or ei.get("EarthquakeMagnitude") or ei.get("earthquakeMagnitude")
|
|
@@ -170,57 +170,71 @@ def plot_trend_path(df):
|
|
| 170 |
fig.autofmt_xdate()
|
| 171 |
return _save_fig_to_tmp(fig)
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
def plot_map_path(df):
|
| 174 |
"""
|
| 175 |
-
優先使用 PyGMT
|
| 176 |
-
|
|
|
|
| 177 |
"""
|
| 178 |
-
lon_min, lon_max, lat_min, lat_max = 119, 123, 21, 26
|
| 179 |
-
|
| 180 |
-
# --- PyGMT 版 ---
|
| 181 |
-
if HAS_PYGMT and not df.empty:
|
| 182 |
-
d = df.dropna(subset=["Lon", "Lat"]).copy()
|
| 183 |
-
if not d.empty:
|
| 184 |
-
mag = pd.to_numeric(d["Magnitude"], errors="coerce").fillna(0).clip(lower=0)
|
| 185 |
-
size_cm = 0.06 * (mag + 1.5) # 每點大小(cm)
|
| 186 |
-
depth = pd.to_numeric(d["Depth_km"], errors="coerce").fillna(0)
|
| 187 |
-
|
| 188 |
-
fig = pygmt.Figure()
|
| 189 |
-
region = [lon_min, lon_max, lat_min, lat_max]
|
| 190 |
-
fig.coast(
|
| 191 |
-
region=region, projection="M12c",
|
| 192 |
-
land="lightgray", water="white",
|
| 193 |
-
shorelines="0.5p,black", borders="1/0.6p,black",
|
| 194 |
-
frame=["WSen", "xaf", "yaf"]
|
| 195 |
-
)
|
| 196 |
-
fig.plot(
|
| 197 |
-
x=d["Lon"].to_list(), y=d["Lat"].to_list(),
|
| 198 |
-
style="cc", sizes=size_cm.to_list(),
|
| 199 |
-
color=depth.to_list(), cmap="roma", pen="0.25p,black"
|
| 200 |
-
)
|
| 201 |
-
fig.colorbar(frame=["x+lDepth (km)"], cmap=True, position="JMR+w7c/0.4c+o0.6c/0c")
|
| 202 |
-
fig.basemap(map_scale="jBL+w50k+o0.6c/0.6c+f+lkm")
|
| 203 |
-
|
| 204 |
-
outpath = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
|
| 205 |
-
fig.savefig(outpath, dpi=220)
|
| 206 |
-
return outpath
|
| 207 |
-
|
| 208 |
-
# --- Matplotlib 備援 ---
|
| 209 |
if df.empty:
|
| 210 |
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
fig, ax = plt.subplots(figsize=(6, 6))
|
| 212 |
ax.set_xlim(lon_min, lon_max)
|
| 213 |
ax.set_ylim(lat_min, lat_max)
|
| 214 |
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
depth = pd.to_numeric(df["Depth_km"], errors="coerce")
|
| 218 |
-
|
| 219 |
-
sc = ax.scatter(df["Lon"], df["Lat"], s=s, c=depth, alpha=0.85, edgecolor="black")
|
| 220 |
cb = plt.colorbar(sc, ax=ax, fraction=0.046, pad=0.04)
|
| 221 |
cb.set_label("Depth (km)")
|
| 222 |
-
ax.set_xlabel("Longitude (°E)")
|
| 223 |
-
ax.
|
|
|
|
| 224 |
ax.grid(True, linestyle="--", alpha=0.3)
|
| 225 |
return _save_fig_to_tmp(fig)
|
| 226 |
|
|
|
|
| 62 |
"""
|
| 63 |
將各種數字表達轉成 float:
|
| 64 |
- 純數字:23.5
|
| 65 |
+
- 含單位/文字:'23.5°N'、'121.6 E'、'25.3 公里' -> 擷取第一個浮點數
|
| 66 |
- 其他不可解析 -> None
|
| 67 |
"""
|
| 68 |
if x is None:
|
|
|
|
| 91 |
ei = q.get("EarthquakeInfo") or q.get("earthquakeInfo") or {}
|
| 92 |
epic = ei.get("Epicenter") or ei.get("epicenter") or {}
|
| 93 |
|
| 94 |
+
# Magnitude 可能在 Magnitude 或 EarthquakeMagnitude
|
| 95 |
mago = (
|
| 96 |
ei.get("Magnitude") or ei.get("magnitude")
|
| 97 |
or ei.get("EarthquakeMagnitude") or ei.get("earthquakeMagnitude")
|
|
|
|
| 170 |
fig.autofmt_xdate()
|
| 171 |
return _save_fig_to_tmp(fig)
|
| 172 |
|
| 173 |
+
def _auto_region_from_df(d, pad=0.5):
|
| 174 |
+
"""由資料自動推算地圖範圍,並加上邊界緩衝(degrees)。"""
|
| 175 |
+
lon_min = float(pd.to_numeric(d["Lon"], errors="coerce").min())
|
| 176 |
+
lon_max = float(pd.to_numeric(d["Lon"], errors="coerce").max())
|
| 177 |
+
lat_min = float(pd.to_numeric(d["Lat"], errors="coerce").min())
|
| 178 |
+
lat_max = float(pd.to_numeric(d["Lat"], errors="coerce").max())
|
| 179 |
+
return [lon_min - pad, lon_max + pad, lat_min - pad, lat_max + pad]
|
| 180 |
+
|
| 181 |
def plot_map_path(df):
|
| 182 |
"""
|
| 183 |
+
優先使用 PyGMT 畫台灣地圖(含海岸線);若不可用則退回 matplotlib。
|
| 184 |
+
- 自動依據資料決定地圖範圍(避免漏點)
|
| 185 |
+
- 顏色:深度(km);大小:規模
|
| 186 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
if df.empty:
|
| 188 |
return None
|
| 189 |
+
|
| 190 |
+
# 先清一下 NaN
|
| 191 |
+
d = df.dropna(subset=["Lon", "Lat"]).copy()
|
| 192 |
+
if d.empty:
|
| 193 |
+
return None
|
| 194 |
+
|
| 195 |
+
# 數值化
|
| 196 |
+
d["Magnitude"] = pd.to_numeric(d["Magnitude"], errors="coerce").fillna(0).clip(lower=0)
|
| 197 |
+
d["Depth_km"] = pd.to_numeric(d["Depth_km"], errors="coerce").fillna(0)
|
| 198 |
+
|
| 199 |
+
# --- PyGMT 版(DataFrame API + 海岸線 + 自動範圍) ---
|
| 200 |
+
if HAS_PYGMT:
|
| 201 |
+
d["Size"] = 0.06 * (d["Magnitude"] + 1.5) # cm
|
| 202 |
+
region = _auto_region_from_df(d, pad=0.5)
|
| 203 |
+
|
| 204 |
+
fig = pygmt.Figure()
|
| 205 |
+
fig.coast(
|
| 206 |
+
region=region, projection="M12c",
|
| 207 |
+
land="lightgray", water="white",
|
| 208 |
+
shorelines="0.5p,black", borders="1/0.6p,black",
|
| 209 |
+
frame=["WSen", "xaf", "yaf"]
|
| 210 |
+
)
|
| 211 |
+
fig.plot(
|
| 212 |
+
data=d, x="Lon", y="Lat",
|
| 213 |
+
style="c", size="Size",
|
| 214 |
+
color="Depth_km", cmap="roma", pen="0.25p,black"
|
| 215 |
+
)
|
| 216 |
+
fig.colorbar(frame=["x+lDepth (km)"], cmap=True, position="JMR+w7c/0.4c+o0.6c/0c")
|
| 217 |
+
fig.basemap(map_scale="jBL+w50k+o0.6c/0.6c+f+lkm")
|
| 218 |
+
|
| 219 |
+
outpath = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
|
| 220 |
+
fig.savefig(outpath, dpi=220)
|
| 221 |
+
return outpath
|
| 222 |
+
|
| 223 |
+
# --- Matplotlib 備援(自動範圍 + 簡易海岸線無法;以格線為主) ---
|
| 224 |
+
region = _auto_region_from_df(d, pad=0.5)
|
| 225 |
+
lon_min, lon_max, lat_min, lat_max = region
|
| 226 |
+
|
| 227 |
fig, ax = plt.subplots(figsize=(6, 6))
|
| 228 |
ax.set_xlim(lon_min, lon_max)
|
| 229 |
ax.set_ylim(lat_min, lat_max)
|
| 230 |
|
| 231 |
+
s = (d["Magnitude"] + 2) ** 3
|
| 232 |
+
sc = ax.scatter(d["Lon"], d["Lat"], s=s, c=d["Depth_km"], alpha=0.85, edgecolor="black")
|
|
|
|
|
|
|
|
|
|
| 233 |
cb = plt.colorbar(sc, ax=ax, fraction=0.046, pad=0.04)
|
| 234 |
cb.set_label("Depth (km)")
|
| 235 |
+
ax.set_xlabel("Longitude (°E)")
|
| 236 |
+
ax.set_ylabel("Latitude (°N)")
|
| 237 |
+
ax.set_title("Epicenters (auto region)")
|
| 238 |
ax.grid(True, linestyle="--", alpha=0.3)
|
| 239 |
return _save_fig_to_tmp(fig)
|
| 240 |
|