[algorithm] 두 위도 경도 지점 사이의 거리를 계산 하시겠습니까? (Haversine 공식)

위도와 경도로 지정된 두 점 사이의 거리를 어떻게 계산합니까?

명확히하기 위해 거리를 킬로미터 단위로 원합니다. 요점은 WGS84 시스템을 사용하며 사용 가능한 접근 방식의 상대적 정확성을 이해하고 싶습니다.



답변

링크 는 거리 계산에 Haversine 공식 사용을 자세히 설명하므로 도움이 될 수 있습니다 .

발췌 :

이 스크립트 [Javascript]는 ‘Haversine’공식을 사용하여 두 점 사이의 원거리, 즉 지표면에서 가장 짧은 거리를 계산합니다.

function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}


답변

프로젝트의 포인트 사이의 거리를 많이 계산해야했기 때문에 코드를 최적화하려고했습니다. 다른 브라우저에서 평균적으로 새로운 구현 가장 많이 응답 된 답변보다 2 배 빠르게 실행됩니다 .

function distance(lat1, lon1, lat2, lon2) {
  var p = 0.017453292519943295;    // Math.PI / 180
  var c = Math.cos;
  var a = 0.5 - c((lat2 - lat1) * p)/2 + 
          c(lat1 * p) * c(lat2 * p) * 
          (1 - c((lon2 - lon1) * p))/2;

  return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}

내 jsPerf로 게임을하고 결과를 확인할 수 있습니다 .

최근에는 파이썬에서 동일한 작업을 수행해야 했으므로 다음은 파이썬 구현입니다 .

from math import cos, asin, sqrt, pi

def distance(lat1, lon1, lat2, lon2):
    p = pi/180
    a = 0.5 - cos((lat2-lat1)*p)/2 + cos(lat1*p) * cos(lat2*p) * (1-cos((lon2-lon1)*p))/2
    return 12742 * asin(sqrt(a)) #2*R*asin...

그리고 완전성을 위해 : 위키의 Haversine .


답변

다음은 C # 구현입니다.

static class DistanceAlgorithm
{
    const double PIx = 3.141592653589793;
    const double RADIUS = 6378.16;

    /// <summary>
    /// Convert degrees to Radians
    /// </summary>
    /// <param name="x">Degrees</param>
    /// <returns>The equivalent in radians</returns>
    public static double Radians(double x)
    {
        return x * PIx / 180;
    }

    /// <summary>
    /// Calculate the distance between two places.
    /// </summary>
    /// <param name="lon1"></param>
    /// <param name="lat1"></param>
    /// <param name="lon2"></param>
    /// <param name="lat2"></param>
    /// <returns></returns>
    public static double DistanceBetweenPlaces(
        double lon1,
        double lat1,
        double lon2,
        double lat2)
    {
        double dlon = Radians(lon2 - lon1);
        double dlat = Radians(lat2 - lat1);

        double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
        double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
        return angle * RADIUS;
    }

}


답변

Haversine 공식의 Java 구현은 다음과 같습니다.

public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371;
public int calculateDistanceInKilometer(double userLat, double userLng,
  double venueLat, double venueLng) {

    double latDistance = Math.toRadians(userLat - venueLat);
    double lngDistance = Math.toRadians(userLng - venueLng);

    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
      + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat))
      * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);

    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c));
}

여기서 우리는 답을 가장 가까운 km로 반올림합니다.


답변

이 모든 것에 대해 대단히 감사합니다. Objective-C iPhone 앱에서 다음 코드를 사용했습니다.

const double PIx = 3.141592653589793;
const double RADIO = 6371; // Mean radius of Earth in Km

double convertToRadians(double val) {

   return val * PIx / 180;
}

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

        double dlon = convertToRadians(place2.longitude - place1.longitude);
        double dlat = convertToRadians(place2.latitude - place1.latitude);

        double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2);
        double angle = 2 * asin(sqrt(a));

        return angle * RADIO;
}

위도와 경도는 10 진수입니다. asin () 호출에 min ()을 사용하지 않았습니다. 사용하는 거리가 너무 작아서 필요하지 않기 때문입니다.

Radians의 값을 전달할 때까지 잘못된 답변을 제공했습니다. 이제 Apple의 Map 앱에서 얻은 값과 거의 동일합니다. 🙂

추가 업데이트 :

iOS4 이상을 사용하는 경우 Apple은이를 수행하는 몇 가지 방법을 제공하므로 다음과 같은 기능을 수행 할 수 있습니다.

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

    MKMapPoint  start, finish;


    start = MKMapPointForCoordinate(place1);
    finish = MKMapPointForCoordinate(place2);

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}


답변

이것은 매우 합리적인 근사치 (+/- 1 % 오류 한계)를 제공하는 간단한 PHP 함수입니다.

<?php
function distance($lat1, $lon1, $lat2, $lon2) {

    $pi80 = M_PI / 180;
    $lat1 *= $pi80;
    $lon1 *= $pi80;
    $lat2 *= $pi80;
    $lon2 *= $pi80;

    $r = 6372.797; // mean radius of Earth in km
    $dlat = $lat2 - $lat1;
    $dlon = $lon2 - $lon1;
    $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);
    $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
    $km = $r * $c;

    //echo '<br/>'.$km;
    return $km;
}
?>

전에 말했듯이; 지구는 구체가 아닙니다. 그것은 마크 맥 와이어가 연습하기로 결정한 낡고 오래된 야구와 같습니다. 더 간단한 계산 (이와 같이)은 구처럼 취급합니다.

이 불규칙한 난형의 위치와 점이 얼마나 떨어져 있는지에 따라 다른 방법이 다소 정확 할 수 있습니다 (절대 오차 한계가 작을수록). 기대치가 정확할수록 수학은 더 복잡해집니다.

더 많은 정보를 원하시면 : wikipedia geographic distance


답변

나는 여기에 작업 예제를 게시합니다.

MySQL에서 위도 및 경도와 함께 50KM 미만의 지정된 점 사이의 거리를 갖는 테이블의 모든 점을 나열합니다 (임의의 점-위도 : 45.20327, 긴 : 23.7806) (테이블 필드는 coord_lat 및 coord_long).

킬로미터에서 거리가 50 미만인 모든 것을 나열하십시오 (지구 반경 6371 KM으로 간주).

SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta
FROM obiective
WHERE coord_lat<>''
    AND coord_long<>''
HAVING distanta<50
ORDER BY distanta desc

위의 예제는 MySQL 5.0.95 및 5.5.16 (Linux)에서 테스트되었습니다.