본문 바로가기

Android/Troubleshooting

Haversine 공식으로 두 지점간의 거리를 구하기

 

이번에 신규 기능을 추가하기로 했다.

 

대충 배달어플에서 라이더가 목적지에 근접하게되면 판매자나 소비자에게 곧 도착한다는 알림을 전송하는 기능을 만들어 보자는 내용

앱 네비에서 어차피 경로를 표시해 주니깐 거기서 몇 미터 남았을때 호출할까.. 아니면 일정 범위를 설정한 뒤, 해당 범위 내에 접근하면 무조건 호출할까 고민 좀 했다.

 

어느 길로 가든 일단 인접하면 연락이 가게하는게 맞다고 생각되어 후자로 선택

 

그럼 해당 범위에 들어갔는지 계산하는 함수가 필요한데..

 

여기서는 헤버사인(Haversine) 공식을 사용하는게 좋다.

지구상의 두 지점간의 거리를 계산할때 사용되는 공식이며, 일반적으로 위도(latitude)와 경도(longitude)를 사용해서 두 지점간의 최단 거리(대원의 호)를 계산한다.

 

수식은 다음과 같다.

 

a = sin²(Δlat/2) + cos(lat1) ⋅ cos(lat2) ⋅ sin²(Δlong/2)

c = 2 ⋅ atan2(√a, √(1−a))

d = R ⋅ c

 

여기서 Δlat는 두 위도 간의 차이, Δlong는 두 경도 간의 차이, R은 지구의 반지름 (일반적으로 평균 반지름 6,371km 사용), d는 두 지점 간의 거리다.

 

이걸 그대로 구현하면 좋겠으나, 그렇게 정밀하게 계산을 할 필요는 없다고 생각되어 조금 변형된 형태로 코드를 작성하기로 했다.

기본적인 위치 서비스나 거리 추정에서는 크게 문제되지 않는다고 생각한다.

private fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
    val lngDifference = lon1 - lon2

    val radLat1 = degToRad(lat1)
    val radLat2 = degToRad(lat2)
    val radLonDiff = degToRad(lngDifference)

    var dist = sin(radLat1) * sin(radLat2) + cos(radLat1) * cos(radLat2) * cos(radLonDiff)
    dist = acos(dist)
    dist = radToDeg(dist)
    dist *= 60 * 1.1515
    return dist * (1.609344f * 1000)
}
private fun degToRad(deg: Double): Double {
    return deg * Math.PI / 180.0
}
private fun radToDeg(rad: Double): Double {
    return rad * 180.0 / Math.PI
}

위는 내가 작성한 해버사인 함수다.
대충 내가 설정한 범위랑 비교해서, 위 공식으로 도출된 값이 더 작으면 알림을 쏘게했다.

 

[사용 예시]

fun checkDistance() : Boolean{
    val range = 100
    val distance = 
        calculateDistance(
            10,
            10,
            8,
            8
        )
    return if(distance > range)    True else False
}