2010-07-28 123 views
5

例如,我在我的數據庫中有汽車事件。這些事件有經緯度。在使用GPS的手機上,我用他的座標獲取用戶的位置。用戶可以選擇他想知道他周圍是否有事故的半徑。所以,假設他想知道在他身邊2英里的事件。PHP MySQL通過GPS獲取半徑用戶位置的位置

因此,我從電話發送到Web服務用戶的經度,經度和他選擇的半徑。我需要做一個SQL查詢來獲取用戶周圍2英里的事件。

你有什麼想法該怎麼做?

回答

2
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; 
    } 
    } // end function 

$x_lat=center_of_serach; 
$x_lon=center_of_serach; 
$_distance=some_distance_in_miles; 
$query1 = "SELECT * FROM `location_table` WHERE somefield=somefilter"; 
$result=mysql_db_query($db_conn, $query1); 
$max_rows=mysql_num_rows($result); 
if ($max_rows>0) 
    { 
while ($data1=mysql_fetch_assoc($result)) 
    { 
    if (distance($x_lat,$x_lon,$data1['lat'],$data1['lng'],'m')<$_distance) 
    { 
    //do stuff 
    } 
    } 

它更快地獲取所有數據並通過函數運行它,而不是在數據庫不太大的情況下使用查詢。

它也適用於Kilos和Nautical英里。 ;)

+0

LOL我只是注意到你有的鏈接..是的,這是有效的。 ;) – 2010-07-28 04:09:40

0

有一個公式可以計算兩個緯度/經度座標之間的距離。但要小心 - 它在計算上相當昂貴,所以如果你有很多事件發生,你會想要做到這一點。首先,read about the maths involved

至於PHP的代碼,一個快速谷歌翻了起來this link,看起來可能工作。

現在,你可能想使用一些更有效的方法來你的事件點分爲兩類:一類是點可能範圍內(希望一個很小的一套),以及那些你完全可以打折。檢查超過幾十個事件座標可能是一個性能問題。

我對此沒有任何特別的瞭解,但如果沒有人有聰明的事情發生,我會在稍後允許的情況下嘗試自己提出一些事情。

+0

我也推薦這個。我會考慮在數據集上做兩次傳球。第一遍將檢查事件經度是否在位置經度的X英里內,以及事件緯度是否在位置緯度的X英里內。這是一個非常簡單的查詢,它將返回一個較小的數據集來處理。使用這個較小的數據集,開始做一些觸發以確定事件與用戶位置的線性距離,這需要更復雜的數學運算,但您至少現在正在使用較小的數據集。 – 2010-07-28 04:11:01

+0

@Jakobud你可以在相同的查詢中運行該過濾器:'WHERE lat <='「。$ _ distance。」'AND lng <='「。$ _ distance。」'「;' – 2010-07-28 04:15:44

+0

是的,類似的東西。 Jackobud的方法是,首先檢查是否有候選點在用戶座標爲中心的4英里(16平方英里)的區域內,如果是,則進行更昂貴的計算以確定它是否與在那個廣場內的圓圈[http://en.wikipedia.org/wiki/Inscribed_circle] – timdev 2010-07-28 04:19:25

0
SELECT 3963 * ACOS(
    SIN(RADIANS($pointAlat)) * SIN(RADIANS($pointAlat)) + COS(RADIANS($pointAlat)) * COS(RADIANS($pointBlat)) * COS(RADIANS($pointAlong) - RADIANS($pointBlong))) 
AS 
distance; 

另外,如果您正在尋找在這個良好的讀/教程。檢查這裏 http://www.phpfreaks.com/forums/index.php/topic,208965.0.html

+0

沒用,因爲你不能在WHERE子句中使用別名 - 這將迫使他計算一堆超驗函數兩次。 Ouch。在一個潛在的巨大數據集上。Double Ouch。 – NullUserException 2010-07-28 04:17:44

3

計算的距離是相當昂貴的計算,如其他人所說。返回大量數據集也不是一個好主意 - 特別考慮到PHP在性能方面並不是那麼好。

我會使用一種啓發式算法,如使用簡單加法和減法來近似距離。

1分鐘=1.86公里= 1.15 英里

只要搜索與事故分貝該範圍(實際上是方形的,而不是一個圓圈)之內,那麼你可以在這些工作與PHP。


編輯:這是一種替代;這個近似的方法計算較不昂貴的:

近似距離以英里爲:以英里

改進的大致距離:

sqrt(x * x + y * y) 

where x = 69.1 * (lat2 - lat1) 
and y = 53.0 * (lon2 - lon1) 

您可以通過添加餘弦運算功能改善這種近似距離計算的準確性:

sqrt(x * x + y * y) 

where x = 69.1 * (lat2 - lat1) 
and y = 69.1 * (lon2 - lon1) * cos(lat1/57.3) 

來源:http://www.meridianworlddata.com/Distance-Calculation.asp


編輯2:我跑了一堆隨機生成的數據集的測試。

  • 在精度爲3種算法的差異是最小的,特別是在短距離
  • 最慢的算法(所述一個與所述一大堆三角函數)爲4x比其它兩個更慢。

絕對不值得。只需要近似。

代碼是在這裏:http://pastebin.org/424186

+0

另一個很好的啓發式。使用一分鐘(或其相關細分)可能的最小可能距離,並使用該距離排除明顯超過兩英里遠的任何候選點。當然,你可以變得更聰明,並根據用戶的位置使用各種近似值。 – timdev 2010-07-28 04:23:47

0

我做了快速搜索,轉向了this blog post它提供了一個很好的解釋和SQL選擇在給定半徑的記錄。

在評論中,他建議「爲了在大型數據集上加快速度,您可能首先需要在原點附近抓住一個方形塊,然後在原點和緯度/經度上加上一英里左右,然後使用上述從中間開始工作的子選擇「對我來說聽起來像是要走的路。

+0

鏈接已損壞.. – Zeus 2014-07-22 16:47:47

+0

已鏈接的頁面存檔在[web.archive.org](https://web.archive.org/web/20121126042747/http://blog.peoplesdns.com/archives/24) – showdev 2014-07-22 17:23:34