一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。
我们可以来想一下如果让你,设计一个附近的人的功能,你会怎样设计? 我们都知道,通常我们标记一个位置会使用一个二位坐标,比如说经度和维度来标记。这样我们可能第一反应就是 ,这很简单啊,获取当前位置坐标,然后计算两个坐标之间的距离。这就可能存在一个问题,我们是不是 要把所有用户的距离都算一下? 这样计算量是不是有点儿 太大了?有没有更好的方法呢?我们可以来了解下 GeoHash算法。
什么是GeoHash算法?
GeoHash是一种地址编码方法。他能够把二维的空间经纬度数据编码成一个字符串
**这样做有什么好处呢?**这里我们先不谈,先看看他的实现方式和算法的具体过程。
1. 将坐标转换成二进制
经度范围是东经180到西经180,纬度范围是南纬90到北纬90,我们设定西经为负,南纬为负,所以地球上的经度范围就是[-180, 180],纬度范围就是[-90,90]。如果以本初子午线、赤道为界,地球可以分成4个部分。
如果划分的区间更小,这样所表示的区间就更加准确了,像这样。
们将二进制编码的结果填写到空间中,当将空间划分为四块时候,经度左半区间为0,右半区间为1,纬度下半区间 为0,上半区间为1,于是经纬度二进制编码交叉组合,得到编码的顺序分别是左下角00,左上角01,右下脚10,右上角11,也就是类似于Z的曲线,当我们递归的将各个块分解成更小的子块时,编码的顺序是自相似的(分形),每一个子块也形成Z曲线,这种类型的曲线被称为Peano空间填充曲线。是不是很巧妙。是不是大多数相邻的编码,都很相似,但是这种编码也有个缺点,就是突变性,比如1000和0100这两个 编码是相邻的,但是在空间中这两个点的距离巨大。所以我们换一种编码方式。
我们对编码的二进制进行base32编码就得到了一个地址字符串,某一个区域内算出来的编码是一样的,这样比直接使用二进制高效很多,而且有利于信息保护,这个字符串只能解析到某个位置附近,但是不能解析到某个具体位置,有利于用户的信息保护。
但是有边界问题
如图,如果车在红点位置,区域内还有一个黄点。相邻区域内的绿点明显离红点更近。但因为黄点的编码和红点一样,最终找到的将是黄点。这就有问题了。所以我们一般的做法是我们不仅获取当前我们所在的矩形区域,还获取周围8个矩形块中的点。那么怎样定位周围8个点呢?关键就是需要获取周围8个点的经纬度,那我们已经知道自己的经纬度,只需要用自己的经纬度减去最小划分单位的经纬度就行。因为我们知道经纬度的范围,有知道需要划分的次数,所以很容易就能计算出最小划分单位的经纬度。
通过上面这张图,我们就能很容易的计算出周围8个点的经纬度了。有了经纬度就能定位到周围8个矩形块了。这样我们就能获取包括自己所在矩形块的9个矩形块中的所有的点。最后分别计算这些点和自己的距离(由于范围很小,点的数量就也很少,计算量就很少)过滤掉不满足条件的点就行了。
另外 geohash 生成的字符串还有一些性质,就是公共前缀越长的点,距离就越相似,字符串的长度越长地理位置的精确性就越高。