Geographic Distance Calculator 개발

TypeScript로 구현한 지리적 거리 계산기

플래글 익스플로러(Flagle Explorer)라는 게임에서 우리는 사용자가 세계 지리를 배우는 데 도움을 주기 위한 상호작용 피드백을 제공하고 있습니다. 이를 위해 TypeScript를 사용하여 효율적인 지리적 계산 시스템을 구현했습니다. 이 시스템은 Haversine 공식을 이용한 거리 계산, 방향 안내를 위한 수평 보정 계산, 및 실시간 반응을 위한 지리적 근접성 점수 계산을 포함합니다.

주요 도전 과제

우리에게 주어진 기술적 과제는 다음과 같습니다:

  • 지구상의 두 지점 간의 정확한 거리 계산
  • 방향성 안내를 위한 정밀한 수평 보정 계산
  • 사용자 친화적인 방향 안내
  • 즉각적인 피드백을 위한 실시간 성능 유지

구현 세부 사항

  1. 기본 데이터 구조
    우리는 지리적 포인트 인터페이스를 정의했습니다:

    export interface GeoPoint {
      lat: number;  // 위도 (도 단위)
      lon: number;  // 경도 (도 단위)
    }
    
  2. 거리 계산 구현
    Haversine 공식을 사용하여 두 점 사이의 대원 거리를 계산했습니다:

    export function calculateDistance(point1: GeoPoint, point2: GeoPoint): number {
      // 동일한 지점에 대한 조기 반환
      if (point1.lat === point2.lat && point1.lon === point2.lon) {
        return 0;
      }
    
      const R = 6371000; // 지구의 반지름 (미터)
      const dLat = (point2.lat - point1.lat) * Math.PI / 180;
      const dLon = (point2.lon - point1.lon) * Math.PI / 180;
      const lat1 = point1.lat * Math.PI / 180;
      const lat2 = point2.lat * Math.PI / 180;
    
      const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.cos(lat1) * Math.cos(lat2) *
        Math.sin(dLon/2) * Math.sin(dLon/2);
      const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    
      return (R * c) / 1000; // 킬로미터로 변환
    }
    
  3. 방위 계산 시스템

직관적인 방향 안내를 제공하기 위해 우리는 복잡한 각도 수학을 사용자 친화적인 방향 지표로 변환했습니다:

export function calculateOrientation(point1: GeoPoint, point2: GeoPoint): number {
  if (point1.lat === point2.lat && point1.lon === point2.lon) return 0;

  const lat1 = point1.lat * Math.PI / 180;
  const lat2 = point2.lat * Math.PI / 180;
  const dLon = (point2.lon - point1.lon) * Math.PI / 180;

  const y = Math.sin(dLon) * Math.cos(lat2);
  const x = Math.cos(lat1) * Math.sin(lat2) -
           Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);

  let bearing = Math.atan2(y, x) * 180 / Math.PI;
  return (bearing + 360) % 360;
}
  1. 사용자 친화적 방향 매핑
    방향 안내를 더욱 사용자 친화적으로 만들기 위해 다음과 같이 방향을 이모지로 매핑했습니다:
    export function calculateOrientationEmoji(point1: GeoPoint, point2: GeoPoint): string {
      const orientation = calculateOrientation(point1, point2);
      if (orientation >= 337.5 || orientation < 22.5) return '⬆️';
      if (orientation >= 22.5 && orientation < 67.5) return '↗️';
      if (orientation >= 67.5 && orientation < 112.5) return '➡️';
      if (orientation >= 112.5 && orientation < 157.5) return '↘️';
      if (orientation >= 157.5 && orientation < 202.5) return '⬇️';
      if (orientation >= 202.5 && orientation < 247.5) return '↙️';
      if (orientation >= 247.5 && orientation < 292.5) return '⬅️';
      return '↖️';
    }
    

성능 고려 사항

  • 동일점에 대한 조기 반환을 통해 불필요한 계산을 방지했습니다.
  • 지구의 반지름 및 각도-라디안 변환을 사전 계산하여 최적화했습니다.
  • 숫자를 적절한 소수점 자리로 반올림하여 정확성과 성능의 균형을 맞췄습니다.

에러 처리 및 경계 사례

  • 동일한 지점
  • 대척점 점
  • 극점에서의 계산
  • 날짜 변경선 교차 계산 등에 대한 처리를 포함합니다.

테스트 전략

  • 주요 도시 간의 알려진 거리 계산
  • 극점 및 국제 날짜 변경선에서의 경계 사례
  • 주요 및 시계열 방향에 대한 방향 계산
  • 실시간 피드백을 위한 성능 벤치마킹을 포함한 포괄적인 테스트를 구현했습니다.

실전 응용 및 미래 계획

이 시스템은 플래글 익스플로러에서 성공적으로 배포되어 일일 수천 건의 계산을 처리하고 있으며, 향후 WebAssembly 구현, 자주 계산된 경로 캐싱, 복수 계산의 일괄 처리, 지형 고도 데이터와의 통합 등을 통해 최적화를 계속 탐구할 것입니다.

출처 : 원문 보러가기

Leave a Comment