PHP에서 두 좌표 사이의 거리 측정


145

안녕 나는 위도와 경도를 가진 두 점 사이의 거리를 계산할 필요가있다.

외부 API에 대한 호출을 피하고 싶습니다.

PHP에서 Haversine Formula를 구현하려고했습니다.

코드는 다음과 같습니다.

class CoordDistance
 {
    public $lat_a = 0;
    public $lon_a = 0;
    public $lat_b = 0;
    public $lon_b = 0;

    public $measure_unit = 'kilometers';

    public $measure_state = false;

    public $measure = 0;

    public $error = '';



    public function DistAB()

      {
          $delta_lat = $this->lat_b - $this->lat_a ;
          $delta_lon = $this->lon_b - $this->lon_a ;

          $earth_radius = 6372.795477598;

          $alpha    = $delta_lat/2;
          $beta     = $delta_lon/2;
          $a        = sin(deg2rad($alpha)) * sin(deg2rad($alpha)) + cos(deg2rad($this->lat_a)) * cos(deg2rad($this->lat_b)) * sin(deg2rad($beta)) * sin(deg2rad($beta)) ;
          $c        = asin(min(1, sqrt($a)));
          $distance = 2*$earth_radius * $c;
          $distance = round($distance, 4);

          $this->measure = $distance;

      }
    }

공공 거리가있는 특정 지점으로 테스트하면 신뢰할 수있는 결과를 얻지 못합니다.

원래 수식이나 구현에 오류가 있는지 이해할 수 없습니다.


답변:


273

얼마 전 나는 헤르 세인 공식의 예를 작성하여 내 웹 사이트에 게시했습니다.

/**
 * Calculates the great-circle distance between two points, with
 * the Haversine formula.
 * @param float $latitudeFrom Latitude of start point in [deg decimal]
 * @param float $longitudeFrom Longitude of start point in [deg decimal]
 * @param float $latitudeTo Latitude of target point in [deg decimal]
 * @param float $longitudeTo Longitude of target point in [deg decimal]
 * @param float $earthRadius Mean earth radius in [m]
 * @return float Distance between points in [m] (same as earthRadius)
 */
function haversineGreatCircleDistance(
  $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
  // convert from degrees to radians
  $latFrom = deg2rad($latitudeFrom);
  $lonFrom = deg2rad($longitudeFrom);
  $latTo = deg2rad($latitudeTo);
  $lonTo = deg2rad($longitudeTo);

  $latDelta = $latTo - $latFrom;
  $lonDelta = $lonTo - $lonFrom;

  $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
    cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
  return $angle * $earthRadius;
}

parameter 파라메타로 전달한 것과 동일한 단위로 거리를 다시 얻습니다 $earthRadius. 기본값은 6371000 미터이므로 결과도 [m]이됩니다. 마일로 결과를 얻으려면 예를 들어 3959 마일을 통과 $earthRadius하면 결과는 [mi]가됩니다. 제 생각에는 달리 할 특별한 이유가 없다면 SI 단위를 고수하는 것이 좋습니다.

편집하다:

TreyA가 올바르게 지적한 바와 같이, Haversine 공식은 반올림 오차로 인해 대 지점에 약점이 있습니다 ( 소 거리 에서는 안정적 임). 그것들을 해결하기 위해 대신 Vincenty 공식을 사용할 수 있습니다 .

/**
 * Calculates the great-circle distance between two points, with
 * the Vincenty formula.
 * @param float $latitudeFrom Latitude of start point in [deg decimal]
 * @param float $longitudeFrom Longitude of start point in [deg decimal]
 * @param float $latitudeTo Latitude of target point in [deg decimal]
 * @param float $longitudeTo Longitude of target point in [deg decimal]
 * @param float $earthRadius Mean earth radius in [m]
 * @return float Distance between points in [m] (same as earthRadius)
 */
public static function vincentyGreatCircleDistance(
  $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
  // convert from degrees to radians
  $latFrom = deg2rad($latitudeFrom);
  $lonFrom = deg2rad($longitudeFrom);
  $latTo = deg2rad($latitudeTo);
  $lonTo = deg2rad($longitudeTo);

  $lonDelta = $lonTo - $lonFrom;
  $a = pow(cos($latTo) * sin($lonDelta), 2) +
    pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2);
  $b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta);

  $angle = atan2(sqrt($a), $b);
  return $angle * $earthRadius;
}

1
@TreyA-가능한 다른 버전이 있습니다.이 버전은 Wikipedia 의 공식을 구현하며 테스트를 거쳤습니다. $ angle은 세계 중앙의 각도를 라디안 단위로 나타내므로 지구 반지름과 곱할 수 있습니다. 누군가 관심이 있다면 더 복잡한 Vincenty 공식의 예를 제공 할 수도 있습니다.
— martinstoeckli

@TreyA-예, 알고 있습니다. 당신이 그 말을하고 싶은 것이 확실하지 않습니다. 함수를 테스트했으며 잘못된 결과를 계산 했습니까? 그리고 Wikipedia의 공식을 보셨습니까? 당신은 정말로 당신 자신의 시험을하고 당신이 생각하는 것이 잘못 계산되었다는 것을 보여 주어야합니다.
— martinstoeckli

죄송하지만 지금 몇 가지를 설명해야합니다. 1) 질문은 Haversine 공식에 관한 것이기 때문에 다른 공식을 사용하도록 제안하면 알려주십시오. 2) Haversine 공식은 극 주위에 약점이 있지만 작은 거리 에서는 정확합니다 (arccosine 공식의 문제입니다). 3) 계산 된 $ angle에 누락 된 단계가 있다고 말했는데, 이는 단순히 잘못되었습니다. 결과를 개선 할 수 없습니다. 테스트 해보십시오! 4) 나는 안정적인 Vincenty 공식을 사용하는 것이 낫다는 데 동의합니다. 나는 이미 예를 제시했습니다. 아마 당신도 대답을 쓸 수 있습니까?
— martinstoeckli

@martinstoekli-맞습니다. Haversine 수식에 단계가 없습니다. 나는 미래의 독자들을 혼동하지 않기 위해 내 의견을 삭제했습니다.
— TreyA

1
@PratikCJoshi-마지막으로 다른 단위 사용에 대한 메모를 추가 할 시간을 찾았습니다.
— martinstoeckli

63

신뢰할 수있는 결과를 제공하는 이 코드 를 찾았습니다 .

function distance($lat1, $lon1, $lat2, $lon2, $unit) {

  $theta = $lon1 - $lon2;
  $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
  $dist = acos($dist);
  $dist = rad2deg($dist);
  $miles = $dist * 60 * 1.1515;
  $unit = strtoupper($unit);

  if ($unit == "K") {
      return ($miles * 1.609344);
  } else if ($unit == "N") {
      return ($miles * 0.8684);
  } else {
      return $miles;
  }
}

결과 :

echo distance(32.9697, -96.80322, 29.46786, -98.53506, "M") . " Miles<br>";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "K") . " Kilometers<br>";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "N") . " Nautical Miles<br>";

2
좋은 물건, 나는 이것을 시도하고 또한 구글지도는 여기 저기 동일한 거리 만 소수점 변화를 보여줍니다 ..
— Zohair

세 점 사이의 거리를 계산하려면 어떻게해야합니까?
— kexxcream

3
이 함수를 두 번 호출하고 합산하십시오. 교대로 함수를 변경하십시오
— Janith Chinthana

일부 조건에서 NaN을 반환 stackoverflow.com/questions/37184259/…
— Zahur Sh

23

@martinstoeckli 및 @Janith Chinthana 답변에 추가되었습니다 . 어떤 알고리즘이 가장 빠른지 궁금한 사람들을 위해 성능 테스트를 작성했습니다 . 최고의 성능 결과는 codexworld.com의 최적화 된 기능을 보여줍니다 .

/**
 * Optimized algorithm from http://www.codexworld.com
 *
 * @param float $latitudeFrom
 * @param float $longitudeFrom
 * @param float $latitudeTo
 * @param float $longitudeTo
 *
 * @return float [km]
 */
function codexworldGetDistanceOpt($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo)
{
    $rad = M_PI / 180;
    //Calculate distance from latitude and longitude
    $theta = $longitudeFrom - $longitudeTo;
    $dist = sin($latitudeFrom * $rad) 
        * sin($latitudeTo * $rad) +  cos($latitudeFrom * $rad)
        * cos($latitudeTo * $rad) * cos($theta * $rad);

    return acos($dist) / $rad * 60 *  1.853;
}

테스트 결과는 다음과 같습니다.

Test name       Repeats         Result          Performance     
codexworld-opt  10000           0.084952 sec    +0.00%
codexworld      10000           0.104127 sec    -22.57%
custom          10000           0.107419 sec    -26.45%
custom2         10000           0.111576 sec    -31.34%
custom1         10000           0.136691 sec    -60.90%
vincenty        10000           0.165881 sec    -95.26%

코드에서 codexworlds 알고리즘의 승수는 1.852 인 반면 실제 원본은 1.1515입니다. 왜 이런거야? 왜 차이점이 있습니까?
— GotBatteries

@GotBatteries 원래 mulitplier는 마일입니다. 최적화 된 함수는 km 단위의 결과를 반환합니다. 1.1515 * 1.609344 = 1.853. 1.853으로 수정되었습니다.
— Alexander Yancharuk

더 나은 성능을 위해 M_PI / 180 및 $ rad * 60 * 1.853을 상수로 사용하지 않는 이유는 무엇입니까?
— Evren Yurtesen

@EvrenYurtesen 우선 순위가 성능이라면 좋은 생각입니다. 그러나 유지 보수와 가독성은 더 복잡해질 것이라고 생각합니다.
— Alexander Yancharuk

이전 줄에 주석을 달고 // M_PI / 180 ... 등을 말하십시오. 왜 유지 관리가 어려운지 모르겠습니다. 그것은 당신이 변화시킬 것이 아닙니다.
— Evren Yurtesen

10

다음은 위도와 경도 사이의 거리를 계산하기위한 간단하고 완벽한 코드입니다. 다음 코드는 여기에서 발견되었습니다-http: //www.codexworld.com/distance-between-two-addresses-google-maps-api-php/

$latitudeFrom = '22.574864';
$longitudeFrom = '88.437915';

$latitudeTo = '22.568662';
$longitudeTo = '88.431918';

//Calculate distance from latitude and longitude
$theta = $longitudeFrom - $longitudeTo;
$dist = sin(deg2rad($latitudeFrom)) * sin(deg2rad($latitudeTo)) +  cos(deg2rad($latitudeFrom)) * cos(deg2rad($latitudeTo)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;

$distance = ($miles * 1.609344).' km';

5

짧고 빠른 것을 좋아하는 사람들을 위해 (deg2rad ()를 호출하지 않음).

function circle_distance($lat1, $lon1, $lat2, $lon2) {
  $rad = M_PI / 180;
  return acos(sin($lat2*$rad) * sin($lat1*$rad) + cos($lat2*$rad) * cos($lat1*$rad) * cos($lon2*$rad - $lon1*$rad)) * 6371;// Kilometers
}

2

이 멋진 결과를 제공하십시오

function getDistance($point1_lat, $point1_long, $point2_lat, $point2_long, $unit = 'km', $decimals = 2) {
        // Calculate the distance in degrees
        $degrees = rad2deg(acos((sin(deg2rad($point1_lat))*sin(deg2rad($point2_lat))) + (cos(deg2rad($point1_lat))*cos(deg2rad($point2_lat))*cos(deg2rad($point1_long-$point2_long)))));

        // Convert the distance in degrees to the chosen unit (kilometres, miles or nautical miles)
        switch($unit) {
            case 'km':
                $distance = $degrees * 111.13384; // 1 degree = 111.13384 km, based on the average diameter of the Earth (12,735 km)
                break;
            case 'mi':
                $distance = $degrees * 69.05482; // 1 degree = 69.05482 miles, based on the average diameter of the Earth (7,913.1 miles)
                break;
            case 'nmi':
                $distance =  $degrees * 59.97662; // 1 degree = 59.97662 nautic miles, based on the average diameter of the Earth (6,876.3 nautical miles)
        }
        return round($distance, $decimals);
    }

2

아주 오래된 질문이지만 Google지도와 동일한 결과를 반환하는 PHP 코드에 관심이있는 사람들은 다음과 같은 일을합니다.

/**
 * Computes the distance between two coordinates.
 *
 * Implementation based on reverse engineering of
 * <code>google.maps.geometry.spherical.computeDistanceBetween()</code>.
 *
 * @param float $lat1 Latitude from the first point.
 * @param float $lng1 Longitude from the first point.
 * @param float $lat2 Latitude from the second point.
 * @param float $lng2 Longitude from the second point.
 * @param float $radius (optional) Radius in meters.
 *
 * @return float Distance in meters.
 */
function computeDistance($lat1, $lng1, $lat2, $lng2, $radius = 6378137)
{
    static $x = M_PI / 180;
    $lat1 *= $x; $lng1 *= $x;
    $lat2 *= $x; $lng2 *= $x;
    $distance = 2 * asin(sqrt(pow(sin(($lat1 - $lat2) / 2), 2) + cos($lat1) * cos($lat2) * pow(sin(($lng1 - $lng2) / 2), 2)));

    return $distance * $radius;
}

다양한 좌표로 테스트했으며 완벽하게 작동합니다.

나는 그것이 다른 대안보다 빠를 것이라고 생각합니다. 그러나 그것을 테스트하지 않았습니다.

힌트 : Google지도는 지구 반지름으로 6378137을 사용합니다. 따라서 다른 알고리즘과 함께 사용하면 효과가 있습니다.


1

정확한 값을 얻으려면 다음과 같이하십시오.

public function DistAB()
{
      $delta_lat = $this->lat_b - $this->lat_a ;
      $delta_lon = $this->lon_b - $this->lon_a ;

      $a = pow(sin($delta_lat/2), 2);
      $a += cos(deg2rad($this->lat_a9)) * cos(deg2rad($this->lat_b9)) * pow(sin(deg2rad($delta_lon/29)), 2);
      $c = 2 * atan2(sqrt($a), sqrt(1-$a));

      $distance = 2 * $earth_radius * $c;
      $distance = round($distance, 4);

      $this->measure = $distance;
}

흠 ...

편집하다:

공식 화자 및 적어도 JS 구현의 경우 다음을 시도하십시오. http://www.movable-type.co.uk/scripts/latlong.html

감히 ... 원 함수의 모든 값을 deg2rad하는 것을 잊었습니다 ...


답변 주셔서 감사합니다. 나는 사이의 간단한 계산으로이 구현을 확인했습니다 pointA (42,12)와 pointB (43,12) $ EARTH_RADIUS = 6372.795477598은 110,94 주위에 뭔가를해야 할 때 나는 결과 12745.591로 얻을 사용
— maxdangelo

1

두 가지 다른 위도 및 경도를 사용하여 거리 및 시간 가져 오기 코드

$url ="https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=16.538048,80.613266&destinations=23.0225,72.5714";



    $ch = curl_init();
    // Disable SSL verification

    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    // Will return the response, if false it print the response
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // Set the url
    curl_setopt($ch, CURLOPT_URL,$url);
    // Execute
    $result=curl_exec($ch);
    // Closing
    curl_close($ch);

    $result_array=json_decode($result);
print_r($result_array);

당신은 링크 아래의 예를 확인할 수 PHP의 위도와 경도를 사용하여 서로 다른 두 위치 사이의 GET 시간


6
수학을 사용하여 쉽게 찾을 수있는 것에 대해 API를 호출 할 필요는 없습니다.
— Ivotje50

1

위도와 경도 지점 사이의 거리를 계산하려면이 기능을 사용해보십시오.

function calculateDistanceBetweenTwoPoints($latitudeOne='', $longitudeOne='', $latitudeTwo='', $longitudeTwo='',$distanceUnit ='',$round=false,$decimalPoints='')
    {
        if (empty($decimalPoints)) 
        {
            $decimalPoints = '3';
        }
        if (empty($distanceUnit)) {
            $distanceUnit = 'KM';
        }
        $distanceUnit = strtolower($distanceUnit);
        $pointDifference = $longitudeOne - $longitudeTwo;
        $toSin = (sin(deg2rad($latitudeOne)) * sin(deg2rad($latitudeTwo))) + (cos(deg2rad($latitudeOne)) * cos(deg2rad($latitudeTwo)) * cos(deg2rad($pointDifference)));
        $toAcos = acos($toSin);
        $toRad2Deg = rad2deg($toAcos);

        $toMiles  =  $toRad2Deg * 60 * 1.1515;
        $toKilometers = $toMiles * 1.609344;
        $toNauticalMiles = $toMiles * 0.8684;
        $toMeters = $toKilometers * 1000;
        $toFeets = $toMiles * 5280;
        $toYards = $toFeets / 3;


              switch (strtoupper($distanceUnit)) 
              {
                  case 'ML'://miles
                         $toMiles  = ($round == true ? round($toMiles) : round($toMiles, $decimalPoints));
                         return $toMiles;
                      break;
                  case 'KM'://Kilometers
                        $toKilometers  = ($round == true ? round($toKilometers) : round($toKilometers, $decimalPoints));
                        return $toKilometers;
                      break;
                  case 'MT'://Meters
                        $toMeters  = ($round == true ? round($toMeters) : round($toMeters, $decimalPoints));
                        return $toMeters;
                      break;
                  case 'FT'://feets
                        $toFeets  = ($round == true ? round($toFeets) : round($toFeets, $decimalPoints));
                        return $toFeets;
                      break;
                  case 'YD'://yards
                        $toYards  = ($round == true ? round($toYards) : round($toYards, $decimalPoints));
                        return $toYards;
                      break;
                  case 'NM'://Nautical miles
                        $toNauticalMiles  = ($round == true ? round($toNauticalMiles) : round($toNauticalMiles, $decimalPoints));
                        return $toNauticalMiles;
                      break;
              }


    }

그런 다음 기능을 다음과 같이 사용하십시오.

echo calculateDistanceBetweenTwoPoints('11.657740','77.766270','11.074820','77.002160','ML',true,5);

그것이 도움이되기를 바랍니다.


내 경우에는 실제 시나리오 완벽한 작업으로 검증되었습니다.
— Daxesh Vekariya 2016 년

1
를 작성하고 실제 시나리오에서 그것을 확인하기 위해 거의 5 시간 툭
— Manojkiran.A

0

승수는 다음과 같이 큰 원거리 이론으로 인해 모든 좌표에서 변경됩니다.

http://en.wikipedia.org/wiki/Great-circle_distance

여기에 설명 된이 공식을 사용하여 가장 가까운 값을 계산할 수 있습니다.

http://en.wikipedia.org/wiki/Great-circle_distance#Worked_example

키는 각도-분-초 값을 모든도 값으로 변환합니다.

N 36°7.2', W 86°40.2'  N = (+) , W = (-), S = (-), E = (+) 
referencing the Greenwich meridian and Equator parallel

(phi)     36.12° = 36° + 7.2'/60' 

(lambda)  -86.67° = 86° + 40.2'/60'

0

가장 쉬운 방법 중 하나는 다음과 같습니다.

$my_latitude = "";
$my_longitude = "";
$her_latitude = "";
$her_longitude = "";

$distance = round((((acos(sin(($my_latitude*pi()/180)) * sin(($her_latitude*pi()/180))+cos(($my_latitude*pi()/180)) * cos(($her_latitude*pi()/180)) * cos((($my_longitude- $her_longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344), 2);
echo $distance;

소수점 2 자리까지 반올림합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.