From f0940b9c8b2ad90dccfad9dda4c85c399db0cede Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 13 Apr 2026 11:02:45 +0800 Subject: [PATCH] Speed up JMA nearby refresh and link Tokyo to 10-minute data --- .env.example | 1 + frontend/hooks/useLeafletMap.ts | 4 +-- frontend/lib/dashboard-official-sources.ts | 34 ++++++++++++++++++++-- src/data_collection/weather_sources.py | 16 +++++++++- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index f4a269ca..4688132d 100644 --- a/.env.example +++ b/.env.example @@ -53,6 +53,7 @@ POLYWEATHER_OPEN_METEO_TIMEOUT_SEC=5 POLYWEATHER_METAR_TIMEOUT_SEC=4 POLYWEATHER_METAR_CLUSTER_TIMEOUT_SEC=3.5 METAR_CACHE_TTL_SEC=600 +JMA_AMEDAS_CACHE_TTL_SEC=120 METEOBLUE_CACHE_TTL_SEC=7200 POLYWEATHER_LGBM_ENABLED=false POLYWEATHER_LGBM_MODEL_PATH=/app/artifacts/models/lgbm_daily_high.txt diff --git a/frontend/hooks/useLeafletMap.ts b/frontend/hooks/useLeafletMap.ts index ff7e17a4..69107d88 100644 --- a/frontend/hooks/useLeafletMap.ts +++ b/frontend/hooks/useLeafletMap.ts @@ -31,8 +31,8 @@ interface UseLeafletMapArgs { const AUTO_NEARBY_MIN_ZOOM = 8; const AUTO_NEARBY_MAX_DISTANCE_M = 120000; -const AUTO_NEARBY_IDLE_REFRESH_DELAY_MS = 20_000; -const AUTO_NEARBY_MIN_REFRESH_INTERVAL_MS = 90_000; +const AUTO_NEARBY_IDLE_REFRESH_DELAY_MS = 10_000; +const AUTO_NEARBY_MIN_REFRESH_INTERVAL_MS = 60_000; const MAP_MAX_ZOOM = 19; const CITY_MARKER_DISPLAY_OFFSETS: Record< string, diff --git a/frontend/lib/dashboard-official-sources.ts b/frontend/lib/dashboard-official-sources.ts index 5852ec0b..2181009e 100644 --- a/frontend/lib/dashboard-official-sources.ts +++ b/frontend/lib/dashboard-official-sources.ts @@ -6,6 +6,23 @@ export type OfficialSourceLink = { kind: "agency" | "airport" | "metar"; }; +function buildJmaAmedasTenMinuteUrl( + localDate?: string | null, + options?: { + blockNo?: string; + precNo?: string; + }, +) { + const blockNo = String(options?.blockNo || "0371").trim() || "0371"; + const precNo = String(options?.precNo || "44").trim() || "44"; + const match = String(localDate || "").match(/^(\d{4})-(\d{2})-(\d{2})$/); + const now = new Date(); + const year = match?.[1] || String(now.getFullYear()); + const month = match?.[2] || String(now.getMonth() + 1).padStart(2, "0"); + const day = match?.[3] || String(now.getDate()).padStart(2, "0"); + return `https://www.data.jma.go.jp/stats/etrn/view/10min_a1.php?prec_no=${encodeURIComponent(precNo)}&block_no=${encodeURIComponent(blockNo)}&year=${encodeURIComponent(year)}&month=${encodeURIComponent(month)}&day=${encodeURIComponent(day)}&view=`; +} + const CITY_SPECIFIC_SOURCES: Record = { singapore: [ { @@ -329,8 +346,8 @@ const CITY_SPECIFIC_SOURCES: Record = { ], tokyo: [ { - label: "JMA", - href: "https://www.jma.go.jp/jma/indexe.html", + label: "JMA 羽田10分钟实况", + href: "", kind: "agency", }, { @@ -681,7 +698,18 @@ const CITY_SPECIFIC_SOURCES: Record = { export function getOfficialSourceLinks(detail: CityDetail): OfficialSourceLink[] { const cityKey = String(detail.name || "").trim().toLowerCase(); - const links = [...(CITY_SPECIFIC_SOURCES[cityKey] || [])]; + const links = [...(CITY_SPECIFIC_SOURCES[cityKey] || [])].map((link) => { + if (cityKey === "tokyo" && link.kind === "agency" && link.label === "JMA 羽田10分钟实况") { + return { + ...link, + href: buildJmaAmedasTenMinuteUrl(detail.local_date, { + blockNo: "0371", + precNo: "44", + }), + }; + } + return link; + }); const seen = new Set(); return links.filter((link) => { const key = `${link.label}|${link.href}`; diff --git a/src/data_collection/weather_sources.py b/src/data_collection/weather_sources.py index 31c7de90..5a475117 100644 --- a/src/data_collection/weather_sources.py +++ b/src/data_collection/weather_sources.py @@ -183,7 +183,7 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour self._nmc_cache: Dict[str, Dict] = {} self._nmc_cache_lock = threading.Lock() self.jma_cache_ttl_sec = int( - os.getenv("JMA_AMEDAS_CACHE_TTL_SEC", "300") + os.getenv("JMA_AMEDAS_CACHE_TTL_SEC", "120") ) self._jma_cache: Dict[str, Dict] = {} self._jma_cache_lock = threading.Lock() @@ -700,6 +700,14 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour if key.startswith(prefix): self._metar_cache.pop(key, None) normalized = str(city or "").strip().lower() + with self._jma_cache_lock: + self._jma_cache.pop(f"{normalized}:{use_fahrenheit}", None) + with self._kma_cache_lock: + self._kma_cache.pop(f"{normalized}:{use_fahrenheit}", None) + with self._nmc_cache_lock: + self._nmc_cache.pop(f"{normalized}:{use_fahrenheit}", None) + with self._ru_station_cache_lock: + self._ru_station_cache.pop(f"{normalized}:{use_fahrenheit}", None) with self._settlement_cache_lock: city_meta = self.CITY_REGISTRY.get(normalized) or {} settlement_source = str(city_meta.get("settlement_source") or "").strip().lower() @@ -717,6 +725,12 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour or normalized ) self._settlement_cache.pop(f"noaa:{station_code.lower()}", None) + elif settlement_source == "cwa": + station_code = ( + str(city_meta.get("settlement_station_code") or "").strip() + or normalized + ) + self._settlement_cache.pop(f"cwa:{station_code.lower()}", None) def _uses_fahrenheit(self, city_lower: str) -> bool: return city_lower in self.US_CITIES