PHPとSQL:Haversine式を使用して、緯度と経度のポイント間の大圏距離を計算またはクエリします。

HaversineFormula-PHPまたはMySQLで大圏距離を計算する

今月は、GISに関してPHPとMySQLでかなりプログラミングを行ってきました。 ネットを覗き込んで、私は実際にいくつかを見つけるのに苦労しました 地理計算 XNUMXつの場所の間の距離を見つけるために、ここでそれらを共有したいと思いました。

大圏距離のあるヨーロッパのフライトマップ

XNUMX点間の距離を計算する簡単な方法は、ピタゴラスの公式を使用して三角形の斜辺を計算することです(A²+B²=C²)。 これは、 ユークリッド距離.

これは興味深いスタートですが、緯度と経度の線の間の距離が 等距離ではありません 離れて。 赤道に近づくと、緯度の線はさらに離れます。 ある種の単純な三角測量方程式を使用すると、地球の曲率のために、ある場所では距離を正確に測定し、別の場所ではひどく間違っている可能性があります。

大圏距離

地球の周りを長距離移動するルートは、 大圏距離。 つまり…球上のXNUMX点間の最短距離は、フラットマップ内の点とは異なります。 それを緯度と経度の線が等距離ではないという事実と組み合わせると…そしてあなたは難しい計算をします。

これは、大圏がどのように機能するかについての素晴らしいビデオ説明です。

半正矢式

地球の曲率を利用した距離は、 ハバーシン式、三角法を使用して地球の曲率を考慮します。 (カラスが飛ぶように)地球上の2つの場所の間の距離を見つけるとき、直線は実際には弧です。

これは航空便にも当てはまります。実際のフライトの地図を見て、アーチ型になっていることに気づいたことがありますか。 これは、その場所に直接移動するよりも、XNUMX点間のアーチを飛行する方が短いためです。

PHP:緯度と経度の2点間の距離を計算します

とにかく、これは、小数点以下XNUMX桁に丸められたXNUMX点間の距離を計算するためのPHP式です(マイル対キロメートルの変換とともに)。

function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2, $unit = 'miles') {
  $theta = $longitude1 - $longitude2; 
  $distance = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta))); 
  $distance = acos($distance); 
  $distance = rad2deg($distance); 
  $distance = $distance * 60 * 1.1515; 
  switch($unit) { 
    case 'miles': 
      break; 
    case 'kilometers' : 
      $distance = $distance * 1.609344; 
  } 
  return (round($distance,2)); 
}

SQL:緯度と経度を使用してマイル単位の距離を計算することにより、範囲内のすべてのレコードを取得する

SQLを使用して計算を実行し、特定の距離内にあるすべてのレコードを検索することもできます。 この例では、MySQLのMyTableにクエリを実行して、$ latitudeと$ longitudeの場所までの変数$ distance(マイル単位)以下のすべてのレコードを検索します。

特定の内のすべてのレコードを取得するためのクエリ 距離 緯度と経度のXNUMX点間の距離をマイルで計算すると、次のようになります。

$query = "SELECT *, (((acos(sin((".$latitude."*pi()/180)) * sin((`latitude`*pi()/180)) + cos((".$latitude."*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((".$longitude."- `longitude`)*pi()/180)))) * 180/pi()) * 60 * 1.1515) as distance FROM `table` WHERE distance <= ".$distance."

これをカスタマイズする必要があります:

  • $経度 –これは、ポイントの経度を渡すPHP変数です。
  • $緯度 –これは、ポイントの経度を渡すPHP変数です。
  • $ distance –これは、すべてのレコードが以下であるかどうかを確認する距離です。
  • テーブル –これはテーブルです…これをテーブル名に置き換えます。
  • 緯度 –これはあなたの緯度の分野です。
  • 経度 –これは経度のフィールドです。

SQL:緯度と経度を使用してキロメートル単位の距離を計算することにより、範囲内のすべてのレコードを取得する

そして、MySQLでキロメートルを使用したSQLクエリは次のとおりです。

$query = "SELECT *, (((acos(sin((".$latitude."*pi()/180)) * sin((`latitude`*pi()/180)) + cos((".$latitude."*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((".$longitude."- `longitude`) * pi()/180)))) * 180/pi()) * 60 * 1.1515 * 1.609344) as distance FROM `table` WHERE distance <= ".$distance."

これをカスタマイズする必要があります:

  • $経度 –これは、ポイントの経度を渡すPHP変数です。
  • $緯度 –これは、ポイントの経度を渡すPHP変数です。
  • $ distance –これは、すべてのレコードが以下であるかどうかを確認する距離です。
  • テーブル –これはテーブルです…これをテーブル名に置き換えます。
  • 緯度 –これはあなたの緯度の分野です。
  • 経度 –これは経度のフィールドです。

このコードを、北米全体に1,000を超える場所にある小売店で利用したエンタープライズマッピングプラットフォームで利用しましたが、うまく機能しました。

76のコメント

  1. 1

    シェアしていただき、誠にありがとうございます。 これは簡単なコピーと貼り付けの作業で、うまくいきました。 あなたは私に多くの時間を節約してくれました。
    Cに移植する人のための参考:
    double deg2rad(double deg){return deg *(3.14159265358979323846 / 180.0); }

  2. 2

    非常に素晴らしい投稿–非​​常にうまく機能しました–lat-longを保持しているテーブルの名前を変更するだけで済みました。 それはかなり速く動作します..私はかなり少ない数のlat-long(<400)を持っていますが、これはうまくスケーリングすると思います。 素敵なサイトも– del.icio.usアカウントに追加したばかりで、定期的にチェックします。

  3. 4
  4. 5

    一日中距離の計算を検索し、ハーバーシンアルゴリズムを見つけました。これは、SQLステートメントに配置する方法の例を示してくれてありがとう。 おかげで、挨拶、ダニエル

  5. 8

    SQLにはhaveステートメントが必要だと思います。
    WHERE distance <= $ distanceの代わりに
    HAVING距離<= $ distanceを使用します

    そうでなければ、私にたくさんの時間とエネルギーを節約してくれてありがとう。

    • 9

      こんにちはデビッド、

      何らかのタイプの GROUP BY ステートメントを実行している場合は、HAVING が必要になります。 上記の例ではそうしていません。

      ダグ

  6. 10
  7. 11
  8. 12

    このコードを共有してくれてありがとう。 開発時間を大幅に節約できました。 また、MySQL 5.x には HAVING ステートメントが必要であることを指摘してくださった読者の皆様にも感謝いたします。 非常に役立ちます。

  9. 14

    上記の式により、時間を大幅に節約できます。 どうもありがとうございました。
    また、NMEA 形式と度を切り替える必要があります。 ページの下部にあるこの URL で数式を見つけました。 http://www.errorforum.com/knowledge-base/16273-converting-nmea-sentence-latitude-longitude-decimal-degrees.html

    誰かがこれを確認する方法を知っていますか?

    ありがとうございました!
    ハリー

  10. 15

    こんにちは、

    別の質問です。 以下のような NMEA 文字列の式はありますか?

    1342.7500、N、10052.2287、E

    $GPRMC,032731.000,A,1342.7500,N,10052.2287,E,0.40,106.01,101106,,*0B

    おかげで、
    ハリー

  11. 16

    また、WHERE が機能しないこともわかりました。 それを HAVING に変更すると、すべてが完璧に機能します。 最初はコメントを読まず、ネストされた選択を使用して書き直しました。 どちらも問題なく動作します。

  12. 17

    mysqlで書かれたスクリプトをありがとうございました、ほんの少しの微調整をする必要がありました(HAVING)🙂
    グレットジョブ

  13. 18

    信じられないほど役に立ちました、どうもありがとうございました! 新しい「HAVING」では「WHERE」ではなく問題が発生していましたが、ここでコメントを読んだら(フラストレーション= Pで歯を磨いてから約XNUMX分後)、うまく機能しました。 ありがとう^ _ ^

  14. 19
  15. 20

    そのようなselectステートメントは非常に計算量が多く、したがって遅くなることに注意してください。 これらのクエリがたくさんある場合は、すぐに問題が発生する可能性があります。

    それほど激しくないアプローチは、計算された距離によって定義された SQUARE エリアを使用して最初の (粗い) 選択を実行することです。つまり、「select * from tablename where latitude where latitude where lat1 と lat2 and longitude between lon1 と lon2」です。 lat1 = targetlatitude – latdiff、lat2 = targetlatitude + latdiff、lon と同様です。 latdiff ~= 距離 / 111 (km の場合)、または 69 度の緯度が ~ 1 km であるため、マイルの距離 /111 (地球はわずかに楕円形であるため、わずかな変動ですが、この目的には十分です)。 londiff = 距離 / (abs(cos(deg2rad(latitude))*111)) — またはマイルの場合は 69 です (実際には、変動を考慮するために、わずかに大きな正方形を使用できます)。 次に、その結​​果を取り、それをラジアル セレクトにフィードします。 範囲外の座標を考慮することを忘れないでください – つまり、許容可能な経度の範囲は -180 から +180 で、許容可能な緯度の範囲は -90 から +90 です — latdiff または londiff がこの範囲外で実行される場合. これは、チュコトカの一部とアラスカの一部と交差しますが、極から極まで太平洋を通る線上の計算にのみ影響するため、ほとんどの場合、これは適用されない可能性があることに注意してください。

    これにより、この計算を行うポイントの数が大幅に削減されます。 データベースに100万のグローバルポイントがほぼ均等に分散していて、10000 km以内で検索したい場合、最初の(高速)検索は20平方キロメートルの領域であり、おそらく約500の結果が得られます(表面積は約20M平方キロメートル)です。つまり、このクエリでは、複雑な距離の計算をXNUMX万回ではなくXNUMX回実行します。

    • 21

      例の小さな間違い…私たちが…正方形の「半径」を見ているので、それは50 km以内(100ではない)になります。

      • 22

        素晴らしいアドバイス! 私は実際に、内側の正方形をプルする関数を作成し、残りの点を含めたり除外したりするために境界の周りに「正方形」を作成する再帰関数を作成した開発者と協力しました。 結果は驚くべき速さで、マイクロ秒で数百万点を評価することができました。

        上記の私のアプローチは間違いなく「粗野」ですが、有能です。 再度、感謝します!

        • 23

          ダグ、

          私は mysql と php を使用して、緯度経度の点がポリゴン内にあるかどうかを評価しようとしています。 開発者の友人が、このタスクを実行する方法に関する例を公開しているかどうか知っていますか。 または、何か良い例を知っていますか。 前もって感謝します。

  16. 24

    みなさん、こんにちは。これは私のテストSQLステートメントです。

    SELECT DISTINCT area_id, (
    (
    (
    acos( sin( ( 13.65 * pi( ) /180 ) ) * sin( (
    `lat_dec` * pi( ) /180 ) ) + cos( ( 13.65 * pi( ) /180 ) ) * cos( (
    `lat_dec` * pi( ) /180 )
    ) * cos( (
    ( 51.02 - `lon_dec` ) * pi( ) /180 )
    )
    )
    ) *180 / pi( )
    ) *60 * 1.1515 * 1.609344
    ) AS distance
    FROM `post_codes` WHERE distance <= 50

    Mysqlは、距離が列として存在しないことを教えてくれます。orderbyを使用でき、WHEREなしで実行でき、機能しますが、機能しません…

  17. 26

    これは素晴らしいことですが、鳥が飛ぶのと同じです。 どういうわけか(おそらく道路などを使用して)これにグーグルマップAPIを組み込んでみるのは素晴らしいことです。ただ別の交通手段を使ってアイデアを与えるためです。 巡回セールスマン問題の効率的な解決策を提供できるPHPでのシミュレーテッドアニーリング関数はまだ作成していません。 しかし、私はあなたのコードのいくつかを再利用できるかもしれないと思います。

  18. 27

    ハイダグラス、
    この記事をどうもありがとう-あなたは私に多くの時間を節約してくれました。
    世話をする、
    ニムロッド @イスラエル

  19. 28

    良い記事です! XNUMX点間の距離を計算する方法を説明する記事をたくさん見つけましたが、SQLスニペットを本当に探していました。

  20. 29
  21. 30
  22. 31
  23. 32
  24. 36

    私の問題を解決するこのページを最終的に見つけるための2日間の調査。 WolframAlphaをバストアウトして、数学をブラッシュアップしたほうがいいようです。 WHEREからHAVINGへの変更により、スクリプトは正常に機能します。 ありがとうございました

  25. 37
    • 38

      Georgiに感謝します。 列「距離」が見つかりませんでした。 WHEREをHAVINGに変更すると、それは魅力のように機能しました!

  26. 39

    これが私がこれで見つけた最初のページだったらいいのにと思います。 多くの異なるコマンドを試した後、これが正しく機能する唯一のコマンドであり、自分のデータベースに合わせるために必要な変更は最小限でした。
    どうもありがとう!

  27. 40

    これが私がこれで見つけた最初のページだったらいいのにと思います。 多くの異なるコマンドを試した後、これが正しく機能する唯一のコマンドであり、自分のデータベースに合わせるために必要な変更は最小限でした。
    どうもありがとう!

  28. 41
  29. 42
  30. 43
  31. 45
  32. 46
  33. 47

    この式が機能することは知っていますが、地球の半径が考慮されている場所がわかりません。 誰かが私を啓発できますか?

  34. 49
  35. 50

    素晴らしいものダグラス。 XNUMX点の長さ/緯度/方位を考慮して交点を取得しようとしましたか?

  36. 52

    ダグラスに感謝します。SQLクエリはまさに私が必要としていたものであり、自分で作成する必要があると思いました。 あなたはおそらく何時間もの緯度経度の学習曲線から私を救ってくれました!

  37. 53

    MySQLクエリの「where句」にエラーメッセージ:不明な列「距離」が表示され続けます。

  38. 55
  39. 56
  40. 58

    この役立つ記事を投稿していただきありがとうございます、  
    でもどういうわけか聞きたい
    mysqlデータベース内の座標とユーザーがphpに挿入した座標との間の距離を取得する方法は?
    より明確に説明するには:
    1.ユーザーはデータベースとユーザー自身の座標から指定されたデータを選択するために[id]を挿入する必要があります
    2.phpファイルは[id]を使用してターゲットデータ(座標)を取得し、ユーザーとターゲットポイント間の距離を計算します

    または、単に以下のコードから距離を取得できますか?

    $ qry =“ SELECT *、(((acos(sin((“。$ latitude。” * pi()/ 180))* sin(( `Latitude` * pi()/ 180))+ cos((“。 $ latitude。 "* pi()/ 180))* cos((` Latitude` * pi()/ 180))* cos((( "。$ longitude。"-`Longitude`)* pi()/ 180) )))* 180 / pi())* 60 * 1.1515 * 1.609344)as distance FROM `MyTable` WHERE distance> ="。$ distance。” >>>>ここから距離を「取り出す」ことはできますか?
    再度、感謝します、
    ティミーS

    • 59

      気にしないでください、私は「関数」がphpでどのように機能するかを理解しました
      $ dis = getDistanceBetweenPointsNew($ userLati、$ userLongi、$ lati、$ longi、$ unit = 'Km')
      どうもありがとう!! 

  41. 60

    わかりました、私が試したすべてが機能していません。 つまり、私が持っているものは機能しますが、距離はかなり離れています。

    誰かがこのコードの何が問題なのかを理解できるでしょうか?

    if(isset($ _ POST ['submitted'])){$ z = $ _POST ['zipcode']; $ r = $ _POST ['radius']; エコー「結果」。$ z; $ sql = mysql_query(“ SELECT DISTINCT m.zipcode、m.MktName、m.LocAddSt、m.LocAddCity、m.LocAddState、m.x1、m.y1、m.verified、z1.lat、z2.lon、z1。 city、z1.state FROM mrk m、zip z1、zip z2 WHERE m.zipcode = z1.zipcode AND z2.zipcode = $ z AND(3963 * acos(truncate(sin(z2.lat / 57.2958)* sin(m。 y1 / 57.2958)+ cos(z2.lat / 57.2958)* cos(m.y1 / 57.2958)* cos(m.x1 / 57.2958 – z2.lon / 57.2958)、8)))<= $ r ")またはdie (mysql_error()); while($ row = mysql_fetch_array($ sql)){$ store1 = $ row ['MktName']。 ""; $ store = $ row ['LocAddSt']。””; $ store。= $ row ['LocAddCity']。」、「。$ row ['LocAddState']。」 “。$ row ['zipcode']; $ latitude1 = $ row ['lat']; $ longitude1 = $ row ['lon']; $ latitude2 = $ row ['y1']; $ longitude2 = $ row ['x1']; $ city = $ row ['city']; $ state = $ row ['state']; $ dis = getnew($ latitude1、$ longitude1、$ latitude2、$ longitude2、$ unit = 'Mi'); // $ dis = distance($ lat1、$ lon1、$ lat2、$ lon2); $ verify = $ row ['verified']; if($ verifyed == '1'){echo“”; echo“”。$ store。””; エコー$ dis。 ”マイル離れた”; エコー ""; } else {echo“”。$ store。””; エコー$ dis。 ”マイル離れた”; エコー ""; }}}

    私のfunctions.phpコード
    関数getnew($ latitude1、$ longitude1、$ latitude2、$ longitude2、$ unit = 'Mi'){$ theta = $ longitude1 – $ longitude2; $ distance =(sin(deg2rad($ latitude1))* sin(deg2rad($ latitude2)))+(cos(deg2rad($ latitude1))* cos(deg2rad($ latitude2))* cos(deg2rad($ theta)) ); $ distance = acos($ distance); $ distance = rad2deg($ distance); $ distance = $ distance * 60 * 1.1515; switch($ unit){case'Mi ':break; ケース 'Km':$ distance = $ distance * 1.609344; } return(round($ distance、2)); }

    よろしくお願いします

  42. 61
  43. 62

    ねえダグラス、素晴らしい記事。 地理的な概念とコードについてのあなたの説明は本当に興味深いと思いました。 私の唯一の提案は、表示するコードをスペースとインデントすることです(たとえば、Stackoverflowなど)。 スペースを節約したいということは理解していますが、従来のコード間隔/インデントを使用すると、プログラマーとしての私が読みやすく、分析しやすくなります。 とにかく、それは小さなことです。 これからもいい結果を出し続けてください。

  44. 64
  45. 65

    ここで関数を使用している間、あるタイプの距離を取得しています。クエリを使用している間、次の別のタイプの距離を取得しています。

  46. 66
  47. 67
  48. 68
  49. 69
  50. 70

    selectで5.9倍の式を使用する方が速いようです(mysql XNUMX)。
    $ Formula =“(((acos(sin((“。$ latitude。” * pi()/ 180))* sin(( `Latitude` * pi()/ 180))+ cos((“。$ latitude。 ” * pi()/ 180))* cos(( `Latitude` * pi()/ 180))* cos(((“。$ longitude。”-` Longitude`)* pi()/ 180)))) * 180 / pi())* 60 * 1.1515 * 1.609344) ";
    $ sql = 'SELECT *、'。$ formula。 ' テーブルからの距離としてWHERE '.. $ formula。' <= '。$ distance;

  51. 71
  52. 72

    この記事を共有してくれてありがとう。とても役に立ちます。
    PHPは当初、「個人ホームページ」と呼ばれる単純なスクリプトプラットフォームとして作成されました。 現在、PHP(Hypertext Preprocessorの略)は、MicrosoftのActive Server Pages(ASP)テクノロジの代替手段です。

    PHPは、動的なWebページの作成に使用されるオープンソースのサーバーサイド言語です。 HTMLに埋め込むことができます。 PHPは通常、Linux / UNIXWebサーバー上のMySQLデータベースと組み合わせて使用​​されます。 これはおそらく最も人気のあるスクリプト言語です。

  53. 73

    上記の解決策が正しく機能していないことがわかりました。
    私はに変更する必要があります:

    $ qqq =“ SELECT *、(((acos(sin((“。$ latitude。” * pi()/ 180))* sin(( `latt` * pi()/ 180))+ cos(("。 $ latitude。“ * pi()/ 180))* cos(( `latt` * pi()/ 180))* cos((("。$ longitude。“-`longt`)* pi()/ 180) )))* 180 / pi())* 60 * 1.1515)as distance FROM `register`“;

  54. 75
  55. 76

    こんにちは、私は本当にこれについてあなたの助けが必要になります。

    Webサーバーにgetリクエストを送信しました http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $緯度
    -2.23389 = $ longitude
    20 =取得したい距離

    ただし、式を使用すると、データベース内のすべての行が取得されます

    $ results = DB :: select(DB :: raw(“ SELECT *、(((acos(sin((“。$ latitude。” * pi()/ 180))* sin((lat * pi()/ 180 ))+ cos((“。$ latitude。” * pi()/ 180))* cos((lat * pi()/ 180))* cos(((“。$ longitude。”-lng)* pi( )/ 180))))* 180 / pi())* 60 * 1.1515 * 1.609344)距離> =“。$ distance));を持つマーカーからの距離として

    [{“ id”:1、” name”:” Frankie Johnnie&Luigo Too”、” address”:” 939 W El Camino Real、Mountain View、CA”、” lat”:37.386337280273、” lng”:-122.08582305908、 ” distance”:16079.294719663}、{“ id”:2、” name”:” Amici's East Coast Pizzeria”、” address”:” 790 Castro St、Mountain View、CA”、” lat”:37.387138366699、” lng”: -122.08323669434、” distance”:16079.175940152}、{“ id”:3、” name”:” Kapp's Pizza Bar&Grill”、” address”:” 191 Castro St、Mountain View、CA”、” lat”:37.393886566162、 ” lng”:-122.07891845703、” distance”:16078.381373826}、{“ id”:4、” name”:” Round Table Pizza:Mountain View”、” address”:” 570 N Shoreline Blvd、Mountain View、CA”、 ” lat”:37.402652740479、” lng”:-122.07935333252、” distance”:16077.420540582}、{“ id”:5、” name”:” Tony&Alba's Pizza&Pasta”、” address”:” 619 Escuela Ave、Mountain View、CA”、” lat”:37.394012451172、” lng”:-122.09552764893、” distance”:16078.563225154}、{“ id”:6、” name”:” Oregano's Wood-Fired Pizza”、” address”:” 4546 El Camino Real、Los Altos、CA”、” lat”:37.401725769043、” lng”:-122.11464691162、” distance”:16077.937560795}、{“ id”:7、” name”:”バーとグリル”、” address”:” 24 Whiteley Street、マンチェスター”、” lat”:53.485118865967、” lng”:-2.1828699111938、” distance”:8038.7620112314}]

    20マイルの行だけを取得したいのですが、すべての行が表示されます。 私が間違っていることをお願いします

どう思いますか?

このサイトはAkismetを使用して迷惑メールを減らします。 コメントの処理方法を学ぶ.