Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -16,7 +16,13 @@ from linebot.v3.webhooks import MessageEvent, TextMessageContent
|
|
| 16 |
|
| 17 |
import requests
|
| 18 |
import pandas as pd
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
# --- Environment Variables ---
|
| 22 |
CHANNEL_ACCESS_TOKEN = os.getenv("CHANNEL_ACCESS_TOKEN")
|
|
@@ -139,41 +145,61 @@ def fetch_taiwan_df_this_year(min_mag=5.0) -> pd.DataFrame | str:
|
|
| 139 |
except Exception as e:
|
| 140 |
return f"❌ 查詢失敗: {e}"
|
| 141 |
|
| 142 |
-
# --- Map (PNG via
|
| 143 |
def create_and_save_map(df: pd.DataFrame) -> str:
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
)
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
filename = f"map_{uuid.uuid4().hex}.png"
|
| 169 |
filepath = os.path.join(STATIC_DIR, filename)
|
| 170 |
-
fig.
|
|
|
|
|
|
|
| 171 |
return filename
|
| 172 |
|
| 173 |
def _base_url_for_images() -> str:
|
| 174 |
if HF_SPACE_URL:
|
| 175 |
return HF_SPACE_URL.rstrip("/")
|
| 176 |
-
# request.url_root includes trailing slash; safe if running behind proxy
|
| 177 |
return request.url_root.rstrip("/")
|
| 178 |
|
| 179 |
# --- LINE Webhook ---
|
|
|
|
| 16 |
|
| 17 |
import requests
|
| 18 |
import pandas as pd
|
| 19 |
+
|
| 20 |
+
# --- Matplotlib (no GUI) ---
|
| 21 |
+
import matplotlib
|
| 22 |
+
matplotlib.use("Agg") # headless backend
|
| 23 |
+
import matplotlib.pyplot as plt
|
| 24 |
+
from matplotlib.colors import Normalize
|
| 25 |
+
import matplotlib.cm as cm
|
| 26 |
|
| 27 |
# --- Environment Variables ---
|
| 28 |
CHANNEL_ACCESS_TOKEN = os.getenv("CHANNEL_ACCESS_TOKEN")
|
|
|
|
| 145 |
except Exception as e:
|
| 146 |
return f"❌ 查詢失敗: {e}"
|
| 147 |
|
| 148 |
+
# --- Map (PNG via Matplotlib, no Chrome needed) ---
|
| 149 |
def create_and_save_map(df: pd.DataFrame) -> str:
|
| 150 |
+
# Figure
|
| 151 |
+
fig, ax = plt.subplots(figsize=(9, 6), dpi=150)
|
| 152 |
+
|
| 153 |
+
# Taiwan bounding box & grid
|
| 154 |
+
lon_min, lon_max = 118.5, 123.5
|
| 155 |
+
lat_min, lat_max = 20.5, 26.8
|
| 156 |
+
ax.set_xlim(lon_min, lon_max)
|
| 157 |
+
ax.set_ylim(lat_min, lat_max)
|
| 158 |
+
ax.set_xlabel("Longitude (°E)")
|
| 159 |
+
ax.set_ylabel("Latitude (°N)")
|
| 160 |
+
ax.set_title(f"今年 ({datetime.now(timezone.utc).year}) 台灣區域顯著地震 (M≥5.0) — UTC")
|
| 161 |
+
|
| 162 |
+
ax.grid(True, linestyle="--", linewidth=0.5, alpha=0.4)
|
| 163 |
+
|
| 164 |
+
# Scatter colored by magnitude
|
| 165 |
+
mags = df["magnitude"].astype(float).clip(lower=0)
|
| 166 |
+
norm = Normalize(vmin=max(4.5, mags.min()), vmax=max(6.5, mags.max()))
|
| 167 |
+
cmap = cm.get_cmap("YlOrRd")
|
| 168 |
+
colors = cmap(norm(mags.values))
|
| 169 |
+
|
| 170 |
+
# Size scale (points^2): tune for LINE preview
|
| 171 |
+
sizes = 15 + (mags - mags.min()) * 25 # 15–approx 140
|
| 172 |
+
|
| 173 |
+
sc = ax.scatter(
|
| 174 |
+
df["longitude"].values,
|
| 175 |
+
df["latitude"].values,
|
| 176 |
+
s=sizes,
|
| 177 |
+
c=colors,
|
| 178 |
+
edgecolor="k",
|
| 179 |
+
linewidths=0.4,
|
| 180 |
+
alpha=0.9,
|
| 181 |
)
|
| 182 |
+
|
| 183 |
+
# Colorbar
|
| 184 |
+
cbar = fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax, pad=0.02)
|
| 185 |
+
cbar.set_label("Magnitude")
|
| 186 |
+
|
| 187 |
+
# Legend example (size ~ mag)
|
| 188 |
+
for m in [5.0, 6.0, 7.0]:
|
| 189 |
+
ax.scatter([], [], s=15 + (m - mags.min()) * 25, c="none", edgecolor="k", label=f"M {m:.1f}")
|
| 190 |
+
ax.legend(title="Size ∝ Magnitude", loc="lower right", framealpha=0.9)
|
| 191 |
+
|
| 192 |
+
# Save
|
| 193 |
filename = f"map_{uuid.uuid4().hex}.png"
|
| 194 |
filepath = os.path.join(STATIC_DIR, filename)
|
| 195 |
+
fig.tight_layout()
|
| 196 |
+
fig.savefig(filepath)
|
| 197 |
+
plt.close(fig)
|
| 198 |
return filename
|
| 199 |
|
| 200 |
def _base_url_for_images() -> str:
|
| 201 |
if HF_SPACE_URL:
|
| 202 |
return HF_SPACE_URL.rstrip("/")
|
|
|
|
| 203 |
return request.url_root.rstrip("/")
|
| 204 |
|
| 205 |
# --- LINE Webhook ---
|