PHPでの2つの座標間の距離の測定


145

こんにちは、緯度と経度を持つ2点間の距離を計算する必要があります。

外部APIの呼び出しは避けたいです。

PHPでHaversine式を実装しようとしました。

これがコードです:

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;

      }
    }

公共の距離がある特定のポイントでテストすると、信頼できる結果が得られません。

元の数式または実装にエラーがあるかどうかわかりません


4
私はここのPHPを含む多くの言語で動作するコード見つかっgeodatasource.com/developers/php
クリシュナ

回答:


273

少し前に、haversine式の例を書いて、それを自分のWebサイトに公開しました。

/**
 * 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 2012

@TreyA-はい、わかりました。それについて何を言いたいのかわかりません。関数をテストして、間違った結果を計算しましたか?そして、あなたはウィキペディアの公式を見ましたか?あなたは本当にあなた自身のテストをして、あなたが間違って計算したと思うことの例を私に与えるべきです。
martinstoeckli 2012

申し訳ありませんが、私は今いくつかのことを説明する必要があります。1)質問はHaversineフォーミュラに関するものでした。別のフォーミュラを使用することを提案するかどうかを教えてください。2)Haversine式には極の周りに弱点がありますが、距離が短い場合正確です(これはアークコサイン式の問題です)。3)計算された$ angleに足りないステップがあると述べましたが、それは単に間違っています。結果を改善することはできません。テストしてください4)例を挙げれば、安定したVincentyフォーミュラを使用した方がよいことに同意します。多分あなたも答えを書くことができますか?
martinstoeckli 2012

@martinstoekli-あなたは正しい、あなたはHaversineの式に欠けているステップはありません。今後の読者を混乱させないためにコメントを削除しました。
TreyA

1
@PratikCJoshi-最後に、さまざまなユニットの使用に関するメモを追加する時間を見つけました。
martinstoeckli 2015

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
素晴らしいもの、私はこれを試してみましたが、こちらも同じ距離だけ小数点以下の変更をマップ番組をGoogleとそこに...
Zohair

3点間の距離を計算する場合はどうでしょうか。
kexxcream 2016年

3
この関数を2回呼び出して合計します。代わりに、それに応じて関数を変更します
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 2017

@GotBatteriesオリジナルのマルチプライヤはマイル単位です。最適化された関数は結果をkm単位で返します。1.1515 * 1.609344 = 1.853。おかげで、1.853に修正されました。
Alexander Yancharuk 2017

パフォーマンスを向上させるために、定数としてM_PI / 180および$ rad * 60 * 1.853を使用しないのはなぜですか。
Evren Yurtesen 2017

@EvrenYurtesenパフォーマンスを優先するのであれば、いい考えです。しかし、保守性と可読性はもっと複雑になると思います。
Alexander Yancharuk 2017

前の行にコメントを付けて// // M_PI / 180 ...などと言うだけです。なぜそれが保守を困難にするのかわかりません。それはあなたがこれから変えるものではありません。
Evren Yurtesen 2017

10

ここでは、2つの緯度と経度の間の距離を計算するためのシンプルで完璧なコードを示します。ここから次のコードが見つかりました-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としてgetを使用して
maxdangelo

1

こんにちはここで2つの異なる緯度と経度を使用して距離と時間を取得するコード

$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の下で緯度と経度を使用して2つの異なる場所の間の時間を取得するリンクの下の例を確認できます


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

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

最も簡単な方法の1つは次のとおりです。

$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.