ASOS 시간자료/일자료가 승인돼서, 사상 누적을 Open-Meteo 추정에서 기상청 관측으로 바꾼다. 두 소스의 창이 정확히 맞물린다 — ASOS는 "전날 자료까지"(오늘은 resultCode 99), 초단기실황은 "최근 1일"이라 자정에서 나누면 사상 전체가 관측값이 된다. rn 인코딩 주의: ""=무강수, "0.0"=미량(관측됐으나 0.05mm 미만). 둘을 뭉개면 이슬비로 이어지던 비가 소강으로 잡혀 사상이 끊긴다. 그래서 findRainEpisode가 wet을 명시로 받는다(안 주면 종전 mm 임계). 지점 좌표는 일부러 없다. 최근접 지점 방식을 만들다 폐기했다: 지점명 지오코딩이 홍성·보령·밀양을 북한 동명 지역으로, 남원을 제주도로 돌려줬다. 검증하려고 ASOS 관측기온 vs Open-Meteo 기온을 대조했으나 좌표가 정확한 서울조차 Δ3.1°C(도시열섬·모델편차)라 판별자가 못 됐다. 이름 매칭만 쓰고 못 맞히면 Open-Meteo로 물러난다. 지점 목록 97개는 지점정보 API가 없어서(NO_OPENAPI_SERVICE) 일자료 엔드포인트로 stnIds 90~300을 전수조사해 실응답에서 뽑았다. 같이 고친 것: latLonToKmaGrid가 공식 변환식의 반올림 항을 +0.5가 아닌 +1.5로 써서 nx·ny를 나란히 한 칸씩(대각 ~7km) 밀어 조회했다. 이름이 KMA_GRID 표에 있는 도시는 표를 타서 멀쩡했고 좌표 입력만 틀려서 여태 안 드러났다. 같은 서울을 이름과 좌표로 조회했을 때 일 누적이 9.8mm vs 29mm로 갈리며 발견. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
41 lines
1.9 KiB
TypeScript
41 lines
1.9 KiB
TypeScript
/**
|
|
* kma-grid.test.ts
|
|
*
|
|
* 좌표 → 기상청 격자(nx, ny) 변환.
|
|
*
|
|
* 2026-08-16에 이 함수가 nx·ny를 나란히 한 칸씩 밀어서 조회하고 있었다는 걸 발견했다.
|
|
* 기상청 공식 변환식의 마지막 항은 반올림용 +0.5인데 코드엔 +1.5가 들어 있었다.
|
|
* 격자 1칸이 5km라 대각선으로 ~7km 떨어진 셀을 읽고 있었고, 같은 서울을 이름("서울")과
|
|
* 좌표("37.5665,126.978")로 조회했을 때 오늘 누적 강수량이 9.8mm vs 29mm로 갈리면서 드러났다.
|
|
*
|
|
* 이름이 KMA_GRID 표에 있는 도시는 표를 타므로 멀쩡했고, 좌표로 들어온 위치만 틀렸다.
|
|
* 그래서 몇 년 동안 아무도 못 봤다 — 아래 기대값은 그 표의 값이고, 변환식이 같은 답을
|
|
* 내는지 대조하는 게 이 테스트의 요지다.
|
|
*/
|
|
|
|
import { test, describe } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { latLonToKmaGrid } from '../src/tools/weather';
|
|
|
|
describe('latLonToKmaGrid — 좌표에서 기상청 격자 좌표', () => {
|
|
// 기대값은 기상청이 배포하는 "동네예보 격자" 표의 값(= 코드 내 KMA_GRID 표와 동일).
|
|
const cases: Array<[string, number, number, number, number]> = [
|
|
['서울', 37.5665, 126.9780, 60, 127],
|
|
['부산', 35.1796, 129.0756, 98, 76],
|
|
['대전', 36.3504, 127.3845, 67, 100],
|
|
['구미', 36.1195, 128.3446, 84, 96],
|
|
];
|
|
|
|
for (const [name, lat, lon, nx, ny] of cases) {
|
|
test(`${name} (${lat}, ${lon}) → ${nx}, ${ny}`, () => {
|
|
assert.deepEqual(latLonToKmaGrid(lat, lon), { nx, ny });
|
|
});
|
|
}
|
|
|
|
test('+1 밀림 회귀 방어: 서울이 61,128로 나오면 안 된다', () => {
|
|
// 버그 당시의 정확한 출력. 상수를 되돌리면 이 테스트부터 깨진다.
|
|
const g = latLonToKmaGrid(37.5665, 126.978);
|
|
assert.notDeepEqual(g, { nx: 61, ny: 128 });
|
|
});
|
|
});
|