LBS——实现附近功能的几种方案浅谈
一、背景
附近的人、景点,也即POI,这种需求多见于LBS中,这块不是很专业,也没有特别去实现过,只是根据网络查找的相关网友的说法,将几种实现手段罗列一下。
前提条件说明,做这种需要兴趣点的经纬度信息,以及需要查询的地点的经纬度信息,前者属于历史数据(更新不频繁),后者则是在每次查询的时候都需要变更的。
二、具体实现方法
1、直接利用关系型数据库进行计算得出距离的方案
1 SELECT 2 id, ( 3 3959 * acos ( 4 cos ( radians(78.3232) ) 5 * cos( radians( lat ) ) 6 * cos( radians( lng ) - radians(65.3234) ) 7 + sin ( radians(78.3232) ) 8 * sin( radians( lat ) ) 9 ) 10 ) AS distance 11 FROM markers 12 HAVING distance < 30 13 ORDER BY distance 14 LIMIT 0 , 20;
里面的三角函数是基于球体进行距离的近似计算,然后根据这个会有一些查询上的优化,比如先筛选一个矩形区域,然后再进行上面的查询,类似下面这样
1 select name, 2 ( 3959 * acos( cos( radians(42.290763) ) 3 * cos( radians( locations.lat ) ) 4 * cos( radians( locations.lng ) - radians(-71.35368) ) 5 + sin( radians(42.290763) ) 6 * sin( radians( locations.lat ) ) ) ) AS distance 7 from locations 8 where active = 1 9 and locations.lat between X1 and X2 10 and locations.Long between y1 and y2 11 having distance < 10 ORDER BY distance;
具体可以参考Stack Overflow网址,
Find features within given coordinates and distance using MySQL
Fastest Way to Find Distance Between Two Lat/Long Points
2、利用相关数据库自带的geo地理处理功能
这个主要就包括了Redis,Mysql(需要5.7版本以上),MongoDB这三种实现方式,三者都是通过数据库自带的地理坐标处理函数实现给定经纬度查一定范围内的包含的兴趣点,
其中,
Redis利用内部提供的geo指令实现距离的计算以及筛选最近点
Mysql利用spatial数据类型和spatial索引,以及特有的一些函数,例如MBRContains,
MongoDB则是采用geoJSON这种数据格式(这个用的好像还挺多,echarts里面的地图功能就有用到这种数据格式,是种规范的地理表示方式),以及相关的地理查询操作完成一些像是最近距离等的需求
redis的具体实现可以参考这篇文章
Mysql和MongoDB的实现可以参考这篇文章结合官网,
Mysql: LBS实现查找附近的人 (两经纬度之间的距离)
3、利用GeoHash算法结合Mysql或者其他关系型数据库,自己实现附近点的搜索
关于geoHash是什么,可以参考下面这篇文章
里面说的为了解决临界点需要附近8个点的hash值,这个计算其实只要掌握经度的二进制值是从左往右逐渐加1,纬度的值则是从下往上逐渐加1,掌握这个规律剩下的都比较简单了。
具体原理只要知道前缀匹配的位数越多,距离越近即可(距离越近,前缀不一定匹配,具体在分界线处有问题,所以通过附近8个点一起计算),具体前缀匹配位数和精度的关系可以粗略见下表
至于具体实现不管是php,js还是java都可以在github上找到相关的线程代码。
使用范例可以参考
开发LBS应用之 根据一点的经纬度实现附近点的查询 – geohash
最后再提供一个讲解geoHash看起来比较高大上的网址
高效的多维空间点索引算法 — Geohash 和 Google S2
个人理解,就是将多维空间利用一维线段进行填充,最后将二维问题转换成一维问题,然后在一维的数据上进行索引,最后有个小问题就是对于精度选取是否得当对于问题影响较大,所以根据实际问题需要可能还需要进行对最后结果进行距离的筛选
三、小结
这些都是自己找的相关实现思路,并没有去特意实现,记录一下,防止遗忘。
从开发和效率角度来说,
方法3效率最高,但是具体细节调整需要可能会比较多,
方法2效率居中,如果没接触过这块功能,相关api掌握起来可能比较费时,但是会比较省时,
方法1效率最低,实现起来也简单。
具体开发过程可能需要根据业务需求以及原有业务数据的存储方式进行合理选择