滴滴打车如何找出方圆一千米内的乘客?揭开 GeoHash 的神秘面纱

因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享

点击关注#互联网架构师公众号,领取架构师全套资料 都在这里fbb2e275b53db532be8ec03acf1403c3.png

0、2T架构师学习资料干货分

上一篇:2T架构师学习资料干货分享

大家好,我是互联网架构师!

  • 背景

  • GeoHash基本原理介绍

  • GeoHash如何应用到这个问题当中?

  • 遗留问题


背景

不知道大家是否思考过一个问题,在一些场景下(如大家在使用高德地图打车的时候,邻近的司机是如何知道你在他的附近并将你的打车通知推送给他去接单的?)是如何实现的?

一般来讲,大家也许会想到,首先肯定需要知道每位乘客的经纬度(lng,lat),也即是二维坐标(当然这是在绝对理想的情况,不考虑上下坡度)。

而在知道了经纬度之后,一个暴力简单且容易想到的思路就是将经纬度这个「二元组」 都存放在一个「数组」 当中,然后当我们需要拿到离我们规定范围内的用户(如获取当前位置方圆百米内正在打车的乘客),我们就可以去遍历维护的那个数组,以此去判断数组中的经纬度与自己所在经纬度的距离,然后判断是否在范围内。

显然这种方法一定是能够达到目的的,但是值得注意的点是,维护的数据量一般来讲是海量的,因此如果每次都需要遍历所有数据去进行计算,那这计算量以及存储量目前是无法满足的。那如何在此基础上去优化性能呢??那么这个内容就是本篇文章主要想探讨的问题......

GeoHash基本原理介绍

首先我想先介绍一下GeoHash这种算法「基本原理」 ,再讨论如何进行应用。

对于每一个坐标都有它的经纬度(lng,lat) ,而GeoHash的原理就是将经纬度先通过一个二分的思路拿到一个二进制数组的字符串,然后再通过base32编码去进行压缩存储。

举一个例子,比如经纬度为(116.3111126,40.085003),对其进行二分步骤如下:

经度步骤:

bitleftmidright
1-1800180
1090180
090135180
190112.5135
0112.5123.75135
0112.5118.125123.75
1112.5115.3125118.125
0115.3125116.71875118.125
1115.3125116.015625116.71875
0116.015625116.3671875116.71875
1116.015625116.19140625116.3671875
1116.19140625116.279296875116.3671875
0116.279296875116.323242188116.3671875
1116.279296875116.301269532116.323242188
0116.301269532116.31225586116.323242188

纬度步骤:

bitleftmidright
1-90090
004590
1022.545
122.533.7545
133.7539.37545
039.37542.187645
039.37540.7812542.1876
139.37540.07812540.78125
040.07812540.429687540.78125
040.07812540.2539062540.4296875
040.07812540.16601562540.25390625
040.07812540.122070312540.166015625
040.07812540.100097656340.1220703125
040.07812540.089111328240.1000976563
140.07812540.083618164140.0891113282

「其思路就是不断二分,如果原本值大于mid那本bit位就是1,以此往下递归,最终,我们递归二分得到纬度方向上的二进制字符串为 101110010000001,长度为 15 位」

那此时就拿到了30bit位的字符串,然后就开始进行拼接

结合经度字符串 110100101011010 和纬度字符串 101110010000001,我们遵循先经度后纬度的顺序,逐一交错排列,最终得到的一维字符串为 11100 11101 00100 11000 10100 01001.

然后再进行Base32编码,主要步骤就是首先会维护一个「0-9A-Za-z」 中32个字符的数组,如:['a','b','1','2','3','4','5','6','7','A'...],然后再将这30位的字符串每五个一组(正好覆盖0-31的索引)去索引到指定字符以此拿到30/5=6位的base32编码去进行存储。

「ps:注意并不一定是必要将经纬度都二分得到15位长度,多少位都可以,只是精度越高结果也就越精确,但是算力就越大,只需在此做出权衡即可」

GeoHash如何应用到这个问题当中?

上面讲到了可以通过GeoHash将经纬度转换成bit位的字符串,那么怎么进行应用呢,其实答案很明显,其实如果经纬度越接近,他们的前缀匹配位数也就越长,比如

9c20bcb7b423ba3ea3986139c9e21186.jpeg

通过这个思路我们就比较容易得到我们想要的范围内的乘客了。

遗留问题

但是其实仅仅如此是不够的,因为一个base32其实是覆盖了一片区域的,它并不是说仅仅代表一个精确的ip地址,那这其实就衍生出了一些问题,就比如

c7fdc749c3f3a5e3110443a8cf107950.jpeg

用geohash那结果显然是AB更近,但是实际上A与B的距离比AE、AC、AD都远。这其实是一个边缘性的问题........后续我会更新如何去避免这种问题的出现

—  —

如喜欢本文,请点击右上角,把文章分享到朋友圈

1、2T架构师学习资料干货分享

2、10000+TB 资源,阿里云盘,牛逼!!

3、基本涵盖了Spring所有核心知识点总结

  · END ·

最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全。

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描上方二维码关注一下,您的支持是我坚持写作最大的动力。

求一键三连点赞、转发、在看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值