查找附近的地点算法--笔记
基于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