diff --git a/frontend/lib/dashboard-official-sources.ts b/frontend/lib/dashboard-official-sources.ts index 2181009e..150668cc 100644 --- a/frontend/lib/dashboard-official-sources.ts +++ b/frontend/lib/dashboard-official-sources.ts @@ -126,6 +126,47 @@ const CITY_SPECIFIC_SOURCES: Record = { kind: "metar", }, ], + manila: [ + { + label: "Wunderground RPLL", + href: "https://www.wunderground.com/history/daily/ph/manila/RPLL", + kind: "agency", + }, + { + label: "Ninoy Aquino International Airport", + href: "https://www.newnaia.com.ph/", + kind: "airport", + }, + { + label: "RPLL METAR", + href: "https://aviationweather.gov/data/metar/?id=RPLL&decoded=1&taf=1", + kind: "metar", + }, + ], + karachi: [ + { + label: "Wunderground OPKC", + href: "https://www.wunderground.com/history/daily/pk/karachi/OPKC", + kind: "agency", + }, + { + label: "Jinnah International Airport", + href: "https://www.caapakistan.com.pk/", + kind: "airport", + }, + { + label: "OPKC METAR", + href: "https://aviationweather.gov/data/metar/?id=OPKC&decoded=1&taf=1", + kind: "metar", + }, + ], + "masroor air base": [ + { + label: "OPMR METAR", + href: "https://aviationweather.gov/data/metar/?id=OPMR&decoded=1&taf=1", + kind: "metar", + }, + ], istanbul: [ { label: "MGM", diff --git a/src/data_collection/city_registry.py b/src/data_collection/city_registry.py index 5dff285b..a028643f 100644 --- a/src/data_collection/city_registry.py +++ b/src/data_collection/city_registry.py @@ -228,6 +228,58 @@ CITY_REGISTRY = { "distance_km": 12.0, "warning": "市场现按 Wunderground Halim 机场站整度°C口径结算;热带午后阵雨常在峰值前后快速压制气温。", }, + "manila": { + "name": "Manila", + "lat": 14.5086, + "lon": 121.0198, + "icao": "RPLL", + "settlement_source": "wunderground", + "settlement_station_code": "RPLL", + "settlement_station_label": "Manila/Ninoy Aquino International Airport Station", + "settlement_url": "https://www.wunderground.com/history/daily/ph/manila/RPLL", + "tz_offset": 28800, + "use_fahrenheit": False, + "is_major": True, + "risk_level": "medium", + "risk_emoji": "🟡", + "airport_name": "尼诺伊·阿基诺国际机场", + "distance_km": 10.0, + "warning": "市场现按 Wunderground 尼诺伊·阿基诺国际机场站整度°C口径结算;海风、湿热与午后对流会压缩日最高温窗口。", + }, + "karachi": { + "name": "Karachi", + "lat": 24.9065, + "lon": 67.1608, + "icao": "OPKC", + "settlement_source": "wunderground", + "settlement_station_code": "OPKC", + "settlement_station_label": "Karachi/Jinnah International Airport Station", + "settlement_url": "https://www.wunderground.com/history/daily/pk/karachi/OPKC", + "tz_offset": 18000, + "use_fahrenheit": False, + "is_major": True, + "risk_level": "medium", + "risk_emoji": "🟡", + "airport_name": "真纳国际机场", + "distance_km": 15.0, + "warning": "市场现按 Wunderground 真纳国际机场站整度°C口径结算;海风锋、干热输送与尘霾会影响峰值兑现时间。", + }, + "masroor air base": { + "name": "Masroor Air Base", + "lat": 24.8936, + "lon": 66.9388, + "icao": "OPMR", + "settlement_station_code": "OPMR", + "settlement_station_label": "Masroor Air Base METAR", + "tz_offset": 18000, + "use_fahrenheit": False, + "is_major": False, + "risk_level": "medium", + "risk_emoji": "🟡", + "airport_name": "Masroor Air Base", + "distance_km": 0.0, + "warning": "军事机场 METAR 锚点;公开报文可能不连续,若 OPMR 缺报则不要自动替换为 OPKC 结算口径。", + }, "tokyo": { "name": "Tokyo", "lat": 35.5523, @@ -777,6 +829,9 @@ ALIASES = { "sg": "singapore", "tok": "tokyo", "tyo": "tokyo", "kul": "kuala lumpur", "wmkk": "kuala lumpur", "sepang": "kuala lumpur", "jkt": "jakarta", "wihh": "jakarta", "halim": "jakarta", + "mnl": "manila", "manila": "manila", "rpll": "manila", + "khi": "karachi", "karachi": "karachi", "opkc": "karachi", "jinnah": "karachi", + "opmr": "masroor air base", "masroor": "masroor air base", "masroor airbase": "masroor air base", "hel": "helsinki", "efhk": "helsinki", "vantaa": "helsinki", "ams": "amsterdam", "eham": "amsterdam", "schiphol": "amsterdam", "pty": "panama city", "mpmg": "panama city", "panamacity": "panama city", @@ -820,6 +875,13 @@ ALIASES = { "吉隆坡": "kuala lumpur", "雅加达": "jakarta", "雅加達": "jakarta", + "马尼拉": "manila", + "馬尼拉": "manila", + "卡拉奇": "karachi", + "马斯鲁尔": "masroor air base", + "馬斯魯爾": "masroor air base", + "马斯鲁尔空军基地": "masroor air base", + "馬斯魯爾空軍基地": "masroor air base", "东京": "tokyo", "東京": "tokyo", "特拉维夫": "tel aviv", diff --git a/src/data_collection/weather_sources.py b/src/data_collection/weather_sources.py index 80eb50db..f9e82371 100644 --- a/src/data_collection/weather_sources.py +++ b/src/data_collection/weather_sources.py @@ -61,6 +61,9 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour "singapore": ["WSSS", "WSAP", "WMKK"], "kuala lumpur": ["WMKK", "WMSA"], "jakarta": ["WIHH", "WIII"], + "manila": ["RPLL"], + "karachi": ["OPKC", "OPMR"], + "masroor air base": ["OPMR", "OPKC"], "tokyo": ["RJTT", "RJAA", "RJAH", "RJTJ"], "tel aviv": ["LLBG"], "milan": ["LIMC", "LIML", "LIME", "LIPO"], @@ -608,6 +611,18 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour "jakarta": "Jakarta", "雅加达": "Jakarta", "雅加達": "Jakarta", + "manila": "Manila", + "rpll": "Manila", + "马尼拉": "Manila", + "馬尼拉": "Manila", + "karachi": "Karachi", + "opkc": "Karachi", + "卡拉奇": "Karachi", + "masroor": "Masroor Air Base", + "masroor air base": "Masroor Air Base", + "opmr": "Masroor Air Base", + "马斯鲁尔": "Masroor Air Base", + "馬斯魯爾": "Masroor Air Base", "helsinki": "Helsinki", "vantaa": "Helsinki", "赫尔辛基": "Helsinki", diff --git a/tests/test_country_networks.py b/tests/test_country_networks.py index 43f5de0a..cc6f79cf 100644 --- a/tests/test_country_networks.py +++ b/tests/test_country_networks.py @@ -1,7 +1,19 @@ from src.data_collection.country_networks import build_country_network_snapshot +from src.data_collection.city_registry import ALIASES, CITY_REGISTRY from web.analysis_service import _build_city_detail_payload +def test_new_south_asia_city_registry_entries_are_wired(): + assert CITY_REGISTRY["manila"]["settlement_source"] == "wunderground" + assert CITY_REGISTRY["manila"]["settlement_station_code"] == "RPLL" + assert CITY_REGISTRY["karachi"]["settlement_source"] == "wunderground" + assert CITY_REGISTRY["karachi"]["settlement_station_code"] == "OPKC" + assert CITY_REGISTRY["masroor air base"]["icao"] == "OPMR" + assert ALIASES["rpll"] == "manila" + assert ALIASES["opkc"] == "karachi" + assert ALIASES["opmr"] == "masroor air base" + + def test_turkey_mgm_provider_returns_official_nearby_rows(): raw = { "metar": {