PHP dan SQL: Hitung atau Pertanyaan Jarak Antara Titik Lintang dan Bujur

mengukur jarak
Waktu membaca: 3 minit

Pada bulan ini saya telah memprogram sedikit dalam PHP dan MySQL berkenaan dengan GIS. Mengintai di sekitar jaring, saya sebenarnya sukar mencari beberapa perhitungan Geografi untuk mencari jarak antara dua lokasi jadi saya ingin berkongsi di sini.

Cara mudah untuk mengira jarak antara dua titik adalah dengan menggunakan formula Pythagoras untuk mengira hipotenus segitiga (A² + B² = C²). Ini dikenali sebagai Jarak Euclidean.

Itu permulaan yang menarik tetapi tidak berlaku dengan Geografi kerana jarak antara garis lintang dan garis bujur adalah bukan jarak yang sama berjauhan. Apabila anda semakin hampir dengan khatulistiwa, garis lintang semakin jauh. Sekiranya anda menggunakan beberapa jenis persamaan triangulasi sederhana, ini dapat mengukur jarak dengan tepat di satu lokasi dan sangat salah di lokasi lain, kerana kelengkungan Bumi.

Formula Haversine

Peta Penerbangan Eropah

Jarak menggunakan kelengkungan Bumi digabungkan di Formula Haversine, yang menggunakan trigonometri untuk memungkinkan kelengkungan bumi. Apabila anda menemui jarak antara 2 tempat di bumi (ketika burung gagak terbang), garis lurus benar-benar busur.

Ini berlaku dalam penerbangan udara - adakah anda pernah melihat peta penerbangan yang sebenarnya dan melihat bahawa ia melengkung? Ini kerana lebih pendek untuk terbang di lengkungan antara dua titik daripada terus ke lokasi.

PHP: Hitung Jarak Antara 2 Mata

Bagaimanapun, inilah formula PHP untuk mengira jarak antara dua titik (bersama dengan penukaran Mile vs Kilometer) yang dibundarkan kepada dua tempat perpuluhan.

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: Mendapatkan Semua Rekod Dalam Julat Dengan Mengira Jarak Dalam Mil Menggunakan Lintang dan Bujur

Anda juga boleh menggunakan SQL untuk melakukan pengiraan untuk mencari semua rekod dalam jarak tertentu. Dalam contoh ini, saya akan meminta MyTable di MySQL untuk mencari semua rekod yang kurang daripada atau sama dengan jarak $ berubah (dalam Miles) ke lokasi saya pada $ lintang dan $ bujur:

Pertanyaan untuk mengambil semua rekod dalam spesifik jarak dengan mengira jarak dalam batu antara dua titik garis lintang dan garis bujur adalah:

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

Anda perlu menyesuaikannya:

  • $ bujur - ini adalah pemboleh ubah PHP di mana saya melewati garis bujur titik.
  • $ lintang - ini adalah pemboleh ubah PHP di mana saya melewati garis bujur titik.
  • $ jarak - ini adalah jarak yang anda mahu cari semua rekod kurang atau sama.
  • meja - ini adalah jadual ... anda pasti mahu menggantinya dengan nama jadual anda.
  • latitud - ini adalah medan lintang anda.
  • longitud - ini adalah bidang bujur anda.

SQL: Mengambil Semua Rekod Dalam Julat Dengan Mengira Jarak Dalam Kilometer Menggunakan Lintang dan Bujur

Dan inilah pertanyaan SQL menggunakan kilometer di MySQL:

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

Anda perlu menyesuaikannya:

  • $ bujur - ini adalah pemboleh ubah PHP di mana saya melewati garis bujur titik.
  • $ lintang - ini adalah pemboleh ubah PHP di mana saya melewati garis bujur titik.
  • $ jarak - ini adalah jarak yang anda mahu cari semua rekod kurang atau sama.
  • meja - ini adalah jadual ... anda pasti mahu menggantinya dengan nama jadual anda.
  • latitud - ini adalah medan lintang anda.
  • longitud - ini adalah bidang bujur anda.

Saya menggunakan kod ini dalam platform pemetaan perusahaan yang kami gunakan untuk sebuah kedai runcit dengan lebih dari 1,000 lokasi di seluruh Amerika Utara dan ia berfungsi dengan baik.

76 Komen

  1. 1

    Terima kasih banyak berkongsi. Ini adalah kerja salin dan tampal yang mudah dan berfungsi dengan baik. Anda telah menjimatkan banyak masa.
    FYI untuk sesiapa yang mengangkut ke C:
    double deg2rad (deg ganda) {return deg * (3.14159265358979323846 / 180.0); }

  2. 2

    Pengeposan yang sangat bagus - berfungsi dengan sangat baik - Saya hanya perlu menukar nama meja yang memegang panjang. Ia berfungsi dengan cepat untuk .. Saya mempunyai sebilangan kecil lat-panjang (<400) tetapi saya rasa ini akan bertambah baik. Laman web yang bagus juga - saya baru sahaja menambahkannya ke akaun del.icio.us saya dan akan memeriksa semula secara berkala.

  3. 4
  4. 5

    Saya mencari pengiraan jarak sepanjang hari dan menemui algoritma harversine, terima kasih kepada anda kerana memberikan contoh bagaimana memasukkannya ke dalam pernyataan sql. Terima kasih dan salam, Daniel

  5. 8

    saya rasa SQL anda memerlukan pernyataan yang baik.
    dan bukannya di mana jarak <= $ jarak yang mungkin anda perlukan
    gunakan jarak MEMILIKI <= $ jarak

    jika tidak, terima kasih kerana menjimatkan banyak masa dan tenaga.

  6. 10
  7. 11
  8. 12

    Terima kasih banyak kerana berkongsi kod ini. Ia menjimatkan banyak masa pembangunan. Juga, terima kasih kepada pembaca anda kerana menunjukkan bahawa penyataan HAVING diperlukan untuk MySQL 5.x. Sangat membantu.

  9. 14
  10. 15

    Hello,

    Soalan lain. Adakah terdapat formula untuk rentetan NMEA seperti yang ada di bawah?

    1342.7500, N, 10052.2287, E

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

    Terima kasih,
    Harry

  11. 16

    Saya juga mendapati bahawa DI MANA tidak berfungsi untuk saya. Menukarnya kepada MEMILIKI dan semuanya berfungsi dengan sempurna. Pada mulanya saya tidak membaca komen dan menulis semula dengan menggunakan pilihan bersarang. Kedua-duanya akan berfungsi dengan baik.

  12. 17
  13. 18

    Sangat membantu, terima kasih banyak! Saya menghadapi beberapa masalah dengan "MEMILIKI" yang baru, bukannya "DI MANA", tetapi setelah saya membaca komen di sini (setelah kira-kira setengah jam mengetap gigi dengan kecewa = P), saya membuatnya berfungsi dengan baik. Terima kasih ^ _ ^

  14. 19
  15. 20

    Perlu diingat bahawa pernyataan terpilih seperti itu akan menjadi sangat komputasi dan oleh itu lambat. Sekiranya anda mempunyai banyak pertanyaan, pertanyaan itu dapat diselesaikan dengan cepat.

    Pendekatan yang jauh lebih sengit adalah dengan menjalankan pilihan pertama (kasar) menggunakan kawasan SQUARE yang ditentukan oleh jarak yang dikira iaitu “select * dari tablename di mana garis lintang antara lat1 dan lat2 dan garis bujur antara lon1 dan lon2”. lat1 = targetlatitude - latdiff, lat2 = targetlatitude + latdiff, serupa dengan lon. latdiff ~ = jarak / 111 (untuk km), atau jarak / 69 untuk batu kerana 1 darjah lintang adalah ~ 111 km (sedikit variasi kerana bumi sedikit bujur, tetapi cukup untuk tujuan ini). londiff = jarak / (abs (cos (deg2rad (latitud)) * 111)) - atau 69 untuk batu (anda sebenarnya boleh mengambil petak yang sedikit lebih besar untuk memperhitungkan variasi). Kemudian ambil hasilnya dan masukkan ke dalam pilihan radial. Jangan lupa untuk mengambil kira koordinat di luar batas - iaitu julat garis bujur yang boleh diterima ialah -180 hingga +180 dan julat garis lintang yang boleh diterima adalah -90 hingga +90 - sekiranya latdiff atau londiff anda berjalan di luar julat ini . Perhatikan bahawa dalam kebanyakan kes ini mungkin tidak berlaku kerana ia hanya mempengaruhi pengiraan di sepanjang garis melalui lautan pasifik dari kutub ke tiang, walaupun ia memotong bahagian chukotka dan bahagian alaska.

    Yang kami capai dengan ini adalah pengurangan yang ketara dalam jumlah mata yang anda buat untuk membuat pengiraan ini. Sekiranya anda mempunyai sejuta mata global dalam pangkalan data yang diedarkan secara merata dan anda ingin mencari dalam jarak 100 km, maka carian pertama (cepat) anda adalah seluas 10000 km persegi dan mungkin akan menghasilkan sekitar 20 hasil (berdasarkan pemerataan genap luas permukaan kira-kira 500M km persegi), yang bermaksud bahawa anda menjalankan pengiraan jarak kompleks 20 kali untuk pertanyaan ini dan bukannya sejuta kali.

    • 21
      • 22

        Nasihat yang hebat! Saya benar-benar bekerja dengan pembangun yang menulis fungsi yang menarik kotak dalam dan kemudian fungsi rekursif yang menjadikan 'kotak' di sekeliling perimeter untuk memasukkan dan mengecualikan titik yang tinggal. Hasilnya adalah hasil yang sangat pantas - dia dapat menilai berjuta-juta mata dalam mikrodetik.

        Pendekatan saya di atas pasti 'kasar' tetapi mampu. Terima kasih sekali lagi!

        • 23

          Doug,

          Saya telah berusaha menggunakan mysql dan php untuk menilai sama ada titik panjang lat berada dalam poligon. Adakah anda tahu sama ada rakan pemaju anda menerbitkan sebarang contoh bagaimana melaksanakan tugas ini. Atau adakah anda tahu ada contoh yang baik. Terima kasih terlebih dahulu.

  16. 24

    Hai semua ini adalah pernyataan SQL ujian saya:

    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

    dan Mysql memberitahu saya bahawa jarak, tidak ada sebagai lajur, saya boleh menggunakan pesanan dengan, saya dapat melakukannya tanpa DI MANA, dan ia berfungsi, tetapi tidak dengannya ...

  17. 26

    Ini bagus, namun seperti burung terbang. Adalah baik untuk mencuba dan memasukkan API peta google untuk ini (mungkin menggunakan jalan dll) Hanya untuk memberi idea menggunakan bentuk pengangkutan yang berbeza. Saya masih belum membuat fungsi penyepuhlindapan simulasi dalam PHP yang dapat menawarkan penyelesaian yang berkesan untuk masalah jurujual perjalanan. Tetapi saya berpendapat bahawa saya mungkin dapat menggunakan semula beberapa kod anda untuk melakukannya.

  18. 27
  19. 28

    Artikel yang bagus! Saya menemui banyak artikel yang menerangkan cara mengira jarak antara dua titik tetapi saya benar-benar mencari coretan SQL.

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

    2 hari penyelidikan akhirnya dapat mencari halaman ini yang menyelesaikan masalah saya. Nampaknya saya lebih baik membuang WolframAlpha saya dan mengemas kini matematik saya. Perubahan dari WHERE to HAVING mempunyai skrip saya dalam keadaan berfungsi. TERIMA KASIH

  25. 37
    • 38

      Terima kasih Georgi. Saya terus mendapat tiang 'jarak' yang tidak dijumpai. Setelah saya menukar DI MANA untuk MEMILIKI ia berfungsi seperti daya tarikan!

  26. 39

    Saya harap ini adalah halaman pertama yang saya dapati. Setelah mencuba pelbagai arahan, ini adalah satu-satunya yang dapat berfungsi dengan baik, dan dengan sedikit perubahan diperlukan untuk memenuhi pangkalan data saya sendiri.
    Thanks a lot!

  27. 40

    Saya harap ini adalah halaman pertama yang saya dapati. Setelah mencuba pelbagai arahan, ini adalah satu-satunya yang dapat berfungsi dengan baik, dan dengan sedikit perubahan diperlukan untuk memenuhi pangkalan data saya sendiri.
    Thanks a lot!

  28. 41
  29. 42
  30. 43
  31. 45
  32. 46
  33. 47
  34. 49
  35. 50
  36. 52

    Terima kasih Douglas, Pertanyaan SQL adalah apa yang saya perlukan, dan saya fikir saya harus menulisnya sendiri. Anda telah menyelamatkan saya dari kemungkinan kurva pembelajaran garis bujur lintang!

  37. 53
  38. 55
  39. 56

    Douglas, terima kasih untuk kod yang luar biasa ini. Telah memusingkan badan saya untuk melakukan ini di portal komuniti GPS saya. Anda telah menyelamatkan saya berjam-jam.

  40. 58

    terima kasih kerana menyiarkan artikel bermanfaat ini,  
    tapi atas sebab tertentu saya ingin bertanya
    bagaimana untuk mendapatkan jarak antara koord dalam ds mysql dan kord yang dimasukkan ke php oleh pengguna?
    untuk menerangkan dengan lebih jelas:
    1. pengguna harus memasukkan [id] untuk memilih data yang ditentukan dari db dan kord pengguna itu sendiri
    2. fail php mendapatkan data sasaran (koordinat) menggunakan [id] dan kemudian mengira jarak antara pengguna dan titik sasaran

    atau hanya dapat jarak dari kod di bawah?

    $ qry = “SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((` Latitude` * pi () / 180)) + cos ((“. $ latitud. "* pi () / 180)) * cos ((` Latitude` * pi () / 180)) * cos (((". $ bujur." - "Bujur") * pi () / 180) )) * 180 / pi ()) * 60 * 1.1515 * 1.609344) sebagai jarak DARI `MyTable` DI MANA jarak> =". $ Jarak. " >>>> bolehkah saya "mengambil" jarak dari sini?
    terima kasih sekali lagi,
    Timmy S

    • 59

      tidak mengapa, saya telah memikirkan bagaimana "fungsi" berfungsi di php
      $ dis = getDistanceBetweenPointsNew ($ userLati, $ userLongi, $ lati, $ longi, $ unit = 'Km')
      terima kasih banyak-banyak!! 

  41. 60

    ok, semua yang saya cuba tidak berjaya. Maksud saya, apa yang saya ada berjaya, tetapi jaraknya jauh.

    Mungkinkah ada yang melihat apa yang salah dengan kod ini?

    if (isset ($ _ POST ['diserahkan'])) {$ z = $ _POST ['zipcode']; $ r = $ _POST ['radius']; gema "Hasil untuk". $ 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. bandar, z1. negara DARI mrk m, zip z1, zip z2 DI MANA 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 ") atau mati (mysql_error ()); manakala ($ baris = mysql_fetch_array ($ sql)) {$ store1 = $ baris ['MktName']. ""; $ store = $ baris ['LocAddSt']. ””; $ store. = $ baris ['LocAddCity']. ",". $ baris ['LocAddState']. " $. Baris ['zipcode']; $ latitude1 = $ baris ['lat']; $ longitud1 = $ baris ['lon']; $ latitud2 = $ baris ['y1']; $ longitud2 = $ baris ['x1']; $ bandar = $ baris ['bandar']; $ state = $ baris ['state']; $ dis = getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi'); // $ dis = jarak ($ ​​lat1, $ lon1, $ lat2, $ lon2); $ disahkan = $ baris ['disahkan']; jika ($ disahkan == '1') {echo “”; gema "". $ kedai. ""; gema $ dis. "Batu jauhnya"; gema ""; } lain {echo "". $ store. ""; gema $ dis. "Batu jauhnya"; gema ""; }}}

    kod fungsi saya.php
    fungsi getnew ($ latitude1, $ longitude1, $ latitude2, $ longitude2, $ unit = 'Mi') {$ theta = $ longitude1 - $ longitude2; $ jarak = (sin (deg2rad ($ latitude1)) * sin (deg2rad ($ latitude2))) + (cos (deg2rad ($ latitude1)) * cos (deg2rad ($ latitude2)) * cos (deg2rad ($ theta)) ); $ jarak = acos ($ jarak); $ jarak = rad2deg ($ jarak); $ jarak = $ jarak * 60 * 1.1515; tukar (unit $) {case 'Mi': break; kes 'Km': $ jarak = $ jarak * 1.609344; } kembali (pusingan ($ jarak, 2)); }

    Terima kasih terlebih dahulu

  42. 61
  43. 62

    Hai Douglas, artikel hebat. Saya dapati penjelasan anda mengenai konsep geografi dan kodnya sangat menarik. Satu-satunya cadangan saya ialah memberi ruang dan memasukkan kod untuk paparan (seperti Stackoverflow, misalnya). Saya faham bahawa anda ingin menjimatkan ruang, tetapi jarak / lekukan kod konvensional akan menjadikan saya lebih mudah, sebagai pengaturcara, membaca dan membedah. Bagaimanapun, itu perkara kecil. Teruskan usaha yang hebat.

  44. 64
  45. 65
  46. 66
  47. 67
  48. 68
  49. 69
  50. 70

    nampaknya lebih pantas (mysql 5.9) untuk menggunakan formula dua kali dalam pilih dan di mana:
    $ formula = “(((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((` Latitude` * pi () / 180)) + cos ((". $ latitude. "* Pi () / 180)) * cos ((` Latitude` * pi () / 180)) * cos (((". $ Bujur." - "Bujur") * pi () / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) ”;
    $ sql = 'SELECT *,'. $ formula. ' sebagai jarak DARI jadual DI MANA '.. formula $.' <= '. $ jarak;

  51. 71
  52. 72

    Terima kasih banyak untuk memotong artikel ini. Sangat membantu.
    PHP pada mulanya diciptakan sebagai platform skrip sederhana yang disebut "Personal Home Page". Kini PHP (kependekan dari Hypertext Preprocessor) adalah alternatif teknologi Microsoft Active Server Pages (ASP).

    PHP adalah bahasa sisi pelayan sumber terbuka yang digunakan untuk membuat laman web yang dinamik. Ia boleh dimasukkan ke dalam HTML. PHP biasanya digunakan bersama dengan pangkalan data MySQL pada pelayan web Linux / UNIX. Ia mungkin merupakan bahasa tulisan yang paling popular.

  53. 73

    Saya dapati penyelesaian di atas tidak berfungsi dengan baik.
    Saya perlu menukar kepada:

    $ qqq = "SELECT *, (((acos (sin ((". $ latitude. "* pi () / 180)) * sin ((` latt` * pi () / 180)) + cos ((". $ lintang. "* pi () / 180)) * cos ((` latt` * pi () / 180)) * cos (((". $ bujur." - `longt`) * pi () / 180) )) * 180 / pi ()) * 60 * 1.1515) sebagai jarak DARI `register`“;

  54. 75

    terima kasih tuan membalas dengan sempurna .. tetapi saya mempunyai satu soalan jika saya mahu mengeluarkan tanpa titik perpuluhan maka apa yang boleh saya lakukan ..?

    Terima kasih terlebih dahulu.

  55. 76

    Halo, tolong saya sangat memerlukan bantuan anda mengenai perkara ini.

    Saya membuat permintaan get ke pelayan web saya http://localhost:8000/users/findusers/53.47792/-2.23389/20/
    53.47792 = $ garis lintang
    -2.23389 = $ bujur
    dan 20 = jarak yang ingin saya perolehi

    Walaupun menggunakan formula anda, ia mengambil semua baris di db saya

    $ hasil = DB :: pilih (DB :: raw (“SELECT *, (((acos (sin ((“. $ latitude. ”* pi () / 180)) * sin ((lat * pi () / 180 cos) ((". $ lintang." * pi () / 180)) * cos ((lat * pi () / 180)) * cos (((". $ bujur." - lng) * pi ( ) / 180)))) * 180 / pi ()) * 60 * 1.1515 * 1.609344) sebagai jarak DARI penanda MEMILIKI jarak> = ". $ Jarak));

    [{"Id": 1, "name": "Frankie Johnnie & Luigo Too", "address": "939 W El Camino Real, Mountain View, CA", "lat": 37.386337280273, "lng": - 122.08582305908, "Jarak": 16079.294719663}, {"id": 2, "nama": "Pizzeria Pantai Timur Amici", "alamat": "790 Castro St, Mountain View, CA", "lat": 37.387138366699, "lng": -122.08323669434, "jarak": 16079.175940152}, {"id": 3, "name": "Kapp's Pizza Bar & Grill", "address": "191 Castro St, Mountain View, CA", "lat": 37.393886566162, "Lng": - 122.07891845703, "jarak": 16078.381373826}, {"id": 4, "name": "Pizza Meja Bulat: Pemandangan Gunung", "alamat": "570 N Shoreline Blvd, Mountain View, CA", "Lat": 37.402652740479, "lng": - 122.07935333252, "jarak": 16077.420540582}, {"id": 5, "name": "Tony & Alba's Pizza & Pasta", "address": "619 Escuela Ave, Mountain Lihat, CA "," lat ": 37.394012451172," lng ": - 122.09552764893," jarak ": 16078.563225154}, {" id ": 6," name ":" Oregano's Wood-Fired Pizza "," address ":" 4546 El Camino Real, Los Altos, CA "," lat ": 37.401725769043," lng ": - 122.11464691162," jarak ": 16077.937560795}, {" id ": 7," name ":" The bar and grills "," address ":" 24 Whiteley Street, Manchester "," lat ": 53.485118865967," lng ": - 2.1828699111938," jarak ": 8038.7620112314}]

    Saya mahu mengambil baris hanya dengan jarak 20 batu tetapi ia membawa semua baris. Tolong apa yang saya buat salah

Apa yang anda fikir?

Laman web ini menggunakan Akismet untuk mengurangkan spam. Ketahui bagaimana data komen anda diproses.