2012-10-02 74 views
5

我需要能夠顯示從用戶選擇的特定位置到n的城鎮/城鎮的距離。它就像點擊地圖並獲得100英里以內的所有目的地,只是它不會是地圖,而是網頁上的鏈接。如何有效地在DB中存儲城鎮和城鎮之間的距離

我需要選擇一種解決方案,可以從一個國家擴展到一個國家,從全球範圍擴展到全球 - 這意味着從一千到十萬個位置。

雖然在關係數據庫表中存儲了CITY1_ID,CITY2_ID & DISTANCE,但我懷疑它是否可以很好地適用於Web應用程序(數百萬行)。

使用NoSQL數據庫或圖形數據庫可以更有效地完成這項工作嗎?或者,RDBMS是否足以解決適當設計的問題?我可以得到類似如下的東西:讓我來自聖何塞100英里內的所有城市嗎?

回答

4

您應該爲每個城市存儲一個city_id, latitude, longitude - 然後根據運行時輸入計算距離。

+0

是......這個。雖然是第二個「再計算」的步驟是有點棘手:D這絕對是一個壞主意,(每次添加一個你所要做'N'計算/'inserts'時間)存儲市惠城區的距離。數據庫類型(RDBMS或NoSQL)沒有區別。 – Rudu

+0

如果我沒有在數據庫存儲,然後我將如何得到這樣的:給我100英里聖荷西內的所有城市? –

+0

檢查GREAT CIRCLE DISTANCE公式或HAVERSINE DISTANCE。 – Randy

0

不要存儲它,用經度和緯度計算它的運行時間。與節省城市之間的所有距離相反,極其可擴展。

你有一個參考點(聖何塞),並遍歷所有的城市記錄並計算它的運行時間(在許多記錄的情況下,這個計算由客戶端完成,可能與JavaScript或其他東西,因爲如果你有服務器做到這一點,它會花費過多的時間)。 JavaScript的可能是這個樣子:

var R = 6371; // Radius of the earth in km 
var dLat = (lat2-lat1).toRad(); // Javascript functions in radians 
var dLon = (lon2-lon1).toRad(); 
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + 
     Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) * 
     Math.sin(dLon/2) * Math.sin(dLon/2); 
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
var d = R * c; // Distance in km 

上面的代碼來自here

注:這是一個在千米因爲我的荷蘭,從而使用公制

+0

與上面相同的問題我將如何獲得距我的源LongLat特定距離內的所有城市。基於這些位置,我需要從DB獲取關於這些城市的更多信息。 –

+0

@AJ。見上面除了 – stealthjong

+0

如果我有一百萬條記錄,這意味着這樣做了一百萬次服務器端或客戶端? –

0

我使用Neo4J的東西類似地,它可以很好地適用於任何可以表示爲圖形的數據。

0

你可以,正如其他人所指出的,存儲每個條目的緯度/龍COORDS和使用類似於在運行時以下的東西,它提供千米/英里的距離輸出計算距離:

function distance($lat1, $lng1, $lat2, $lng2, $miles = true) 
{ 
     $pi80 = M_PI/180; 
     $lat1 *= $pi80; 
     $lng1 *= $pi80; 
     $lat2 *= $pi80; 
     $lng2 *= $pi80; 

     $r = 6372.797; // mean radius of Earth in km 
     $dlat = $lat2 - $lat1; 
     $dlng = $lng2 - $lng1; 
     $a = sin($dlat/2) * sin($dlat/2) + cos($lat1) * cos($lat2) * sin($dlng/2) * sin($dlng/2); 
     $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); 
     $km = $r * $c; 

     return ($miles ? ($km * 0.621371192) : $km); 
} 

編輯:這不適用於n在半徑搜索範圍內匹配。考慮到給定半徑範圍內的城鎮密度,最好將距離計算轉換爲SQL,因爲其速度要快得多,並且可以與x公里/英里之內的距離匹配。

+0

這意味着在運行時計算nxn個組合,然後選擇100英里內的所有位置。聽起來不太可行@nickhar –

+0

剛剛看到你的更新 - 我在去年完成了這個確切的功能,但不記得我們最終如何實現它。會檢查。 – nickhar

+0

我們實際上在SQL中做了calc,因爲它比使用PHP和在一個正方形而不是半徑(半徑更復雜)內快得多。這裏有一個僞解決方案[鏈接](http://board.phpbuilder.com/showthread.php?10384415-RESOLVED-Zip-code-radius-etc。),但我們有一個改進的版本,我仍在搜索對於。 – nickhar

0

簡單我已經多次使用(但不與MySQL)溶液是創造定義的函數some_distance_function用戶與四個參數latitude1longitude1latitude2longitude2它返回距離,然後只是測試針對該距離一切函數,並查看每個項目的距離是否小於或等於給定值。如果你只有幾千個地點,這是非常好的和高效的。

如果您需要對數百萬條記錄運行此查詢,您可能需要查看哪些GIS(地理信息系統)擴展可用於您選擇的數據庫,因爲有更好的選擇(至少在搜索能力方面)持久數據結構用於搜索大量的位置。

編輯: 爲了讓微軟是怎麼做的示例,請參見http://technet.microsoft.com/en-us/library/bb964712(v=sql.105).aspx

它看起來像MySQL支持一般的空間擴展:

http://dev.mysql.com/doc/refman/5.0/en/gis-introduction.html
http://dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html

編輯II:

看起來這個問題也可能會有所幫助。

Find the distance between two points in MYSQL. (using the Point Datatype)

0

下面是使用RDBMS中的溶液。保持兩個表

  • CityByLat {緯度,city_id}與緯度聚集索引和
  • CityByLng {logitude,city_id}與經度

當你需要找到在一定的城市聚集索引從給定經度和緯度的半徑,您可以對兩個表格進行有效的範圍查詢,以獲取某個經度和緯度範圍內的城市。然後,您可以只計算檢索到的城市的實際距離。

2

代替計算兩個城市之間的距離計算爲100英里的邊界框,那麼你有4個浮點變量插入到你的數據庫 - 浮動比較是比數據庫中距離計算快了很多。下行是你在角落裏多走一點距離。

PHP函數來計算邊框

 
function getBoundingBox($lat_degrees,$lon_degrees,$distance_in_miles) 
{ 
     $radius = 3963.1; // of earth in miles 

     // bearings 
     $due_north = 0; 
     $due_south = 180; 
     $due_east = 90; 
     $due_west = 270; 

     // convert latitude and longitude into radians 
     $lat_r = deg2rad($lat_degrees); 
     $lon_r = deg2rad($lon_degrees); 

     // find the northmost, southmost, eastmost and westmost corners $distance_in_miles away 
     // original formula from 
     // http://www.movable-type.co.uk/scripts/latlong.html 

     $northmost = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_north)); 
     $southmost = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_south)); 

     $eastmost = $lon_r + atan2(sin($due_east)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r)); 
     $westmost = $lon_r + atan2(sin($due_west)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r)); 

     $northmost = rad2deg($northmost); 
     $southmost = rad2deg($southmost); 
     $eastmost = rad2deg($eastmost); 
     $westmost = rad2deg($westmost); 

     //return 2 points NW corner and SE corner 
     return array($northmost,$westmost,$southmost,$eastmost); 
} 

那麼你的SQL是

SELECT * FROM table WHERE latitude <= $northmost AND longitude >= $westmost AND latitude >= $southmost AND longitude <= $eastmost

相關問題