From 6ffbdd97f75778d610d07f84b9c9aa16602317be Mon Sep 17 00:00:00 2001
From: AmandaloveYang <2569718930@qq.com>
Date: Fri, 20 Feb 2026 20:03:49 +0800
Subject: [PATCH] feat: Add multi-source weather data collection with
OpenWeatherMap, Visual Crossing, and NOAA METAR, including city-to-ICAO
mapping and local high temperature calculation.
---
bot_listener.py | 57 +++++++++++++++++++++++++-
src/data_collection/weather_sources.py | 4 +-
2 files changed, 57 insertions(+), 4 deletions(-)
diff --git a/bot_listener.py b/bot_listener.py
index debe1cee..f11d060d 100644
--- a/bot_listener.py
+++ b/bot_listener.py
@@ -292,6 +292,54 @@ def analyze_weather_trend(weather_data, temp_symbol):
if abs(mgm_max - max_so_far) > 1.5:
insights.append(f"📊 数据差异:MGM 官方记录最高 {mgm_max}{temp_symbol},METAR 记录 {max_so_far}{temp_symbol},相差 {abs(mgm_max - max_so_far):.1f}°。")
+ # 10. 太阳辐射分析 (Open-Meteo shortwave_radiation)
+ hourly_rad = hourly.get("shortwave_radiation", [])
+ sunshine_durations = daily.get("sunshine_duration", [])
+ if hourly_rad and times:
+ # 计算今天已经过去的小时的累计辐射 vs 全天预测总辐射
+ today_total_rad = 0.0
+ today_so_far_rad = 0.0
+ today_peak_rad = 0.0
+ today_peak_hour = ""
+ for t_str, rad in zip(times, hourly_rad):
+ if t_str.startswith(local_date_str) and rad is not None:
+ today_total_rad += rad
+ hour_val = int(t_str.split("T")[1][:2])
+ if hour_val <= local_hour:
+ today_so_far_rad += rad
+ if rad > today_peak_rad:
+ today_peak_rad = rad
+ today_peak_hour = t_str.split("T")[1][:5]
+
+ if today_total_rad > 0:
+ rad_pct = today_so_far_rad / today_total_rad * 100
+
+ if not is_peak_passed and local_hour >= 8:
+ # 白天升温期:报告太阳能量进度
+ if rad_pct < 30 and local_hour >= 12:
+ insights.append(f"🌤️ 日照不足:到目前为止只吸收了全天 {rad_pct:.0f}% 的太阳能量,云层可能在严重削弱日照。")
+
+ # 检测"暖平流型"高温:峰值温度出现在太阳辐射极低的时段
+ max_temp_time_str = metar.get("current", {}).get("max_temp_time", "")
+ if max_so_far is not None and max_temp_time_str:
+ try:
+ max_h = int(max_temp_time_str.split(":")[0])
+ # 找到最高温时段对应的辐射值
+ max_temp_rad = 0.0
+ for t_str, rad in zip(times, hourly_rad):
+ if t_str.startswith(local_date_str) and rad is not None:
+ h = int(t_str.split("T")[1][:2])
+ if h == max_h:
+ max_temp_rad = rad
+ break
+ if max_temp_rad < 50 and today_peak_rad > 200:
+ insights.append(
+ f"🌙 暖平流驱动:最高温出现在 {max_temp_time_str},"
+ f"当时太阳辐射仅 {max_temp_rad:.0f} W/m²(峰值 {today_peak_rad:.0f} W/m²),"
+ f"说明气温是被暖空气推高的,而不是被太阳晒热的。"
+ )
+ except (ValueError, IndexError):
+ pass
if not insights:
return ""
@@ -450,13 +498,18 @@ def start_bot():
future_forecasts.append(f"{d[5:]}: {t}{temp_symbol}")
msg_lines.append("📅 " + " | ".join(future_forecasts))
- # --- 3.5 日出日落 ---
+ # --- 3.5 日出日落 + 日照时长 ---
sunrises = daily.get("sunrise", [])
sunsets = daily.get("sunset", [])
+ sunshine_durations = daily.get("sunshine_duration", [])
if sunrises and sunsets:
sunrise_t = sunrises[0].split("T")[1][:5] if "T" in str(sunrises[0]) else sunrises[0]
sunset_t = sunsets[0].split("T")[1][:5] if "T" in str(sunsets[0]) else sunsets[0]
- msg_lines.append(f"🌅 日出 {sunrise_t} | 🌇 日落 {sunset_t}")
+ sun_line = f"🌅 日出 {sunrise_t} | 🌇 日落 {sunset_t}"
+ if sunshine_durations:
+ sunshine_hours = sunshine_durations[0] / 3600 # 秒 -> 小时
+ sun_line += f" | ☀️ 日照 {sunshine_hours:.1f}h"
+ msg_lines.append(sun_line)
# --- 4. 核心 实测区 (合并 METAR 和 MGM) ---
# 基础数据优先用 METAR
diff --git a/src/data_collection/weather_sources.py b/src/data_collection/weather_sources.py
index 1ca3fcaf..fe872f69 100644
--- a/src/data_collection/weather_sources.py
+++ b/src/data_collection/weather_sources.py
@@ -454,8 +454,8 @@ class WeatherDataCollector:
"latitude": lat,
"longitude": lon,
"current_weather": "true",
- "hourly": "temperature_2m",
- "daily": "temperature_2m_max,apparent_temperature_max,sunrise,sunset",
+ "hourly": "temperature_2m,shortwave_radiation",
+ "daily": "temperature_2m_max,apparent_temperature_max,sunrise,sunset,sunshine_duration",
"timezone": "auto",
"forecast_days": forecast_days,
"_t": int(time.time()), # 禁用缓存,强制刷新