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を超える場所にある小売店で利用したエンタープライズマッピングプラットフォームで利用しましたが、うまく機能しました。

77のコメント

  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

      こんにちはデビッド、

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

      ダグ

  6. 10
  7. 11
  8. 12

    このコードを共有していただきありがとうございます。 それは私に多くの開発時間を節約しました。 また、MySQL5.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に変更すると、すべてが完璧に機能します。 最初はコメントを読んでおらず、ネストされたselectを使用して書き直しました。 どちらも問題なく動作します。

  12. 17

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

  13. 18

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

  14. 19
  15. 20

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

    それほど強力ではないアプローチは、計算された距離で定義されたSQUARE領域を使用して、最初の(粗い)選択を実行することです。 lat1 = targetlatitude – latdiff、lat2 = targetlatitude + latdiff、lonと同様。 latdiff〜 =距離/ 1(kmの場合)、または2度の緯度が〜1 kmであるため、マイルの場合は距離/ 2(地球はわずかに楕円形であるため、わずかな変動がありますが、この目的には十分です)。 londiff =距離/(abs(cos(deg111rad(latitude))* 69))—またはマイルの場合は1(実際には、変動を考慮して少し大きい正方形を取ることができます)。 次に、その結​​果を取得して、ラジアルセレクトにフィードします。 範囲外の座標を考慮することを忘れないでください。つまり、緯度または経度がこの範囲外にある場合に備えて、許容経度の範囲は-111〜 + 2、許容緯度の範囲は-111〜 +69です。 。 これは、チュクチの一部とアラスカの一部と交差しますが、太平洋を通る極から極への線上の計算にのみ影響するため、ほとんどの場合、これは適用できないことに注意してください。

    これにより、この計算を行うポイントの数が大幅に削減されます。 データベースに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マイルの行だけを取得したいのですが、すべての行が表示されます。 私が間違っていることをお願いします

  56. 77

    同様のクエリを探していますが、少しステップアップしました。つまり、各座標から2マイル以内のすべての座標をグループ化し、各グループの座標数をカウントして、座標が最も多いXNUMXつのグループのみを出力します。座標の数が最も多いグループの中に複数のグループがあります–同じ最大数のグループからランダムなグループを出力するだけです–

どう思いますか?

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