基於LBS的應用在眼下已是如火如荼,地理位置功能都相應的被添加在各大應用中,基本上算是作為了行業的標桿。最近開發的一個應用也是涉及到對用戶發表帖子的當前位置下的附近帖子的搜索,類似的搜索功能其實也不是什么新鮮事,但是貌似都沒有公布其實現,所以當時也是非常的茫然。
方案一:
最容易想到的肯定就是給定范圍然后直接全庫搜索,但是一旦數據量過大,性能肯定下降得非常快。
方案二:
后來在網上搜到還有一種常用的算法是geohash算法,它是一種地址編碼,它能把二維的經緯度編碼成一維的字符串,但是其算法自身也是有缺陷的,位於格子邊界兩側的兩點,雖然十分接近,但編碼會完全不同。實際應用中,可以同時搜索當前格子周圍的8個格子,即可解決這個問題,雖然不是十全十美,但是還是能解決問題。。
方案三:
因為想法二的局限性,所以就進一步查了下,突然發現一個好東西,那就是空間數據庫,而其底層的實現方式果然就是用到了的R樹算法,所以雖然想法二自己不能實現,但是還是證明了其強大性
方案四:
自己寫基於空間搜索的R樹。雖然有了點思路,但是有看過R樹的就應該知道,R樹的分裂與合並是比較麻煩的。
計算兩點距離的方法:
1 //從google maps的腳本里扒了段代碼,沒准啥時會用上。大家一塊看看是怎么算的。 2 3 private const double EARTH_RADIUS = 6378.137; 4 private static double rad(double d) 5 { 6 return d * Math.PI / 180.0; 7 } 8 9 public static double GetDistance(double lat1, double lng1, double lat2, double lng2) 10 { 11 double radLat1 = rad(lat1); 12 double radLat2 = rad(lat2); 13 double a = radLat1 - radLat2; 14 double b = rad(lng1) - rad(lng2); 15 double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a/2),2) + 16 Math.Cos(radLat1)*Math.Cos(radLat2)*Math.Pow(Math.Sin(b/2),2))); 17 s = s * EARTH_RADIUS; 18 s = Math.Round(s * 10000) / 10000; 19 return s; 20 }
數據庫查找附近的(按距離排序)
$sql='select * from users_location order by ACOS(SIN(('.$lat.' * 3.1415) / 180 ) *SIN((latitude * 3.1415) / 180 ) +COS(('.$lat.' * 3.1415) / 180 ) * COS((latitude * 3.1415) / 180 ) *COS(('.$lon.' * 3.1415) / 180 - (longitude * 3.1415) / 180 ) ) * 6380 asc limit 10';
空間數據庫msdn資料:http://msdn.microsoft.com/zh-cn/library/cc280766(v=sql.100).aspx
方法:http://msdn.microsoft.com/zh-cn/library/bb933917(v=SQL.100).aspx
使用sql server 2008 spatial搜索距離內的地理對象 計算兩點的示例:來自:http://www.soaspx.com/dotnet/sql/mssql/sql2008/sqlserver2008_20100512_4317.html
方法1: 使用 STDistance STDistance(geography 數據類型) 返回一個 geography 實例中的點與另一個 geography 實例中的點之間的最短距離。 語法 .STDistance ( other_geography ) 算法 代碼 1 declare @urplace = geometry::STPointFromText('POINT(nnnn mmmm)',4326); 2 3 select 4 name,lng,lat,location.STDistance(@urplace) as distance 5 from 6 geotable 7 where 8 location.STDistance(@urplace)<1000 9 這個方法計算精度高(STDistance返回精確的距離),但是作為where條件,也因此導致效率低,一般推薦在查詢的數據量比較少的情況下(幾百)使用 方法2:使用STBuffer STBuffer(geography 數據類型) 返回一個地理對象,該對象表示所有與 geography 實例的距離小於或等於指定值的點的並集。 語法 .STBuffer ( distance ) 算法 代碼 1 declare @urplace = geometry::STPointFromText('POINT(nnnn mmmm)',4326); 2 declare @bufArea = @urplace.STBuffer(1000) 3 4 select 5 name,lng,lat,location.STDistance(@urplace) as distance 6 from 7 geotable 8 where 9 location.Filter(@bufArea) = 1 此方法先是對原來地理對象建立緩沖區,然后通過Filter()與緩沖區有交集的數據集,再進行精確的STDistance 計算,Filter會使用到你為該表建立spatial索引,因此會極大地提高性能
參考:
經度緯度求500米范圍內經度緯度的數據 計算距離: http://bbs.php100.com/read-htm-tid-426915.html