feat: Implement PolyWeather web map API with FastAPI, integrating existing weather data collection and analysis modules.
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
# PolyWeather City Registry
|
||||
# A unified "Source of Truth" for all tracked cities, coordinates, ICAO codes, and risk profiles.
|
||||
|
||||
CITY_REGISTRY = {
|
||||
"ankara": {
|
||||
"name": "Ankara",
|
||||
"lat": 40.1281,
|
||||
"lon": 32.9951,
|
||||
"icao": "LTAC",
|
||||
"tz_offset": 10800,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"airport_name": "Esenboğa 机场",
|
||||
"distance_km": 24.5,
|
||||
"warning": "内陆高原城市,昼夜温差大(可达15°C+); 激进取整效应明显。",
|
||||
},
|
||||
"london": {
|
||||
"name": "London",
|
||||
"lat": 51.5048,
|
||||
"lon": 0.0522,
|
||||
"icao": "EGLC",
|
||||
"tz_offset": 0,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"airport_name": "London City 机场",
|
||||
"distance_km": 12.7,
|
||||
"warning": "泰晤士河局部微气候影响。",
|
||||
},
|
||||
"paris": {
|
||||
"name": "Paris",
|
||||
"lat": 49.0097,
|
||||
"lon": 2.5480,
|
||||
"icao": "LFPG",
|
||||
"tz_offset": 3600,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"airport_name": "Charles de Gaulle 机场",
|
||||
"distance_km": 25.2,
|
||||
"warning": "城市热岛效应:市区比机场偏暖1-2°C。",
|
||||
},
|
||||
"seoul": {
|
||||
"name": "Seoul",
|
||||
"lat": 37.4602,
|
||||
"lon": 126.4407,
|
||||
"icao": "RKSI",
|
||||
"tz_offset": 32400,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "high",
|
||||
"risk_emoji": "🔴",
|
||||
"airport_name": "仁川国际机场",
|
||||
"distance_km": 48.8,
|
||||
"warning": "距离太远,海洋性vs大陆性气候差异大。",
|
||||
},
|
||||
"toronto": {
|
||||
"name": "Toronto",
|
||||
"lat": 43.6777,
|
||||
"lon": -79.6248,
|
||||
"icao": "CYYZ",
|
||||
"tz_offset": -18000,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"airport_name": "Pearson 国际机场",
|
||||
"distance_km": 19.6,
|
||||
"warning": "冬季湖效应偶尔导致局部强降温。",
|
||||
},
|
||||
"buenos aires": {
|
||||
"name": "Buenos Aires",
|
||||
"lat": -34.8222,
|
||||
"lon": -58.5358,
|
||||
"icao": "SAEZ",
|
||||
"tz_offset": -10800,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"airport_name": "Ezeiza 国际机场",
|
||||
"distance_km": 28.1,
|
||||
"warning": "夏天城区可比郊区高2-3°C。",
|
||||
},
|
||||
"wellington": {
|
||||
"name": "Wellington",
|
||||
"lat": -41.3272,
|
||||
"lon": 174.8053,
|
||||
"icao": "NZWN",
|
||||
"tz_offset": 46800,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"airport_name": "惠灵顿国际机场",
|
||||
"distance_km": 5.5,
|
||||
"warning": "风大影响体感,但温度测量偏差小。",
|
||||
},
|
||||
"new york": {
|
||||
"name": "New York",
|
||||
"lat": 40.7769,
|
||||
"lon": -73.8740,
|
||||
"icao": "KLGA",
|
||||
"tz_offset": -18000,
|
||||
"use_fahrenheit": True,
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"airport_name": "LaGuardia 机场",
|
||||
"distance_km": 14.5,
|
||||
"warning": "东河水汽可能在春季产生温差。",
|
||||
},
|
||||
"chicago": {
|
||||
"name": "Chicago",
|
||||
"lat": 41.9742,
|
||||
"lon": -87.9073,
|
||||
"icao": "KORD",
|
||||
"tz_offset": -21600,
|
||||
"use_fahrenheit": True,
|
||||
"risk_level": "high",
|
||||
"risk_emoji": "🔴",
|
||||
"airport_name": "O'Hare 国际机场",
|
||||
"distance_km": 25.3,
|
||||
"warning": "密歇根湖效应:湖边vs内陆可差10°F+。",
|
||||
},
|
||||
"dallas": {
|
||||
"name": "Dallas",
|
||||
"lat": 32.8471,
|
||||
"lon": -96.8518,
|
||||
"icao": "KDAL",
|
||||
"tz_offset": -21600,
|
||||
"use_fahrenheit": True,
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"airport_name": "Dallas Love Field 机场",
|
||||
"distance_km": 11.2,
|
||||
"warning": "城市热岛效应在夏季午后会使温度略高于郊区。",
|
||||
},
|
||||
"miami": {
|
||||
"name": "Miami",
|
||||
"lat": 25.7959,
|
||||
"lon": -80.2870,
|
||||
"icao": "KMIA",
|
||||
"tz_offset": -18000,
|
||||
"use_fahrenheit": True,
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"airport_name": "Miami 国际机场",
|
||||
"distance_km": 10.3,
|
||||
"warning": "温差较小,数据稳定。",
|
||||
},
|
||||
"atlanta": {
|
||||
"name": "Atlanta",
|
||||
"lat": 33.6407,
|
||||
"lon": -84.4277,
|
||||
"icao": "KATL",
|
||||
"tz_offset": -18000,
|
||||
"use_fahrenheit": True,
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"airport_name": "Hartsfield-Jackson 机场",
|
||||
"distance_km": 12.6,
|
||||
"warning": "数据较准。",
|
||||
},
|
||||
"seattle": {
|
||||
"name": "Seattle",
|
||||
"lat": 47.4502,
|
||||
"lon": -122.3088,
|
||||
"icao": "KSEA",
|
||||
"tz_offset": -28800,
|
||||
"use_fahrenheit": True,
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"airport_name": "Sea-Tac 国际机场",
|
||||
"distance_km": 17.4,
|
||||
"warning": "微气候差异存在但较小。",
|
||||
},
|
||||
"lucknow": {
|
||||
"name": "Lucknow",
|
||||
"lat": 26.7606,
|
||||
"lon": 80.8893,
|
||||
"icao": "VILK",
|
||||
"tz_offset": 19800,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"airport_name": "Chaudhary Charan Singh 国际机场",
|
||||
"distance_km": 14.0,
|
||||
"warning": "印度北方热岛效应,夏季午后机场反馈略低于市区。",
|
||||
},
|
||||
"sao paulo": {
|
||||
"name": "São Paulo",
|
||||
"lat": -23.4356,
|
||||
"lon": -46.4731,
|
||||
"icao": "SBGR",
|
||||
"tz_offset": -10800,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "high",
|
||||
"risk_emoji": "🔴",
|
||||
"airport_name": "São Paulo/Guarulhos 机场",
|
||||
"distance_km": 25.0,
|
||||
"warning": "距离远且海拔有差异,局部降雨温差极大。",
|
||||
},
|
||||
"munich": {
|
||||
"name": "Munich",
|
||||
"lat": 48.3538,
|
||||
"lon": 11.7861,
|
||||
"icao": "EDDM",
|
||||
"tz_offset": 3600,
|
||||
"use_fahrenheit": False,
|
||||
"risk_level": "high",
|
||||
"risk_emoji": "🔴",
|
||||
"airport_name": "Munich 机场",
|
||||
"distance_km": 28.5,
|
||||
"warning": "距离非常远,空旷机场夜间降温快,冬季易生大雾压制气温。",
|
||||
},
|
||||
}
|
||||
|
||||
ALIASES = {
|
||||
# English shortcuts
|
||||
"ank": "ankara", "lon": "london", "par": "paris",
|
||||
"nyc": "new york", "ny": "new york", "chi": "chicago",
|
||||
"dal": "dallas", "mia": "miami", "atl": "atlanta",
|
||||
"sea": "seattle", "tor": "toronto", "sel": "seoul",
|
||||
"seo": "seoul", "ba": "buenos aires", "wel": "wellington",
|
||||
"luc": "lucknow", "sp": "sao paulo", "mun": "munich",
|
||||
|
||||
# Chinese names
|
||||
"安卡拉": "ankara",
|
||||
"伦敦": "london",
|
||||
"巴黎": "paris",
|
||||
"纽约": "new york",
|
||||
"芝加哥": "chicago",
|
||||
"达拉斯": "dallas",
|
||||
"迈阿密": "miami",
|
||||
"亚特兰大": "atlanta",
|
||||
"西雅图": "seattle",
|
||||
"多伦多": "toronto",
|
||||
"首尔": "seoul",
|
||||
"布宜诺斯艾利斯": "buenos aires",
|
||||
"惠灵顿": "wellington",
|
||||
"勒克瑙": "lucknow",
|
||||
"圣保罗": "sao paulo",
|
||||
"慕尼黑": "munich",
|
||||
}
|
||||
@@ -1,171 +1,28 @@
|
||||
# Polymarket 城市温度市场 - 数据偏差风险档案
|
||||
# 基于 METAR 机场站与市区实际温度的系统性差异
|
||||
|
||||
from src.data_collection.city_registry import CITY_REGISTRY
|
||||
|
||||
# Generate profiles from registry
|
||||
CITY_RISK_PROFILES = {
|
||||
# 🔴 高危城市 - 数据偏差大,容易误判
|
||||
"seoul": {
|
||||
"risk_level": "high",
|
||||
"risk_emoji": "🔴",
|
||||
"icao": "RKSI",
|
||||
"airport_name": "仁川国际机场",
|
||||
"distance_km": 48.8,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 5.8,
|
||||
"bias_direction": "机场靠海偏暖,市区内陆更冷",
|
||||
"warning": "距离太远,根本不是同一个天气区",
|
||||
"season_notes": None,
|
||||
},
|
||||
"chicago": {
|
||||
"risk_level": "high",
|
||||
"risk_emoji": "🔴",
|
||||
"icao": "KORD",
|
||||
"airport_name": "O'Hare 国际机场",
|
||||
"distance_km": 25.3,
|
||||
"elevation_diff_m": 42,
|
||||
"typical_bias_f": 4.0,
|
||||
"bias_direction": "密歇根湖效应:风向变化时湖边vs内陆可差10°F+",
|
||||
"warning": "冬天温差最不稳定",
|
||||
"season_notes": "冬季",
|
||||
},
|
||||
# 🟡 中危城市 - 存在系统偏差,需注意
|
||||
"ankara": {
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"icao": "LTAC",
|
||||
"airport_name": "Esenboğa 机场",
|
||||
"distance_km": 24.5,
|
||||
"elevation_diff_m": 65,
|
||||
"typical_bias_f": 2.0,
|
||||
"bias_direction": "机场海拔更高",
|
||||
"warning": "内陆高原城市,昼夜温差大(可达15°C+)",
|
||||
"season_notes": "下午最高温时偏差会放大",
|
||||
"metar_rounding": "激进取整:METAR 报告的温度偏高,例如实际 3.4°C 可能报告为 4°C,这意味着 METAR 显示的整数温度往往已接近下一个 WU 结算值。",
|
||||
},
|
||||
"london": {
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"icao": "EGLC",
|
||||
"airport_name": "London City 机场",
|
||||
"distance_km": 12.7,
|
||||
"elevation_diff_m": 4,
|
||||
"typical_bias_f": 0.5,
|
||||
"bias_direction": "河水调节效应:泰晤士河 Royal Docks 使得夏天偏凉,冬天偏暖",
|
||||
"warning": "极端天气日(热浪/寒潮)偏差会显著放大",
|
||||
"season_notes": None,
|
||||
},
|
||||
"dallas": {
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"icao": "KDAL",
|
||||
"airport_name": "Dallas Love Field 机场",
|
||||
"distance_km": 11.2,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 1.1,
|
||||
"bias_direction": "比 DFW 更接近市中心,数据更准",
|
||||
"warning": "城市热岛效应在夏季午后会使温度略高于郊区",
|
||||
"season_notes": None,
|
||||
},
|
||||
"buenos aires": {
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"icao": "SAEZ",
|
||||
"airport_name": "Ezeiza 国际机场",
|
||||
"distance_km": 28.1,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 1.2,
|
||||
"bias_direction": "夏天城区可比郊区高2-3°C",
|
||||
"warning": "距离远但地形平坦,偏差稳定可预测",
|
||||
"season_notes": "夏季",
|
||||
},
|
||||
"paris": {
|
||||
"risk_level": "medium",
|
||||
"risk_emoji": "🟡",
|
||||
"icao": "LFPG",
|
||||
"airport_name": "Charles de Gaulle 机场",
|
||||
"distance_km": 25.2,
|
||||
"elevation_diff_m": 26,
|
||||
"typical_bias_f": 1.5,
|
||||
"bias_direction": "城市热岛效应:市区比机场偏暖1-2°C",
|
||||
"warning": "机场在北郊,冬季北风时比市区更冷",
|
||||
"season_notes": "夏季热浪期间偏差最大",
|
||||
},
|
||||
# 🟢 低危城市 - 数据相对靠谱
|
||||
"toronto": {
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"icao": "CYYZ",
|
||||
"airport_name": "Pearson 国际机场",
|
||||
"distance_km": 19.6,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 0.3,
|
||||
"bias_direction": None,
|
||||
"warning": "冬季湖效应偶尔炸裂",
|
||||
"season_notes": "冬季",
|
||||
},
|
||||
"new york": {
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"icao": "KLGA",
|
||||
"airport_name": "LaGuardia 机场",
|
||||
"distance_km": 14.5,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 0.7,
|
||||
"bias_direction": "相比 JFK 更靠近曼哈顿",
|
||||
"warning": "东河水汽可能在春季产生微小的降温效果",
|
||||
"season_notes": None,
|
||||
},
|
||||
"seattle": {
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"icao": "KSEA",
|
||||
"airport_name": "Sea-Tac 国际机场",
|
||||
"distance_km": 17.4,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 0.6,
|
||||
"bias_direction": "微气候差异存在但较小",
|
||||
"warning": None,
|
||||
"season_notes": None,
|
||||
},
|
||||
"atlanta": {
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"icao": "KATL",
|
||||
"airport_name": "Hartsfield-Jackson 机场",
|
||||
"distance_km": 12.6,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 0.5,
|
||||
"bias_direction": None,
|
||||
"warning": None,
|
||||
"season_notes": None,
|
||||
},
|
||||
"miami": {
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"icao": "KMIA",
|
||||
"airport_name": "Miami 国际机场",
|
||||
"distance_km": 10.3,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 0.3,
|
||||
"bias_direction": None,
|
||||
"warning": None,
|
||||
"season_notes": None,
|
||||
},
|
||||
"wellington": {
|
||||
"risk_level": "low",
|
||||
"risk_emoji": "🟢",
|
||||
"icao": "NZWN",
|
||||
"airport_name": "Wellington 机场",
|
||||
"distance_km": 5.1,
|
||||
"elevation_diff_m": 0,
|
||||
"typical_bias_f": 0.2,
|
||||
"bias_direction": None,
|
||||
"warning": "12城最近,数据最靠谱",
|
||||
"season_notes": None,
|
||||
},
|
||||
cid: {
|
||||
"risk_level": info["risk_level"],
|
||||
"risk_emoji": info["risk_emoji"],
|
||||
"icao": info["icao"],
|
||||
"airport_name": info["airport_name"],
|
||||
"distance_km": info["distance_km"],
|
||||
"warning": info["warning"],
|
||||
# Backwards compatibility flags if needed
|
||||
"typical_bias_f": info.get("typical_bias_f", 0.0),
|
||||
"elevation_diff_m": info.get("elevation_diff_m", 0),
|
||||
"bias_direction": info.get("bias_direction", None),
|
||||
"season_notes": info.get("season_notes", None),
|
||||
}
|
||||
for cid, info in CITY_REGISTRY.items()
|
||||
}
|
||||
|
||||
|
||||
def get_city_risk_profile(city_name: str) -> dict:
|
||||
def get_city_risk_profile(city: str) -> dict:
|
||||
"""获取城市的风险档案"""
|
||||
city_lower = city_name.lower().strip()
|
||||
|
||||
|
||||
@@ -17,24 +17,10 @@ class WeatherDataCollector:
|
||||
- NOAA Aviation Weather (METAR - airport observations)
|
||||
"""
|
||||
|
||||
# Polymarket 12 个天气市场对应的 ICAO 机场代码
|
||||
# 这些是 Weather Underground 结算源使用的气象站
|
||||
CITY_TO_ICAO = {
|
||||
"seattle": "KSEA", # Seattle-Tacoma Airport
|
||||
"london": "EGLC", # London City Airport
|
||||
"dallas": "KDAL", # Dallas Love Field
|
||||
"miami": "KMIA", # Miami International
|
||||
"atlanta": "KATL", # Hartsfield-Jackson
|
||||
"chicago": "KORD", # O'Hare International
|
||||
"new york": "KLGA", # LaGuardia Airport
|
||||
"nyc": "KLGA", # Alias
|
||||
"seoul": "RKSI", # Incheon International
|
||||
"ankara": "LTAC", # Esenboğa International
|
||||
"toronto": "CYYZ", # Toronto Pearson
|
||||
"wellington": "NZWN", # Wellington International
|
||||
"buenos aires": "SAEZ", # Ezeiza International
|
||||
"paris": "LFPG", # Charles de Gaulle
|
||||
}
|
||||
from src.data_collection.city_registry import CITY_REGISTRY
|
||||
CITY_TO_ICAO = {cid: info["icao"] for cid, info in CITY_REGISTRY.items()}
|
||||
# Alias
|
||||
CITY_TO_ICAO["nyc"] = "KLGA"
|
||||
|
||||
# 城市周边 METAR 集群(用于在全球城市模拟类似安卡拉的多测站地图分布)
|
||||
CITY_METAR_CLUSTERS = {
|
||||
@@ -49,6 +35,8 @@ class WeatherDataCollector:
|
||||
"atlanta": ["KATL", "KPDK", "KFTY"],
|
||||
"miami": ["KMIA", "KOPF", "KTMB"],
|
||||
"seattle": ["KSEA", "KBFI", "KPAE"],
|
||||
"sao paulo": ["SBGR", "SBSP", "SBKP"],
|
||||
"munich": ["EDDM", "EDMO", "EDJA"],
|
||||
}
|
||||
|
||||
def __init__(self, config: dict):
|
||||
@@ -1196,34 +1184,29 @@ class WeatherDataCollector:
|
||||
"""
|
||||
使用 Open-Meteo Geocoding API 获取城市坐标 (免费, 无需 Key)
|
||||
"""
|
||||
# 坐标使用 METAR 机场位置(Polymarket 以机场数据结算)
|
||||
static_coords = {
|
||||
"london": {"lat": 51.5053, "lon": 0.0553}, # EGLC London City
|
||||
"paris": {"lat": 49.0097, "lon": 2.5478}, # LFPG Charles de Gaulle
|
||||
"new york": {"lat": 40.7750, "lon": -73.8750}, # KLGA LaGuardia
|
||||
"new york's central park": {"lat": 40.7812, "lon": -73.9665},
|
||||
"nyc": {"lat": 40.7750, "lon": -73.8750}, # KLGA LaGuardia
|
||||
"seattle": {"lat": 47.4499, "lon": -122.3118}, # KSEA Sea-Tac
|
||||
"chicago": {"lat": 41.9769, "lon": -87.9081}, # KORD O'Hare
|
||||
"dallas": {"lat": 32.8459, "lon": -96.8509}, # KDAL Love Field
|
||||
"miami": {"lat": 25.7933, "lon": -80.2906}, # KMIA International
|
||||
"atlanta": {"lat": 33.6367, "lon": -84.4281}, # KATL Hartsfield-Jackson
|
||||
"seoul": {"lat": 37.4691, "lon": 126.4510}, # RKSI Incheon
|
||||
"toronto": {"lat": 43.6759, "lon": -79.6294}, # CYYZ Pearson
|
||||
"ankara": {"lat": 40.1281, "lon": 32.9950}, # LTAC Esenboğa
|
||||
"wellington": {"lat": -41.3272, "lon": 174.8053}, # NZWN Wellington
|
||||
"buenos aires": {"lat": -34.8222, "lon": -58.5358}, # SAEZ Ezeiza
|
||||
}
|
||||
|
||||
from src.data_collection.city_registry import CITY_REGISTRY
|
||||
normalized_city = city.lower().strip()
|
||||
if normalized_city in static_coords:
|
||||
return static_coords[normalized_city]
|
||||
|
||||
# 模糊匹配映射 (针对包含城市名的情况)
|
||||
for key in static_coords:
|
||||
# 1. Check registry first (Source of Truth)
|
||||
if normalized_city in CITY_REGISTRY:
|
||||
info = CITY_REGISTRY[normalized_city]
|
||||
return {"lat": info["lat"], "lon": info["lon"]}
|
||||
|
||||
# 2. Hardcoded specific cases or aliases
|
||||
static_aliases = {
|
||||
"new york's central park": "new york",
|
||||
"nyc": "new york"
|
||||
}
|
||||
if normalized_city in static_aliases:
|
||||
root_city = static_aliases[normalized_city]
|
||||
info = CITY_REGISTRY[root_city]
|
||||
return {"lat": info["lat"], "lon": info["lon"]}
|
||||
|
||||
for key in CITY_REGISTRY:
|
||||
if key in normalized_city:
|
||||
logger.debug(f"地理编码命中模糊映射: {city} -> {key}")
|
||||
return static_coords[key]
|
||||
info = CITY_REGISTRY[key]
|
||||
return {"lat": info["lat"], "lon": info["lon"]}
|
||||
|
||||
try:
|
||||
url = "https://geocoding-api.open-meteo.com/v1/search"
|
||||
|
||||
Reference in New Issue
Block a user