블로그 목록

지도 투영법의 종류와 기상청 격자 변환(람베르트 정각원추도법) 이해하기

기상청 단기예보 API를 처음 써 보면, 위도·경도가 아니라 nx=60, ny=127 같은 격자 번호를 넣어야 한다는 점에서 당황하게 됩니다. 이 격자 번호는 둥근 지구 위의 위치를 평면 지도에 펼친 뒤 5km 간격 칸으로 나눈 결과입니다. 둥근 표면을 평면으로 펼치는 방법을 지도 투영법이라고 하며, 기상청 격자는 그중 람베르트 정각원추도법(Lambert Conformal Conic)을 사용합니다. 이 글에서는 투영법의 기본 개념을 설명하고, 공개된 변환 코드로 실제 위경도를 격자 번호로 바꾸는 과정을 예제와 함께 살펴봅니다.

1. 지도 투영법이 필요한 이유

귤껍질을 찢지 않고 평평하게 펼칠 수 없듯이, 곡면인 지구 표면을 평면으로 옮기면 반드시 어딘가에 왜곡이 생깁니다. 그래서 투영법은 무엇을 지킬지에 따라 나뉩니다.

  • 정각 도법: 좁은 범위에서 각도와 모양을 보존합니다. 메르카토르 도법, 람베르트 정각원추도법이 대표적이며, 방향이 중요한 항해도나 기상도에 널리 쓰입니다.
  • 정적(등적) 도법: 면적 비율을 보존합니다. 분포를 비교하는 통계 지도에 적합합니다.
  • 정거(등거) 도법: 특정 점이나 특정 선을 기준으로 한 거리를 보존합니다. 모든 방향의 모든 거리를 보존하는 평면 지도는 없습니다.
  • 절충 도법: 어느 하나를 완전히 보존하지 않고 전체적인 왜곡을 줄입니다. 세계 지도에 쓰이는 로빈슨 도법이 예입니다.

2. 람베르트 정각원추도법의 특징

지구에 원뿔을 씌우고, 지표를 원뿔 면에 정각 조건을 지키며 옮긴 뒤 원뿔을 펼친다고 생각하면 이해하기 쉽습니다. 원뿔이 지구를 자르는 두 위도선을 표준위선이라고 하며, 이 선 위에서는 축척이 정확합니다. 표준위선에서 멀어질수록 축척 오차가 커지므로, 이 도법은 중위도에서 동서 방향으로 넓은 지역을 표현할 때 왜곡이 작습니다. 기상 모델과 예보처럼 한반도 주변의 중위도 영역을 넓게 다루는 용도에 자주 쓰이는 이유입니다.

3. 기상청 격자의 정의값

기상청 단기예보 조회서비스 활용 자료에 포함된 변환 코드는 다음 값을 사용합니다.

변환에 쓰이는 상수

  • 지구 반지름 RE = 6371.00877km (지구를 구로 가정)
  • 격자 간격 GRID = 5km
  • 표준위선 SLAT1 = 30°, SLAT2 = 60°
  • 기준점 위도 OLAT = 38°, 기준점 경도 OLON = 126°
  • 기준점의 격자 번호 XO = 43, YO = 136

즉, 북위 38°·동경 126° 지점이 격자 (43, 136)이 되고, 동쪽으로 갈수록 x가, 북쪽으로 갈수록 y가 커집니다. 타원체가 아니라 구를 가정한 단순한 형태이므로, 측량용 좌표계와 섞어 쓰지 않도록 주의해야 합니다.

4. 위경도를 격자 번호로 바꾸는 코드

아래는 널리 쓰이는 JavaScript 변환 코드를 정리한 것입니다. 앞부분은 상수에서 투영에 필요한 값(sn, sf, ro)을 계산하고, 뒷부분은 입력 좌표의 원뿔 위 위치(ra, theta)를 구해 격자 번호로 반올림합니다.

function toKmaGrid(lat, lon) {
  const RE = 6371.00877, GRID = 5.0;
  const SLAT1 = 30.0, SLAT2 = 60.0, OLON = 126.0, OLAT = 38.0;
  const XO = 43, YO = 136;
  const DEGRAD = Math.PI / 180.0;

  const re = RE / GRID;
  const slat1 = SLAT1 * DEGRAD;
  const slat2 = SLAT2 * DEGRAD;
  const olon = OLON * DEGRAD;
  const olat = OLAT * DEGRAD;

  let sn = Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5);
  sn = Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(sn);
  let sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
  sf = (Math.pow(sf, sn) * Math.cos(slat1)) / sn;
  let ro = Math.tan(Math.PI * 0.25 + olat * 0.5);
  ro = (re * sf) / Math.pow(ro, sn);

  let ra = Math.tan(Math.PI * 0.25 + lat * DEGRAD * 0.5);
  ra = (re * sf) / Math.pow(ra, sn);
  let theta = lon * DEGRAD - olon;
  if (theta > Math.PI) theta -= 2.0 * Math.PI;
  if (theta < -Math.PI) theta += 2.0 * Math.PI;
  theta *= sn;

  return {
    nx: Math.floor(ra * Math.sin(theta) + XO + 0.5),
    ny: Math.floor(ro - ra * Math.cos(theta) + YO + 0.5),
  };
}

실제 좌표로 확인하기

위치(부근)위도, 경도격자 (nx, ny)
기준점38.0, 126.043, 136
서울시청37.5665, 126.978060, 127
부산시청35.1796, 129.075698, 76
제주시청33.4996, 126.531253, 38

위 결과는 이 코드로 직접 계산한 값입니다. 구현을 새로 만들었다면 기상청이 활용가이드와 함께 제공하는 행정구역별 격자 좌표 엑셀 자료와 몇 곳을 대조해 보는 것이 가장 확실한 검증 방법입니다.

계산한 nx, ny는 단기예보 조회서비스의 요청 파라미터에 그대로 넣습니다. 같은 활용 자료의 변환 코드에는 격자 번호를 다시 위경도로 바꾸는 역변환도 포함되어 있어, 격자 중심의 위치를 지도에 표시할 때 사용할 수 있습니다.

5. 자주 하는 실수

  • 위도와 경도를 바꿔 넣기: 함수의 인자 순서를 확인합니다. 서울을 넣었는데 격자 번호가 터무니없이 크거나 작다면 순서부터 의심합니다.
  • 상수를 임의로 바꾸기: 지구 반지름을 6371km나 GRS80 값으로 바꾸거나 기준점을 수정하면 기상청 격자와 어긋납니다. 상수는 공개된 값을 그대로 사용합니다.
  • 격자 경계 근처의 차이: 반올림으로 칸을 정하므로, 경계에 가까운 위치는 좌표가 조금만 달라도 옆 격자가 됩니다. 이는 오류가 아니라 격자 방식의 특성입니다.
  • 격자와 행정동을 같은 것으로 생각하기: 한 행정동이 여러 격자에 걸치거나 한 격자에 여러 동이 들어갈 수 있습니다.

6. 정리

지도 투영법은 곡면을 평면으로 옮기면서 무엇을 보존할지 선택하는 방법이며, 기상청 단기예보 격자는 표준위선 30°·60°의 람베르트 정각원추도법과 구형 지구 가정 위에서 5km 간격으로 정의됩니다. 공개된 상수를 그대로 쓰고 기상청 자료와 대조해 검증하면 안정적으로 위경도를 격자 번호로 바꿀 수 있습니다.

HereWeather 역시 현재 위치를 기상청 단기예보 격자로 변환해 예보를 불러옵니다. 위경도 자체의 좌표계는 WGS84와 UTM-K 비교에서, 예보 종류의 차이는 초단기·단기·중기예보의 차이에서 확인할 수 있습니다.

참고 자료