cwadayi commited on
Commit
6b09295
·
verified ·
1 Parent(s): eab7c29

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -27
app.py CHANGED
@@ -16,7 +16,13 @@ from linebot.v3.webhooks import MessageEvent, TextMessageContent
16
 
17
  import requests
18
  import pandas as pd
19
- import plotly.express as px
 
 
 
 
 
 
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 kaleido) ---
143
  def create_and_save_map(df: pd.DataFrame) -> str:
144
- fig = px.scatter_geo(
145
- df,
146
- lat="latitude",
147
- lon="longitude",
148
- size="magnitude",
149
- color="magnitude",
150
- hover_name="place",
151
- hover_data={"magnitude": ":.1f", "time_utc": "|%Y-%m-%d %H:%M UTC"},
152
- size_max=24,
153
- color_continuous_scale=px.colors.sequential.YlOrRd,
154
- projection="natural earth",
155
- )
156
- fig.update_layout(
157
- title=f"<b>今年 ({datetime.now(timezone.utc).year}) 台灣區域顯著地震 (M≥5.0)</b>",
158
- margin=dict(r=0, t=40, l=0, b=0),
159
- )
160
- fig.update_geos(
161
- lonaxis_range=[118.5, 123.5],
162
- lataxis_range=[20.5, 26.8],
163
- showcountries=True,
164
- showland=True,
165
- landcolor="#f8f8f8",
166
- countrycolor="#aaa",
 
 
 
 
 
 
 
 
167
  )
 
 
 
 
 
 
 
 
 
 
 
168
  filename = f"map_{uuid.uuid4().hex}.png"
169
  filepath = os.path.join(STATIC_DIR, filename)
170
- fig.write_image(filepath, scale=2, width=900, height=600) # needs kaleido
 
 
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 ---