首页 > 代码库 > 附近门店的实现
附近门店的实现
门店系统作为O2O体系重要的一环,LBS功能自然是当中必不可少的利器。在搭建附近门店的系统,通常情况需要满足这样的需求:
(1) 附近默认距离(比如两公里)内的某商户的门店
(2) 找不到的话,扩大距离继续查询
(3) 查询到门店,返回距离数据
这么看,是球面空间两点的距离分析问题。可是随着门店数据的增加,简单距离计算的次数就会被放大,我们就会自然地考虑到,门店的位置是固定的,能否进行一些预处理,把门店先切片,然后把用户的位置先放到某个片中,再进行距离计算。
第一种思路,经纬度排除。纬度的每个度大约相当于111km,但经度的每个度的距离从0km到111km不等。它的距离随纬度的不同而变化,等于111km乘纬度的余弦。因此,使用纬度天然分隔,是一种解决方案。假设,现在一个用户的经纬度是(22.534756,113.922333),需要查询十公里内的数据,可以采用纬度小数点后一位进行排除,期后再进行经度排除。显然,这种分片方式显得累赘,而且效率和扩展性都不算特别良好。但是它提供了一种思维方式,既然经纬度可以分块,那把这些小块进行编号,再把商家固定在这些编号块上成为预处理数据,用户进入后再采用同样的编码方式把用户固定到小块上,再在同一个小块里面精确计算距离,不是可以解决问题了么?
于是,第二种思路来了,平面切片方式。Geohash就是当中一种采用二分切分的方法,简单介绍一下其实现原理。
(1)首先将纬度范围(-90, 90)平分成两个区间(-90, 0)、(0, 90), 如果目标纬度位于前一个区间,则编码为0,否则编码为1。
(2)使用前一个编码为1的区域,继续分割,直到达到精度要求。
(3)经度和纬度的编码合并,奇数位是纬度,偶数位是经度。得到一个二进制序列
(4)每五位分割序列(base32编码),使用数字0-9以及字母b-z(排除a、i、l、o)编码。
(5)由于临近两个方格的编码差异极大,因此需要检索的两个点是否在某个范围内,需要进行对周边八个格子的检索。
geohash精度列表
下面的介绍,所使用hash1,即为一位字母编码值,同理,hash6为六位字母编码值。
hash取值 |
面积(km^2) |
大概的纵横(纬度*经度)乘积(km*km) |
1(地球总面积) |
510,065,600 |
16384*32768 |
hash1 |
15,939,500 |
4096*4096 |
hash2 |
498,110 |
512*1024 |
hash3 |
15,565 |
128*128 |
hash4 |
486 |
32*16 |
hash5 |
15 |
4*4 |
hash6 |
0.5 |
1*0.5 |
hash7 |
15625(m^2) |
125*125(m*m) |
hash8 |
488.28125(m^2) |
31.25*15.625(m*m) |
说到这里,假如需要查询附近两公里的门店,应该采用那种hash取值去算呢?
[object Object]
上图为hash6的九宫格排布,图中每个小格都为1KM*0.5KM的面积,用户点在E格中任意位置,于是能看到,九宫格内能保证至少查询出附近0.5KM的门店,要满足2KM的需求,就需要81个格子。同样方式发现hash5只需要九个格子即可满足,但是最大覆盖范围达到了10KM,不满足距离要求门店数量可能会比较多。
分片筛选之后,得到基本满足需要的门店集合,再进行球面距离计算,即可得到门店距离了。
附近门店的实现