不知道大家是否思考过一个问题,在一些场景下(如大家在使用高德地图打车的时候,邻近的司机是如何知道你在他的附近并将你的打车通知推送给他去接单的?)是如何实现的?
一般来讲,大家也许会想到,首先肯定需要知道每位乘客的经纬度(lng,lat),也即是二维坐标(当然这是在绝对理想的情况,不考虑上下坡度)。
而在知道了经纬度之后,一个暴力简单且容易想到的思路就是将经纬度这个
「二元组」
都存放在一个
「数组」
当中,然后当我们需要拿到离我们规定范围内的用户(如获取当前位置方圆百米内正在打车的乘客),我们就可以去遍历维护的那个数组,以此去判断数组中的经纬度与自己所在经纬度的距离,然后判断是否在范围内。
显然这种方法一定是能够达到目的的,
但是值得注意的点是,维护的数据量一般来讲是海量的
,因此如果每次都需要遍历所有数据去进行计算,那这计算量以及存储量目前是无法满足的。那如何在此基础上去优化性能呢??那么这个内容就是本篇文章主要想探讨的问题......
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
-
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
-
视频教程:https://doc.iocoder.cn/video/
首先我想先介绍一下GeoHash这种算法
「基本原理」
,再讨论如何进行应用。
对于每一个坐标都有它的
经纬度(lng,lat)
,而GeoHash的原理就是将经纬度先通过一个
二分
的思路拿到一个
二进制数组
的字符串,然后再通过
base32编码
去进行压缩存储。
举一个例子,比如经纬度为(116.3111126,40.085003),对其进行二分步骤如下:
经度步骤:
bit
|
left
|
mid
|
right
|
1
|
-180
|
0
|
180
|
1
|
0
|
90
|
180
|
0
|
90
|
135
|
180
|
1
|
90
|
112.5
|
135
|
0
|
112.5
|
123.75
|
135
|
0
|
112.5
|
118.125
|
123.75
|
1
|
112.5
|
115.3125
|
118.125
|
0
|
115.3125
|
116.71875
|
118.125
|
1
|
115.3125
|
116.015625
|
116.71875
|
0
|
116.015625
|
116.3671875
|
116.71875
|
1
|
116.015625
|
116.19140625
|
116.3671875
|
1
|
116.19140625
|
116.279296875
|
116.3671875
|
0
|
116.279296875
|
116.323242188
|
116.3671875
|
1
|
116.279296875
|
116.301269532
|
116.323242188
|
0
|
116.301269532
|
116.31225586
|
116.323242188
|
纬度步骤:
bit
|
left
|
mid
|
right
|
1
|
-90
|
0
|
90
|
0
|
0
|
45
|
90
|
1
|
0
|
22.5
|
45
|
1
|
22.5
|
33.75
|
45
|
1
|
33.75
|
39.375
|
45
|
0
|
39.375
|
42.1876
|
45
|
0
|
39.375
|
40.78125
|
42.1876
|
1
|
39.375
|
40.078125
|
40.78125
|
0
|
40.078125
|
40.4296875
|
40.78125
|
0
|
40.078125
|
40.25390625
|
40.4296875
|
0
|
40.078125
|
40.166015625
|
40.25390625
|
0
|
40.078125
|
40.1220703125
|
40.166015625
|
0
|
40.078125
|
40.1000976563
|
40.1220703125
|
0
|
40.078125
|
40.0891113282
|
40.1000976563
|
1
|
40.078125
|
40.0836181641
|
40.0891113282
|
「其思路就是不断二分,如果原本值大于mid那本bit位就是1,以此往下递归,最终,我们递归二分得到纬度方向上的二进制字符串为 101110010000001,长度为 15 位」