geohash在个人目前的工作中用到的频率非常高,所以写一篇文章进行一些相关知识点的整理。
1 定义
geohash是由Gustavo Niemeyer发明的一套空间地理信息编码系统,能够将一个地理位置的经纬度信息转化为一串较短的数字和字母组成的字符串。geohash是一个层级空间数据结构,通过使用“Z型曲线”能够将空间划分到网格状的桶(buckets)中,一般也称为”空间填充曲线“[1]。
2 算法步骤
以大宁国际广场为例,计算其4位geohash
2.1 根据经纬度计算Geohash二进制编码
地球纬度区间是[-90,90], 大宁国际广场的纬度是31.280291,可以通过下面算法对纬度31.280291进行逼近编码:
1)区间[-90,90]进行二分为[-90,0),[0,90],称为左右区间,可以确定39.928167属于右区间[0,90],给标记为1;
2)接着将区间[0,90]进行二分为 [0,45),[45,90],可以确定31.280291属于左区间 [0,45),给标记为0;
3)递归上述过程31.280291总是属于某个区间[a,b]。随着每次迭代区间[a,b]总在缩小,并越来越逼近31.280291;
4)如果给定的纬度x(31.280291)属于左区间,则记录0,如果属于右区间则记录1,这样随着算法的进行会产生一个序列1010110001,序列的长度跟给定的区间划分次数有关。
根据纬度算编码
| bit | min | mid | max |
| 1 | -90.000 | 0.000 | 90.000 |
| 0 | 0.000 | 45.000 | 90.000 |
| 1 | 0.000 | 22.500 | 45.000 |
| 0 | 22.500 | 33.750 | 45.000 |
| 1 | 22.500 | 28.125 | 33.750 |
| 1 | 28.125 | 30.9375 | 33.750 |
| 0 | 30.9375 | 32.34375 | 33.750 |
| 0 | 30.9375 | 31.640625 | 32.34375 |
| 0 | 30.9375 | 31.2890625 | 31.640625 |
| 1 | 30.9375 | 31.1090625 | 31.2890625 |
根据经度算编码
| bit | min | mid | max |
| 1 | -180 | 0 | 180 |
| 1 | 0 | 90 | 180 |
| 0 | 90 | 135 | 180 |
| 1 | 90 | 112.5 | 135 |
| 0 | 112.5 | 123.75 | 135 |
| 1 | 112.5 | 118.125 | 123.75 |
| 1 | 118.125 | 120.9375 | 123.75 |
| 0 | 120.9375 | 122.34375 | 123.75 |
| 0 | 120.9375 | 121.640625 | 122.34375 |
| 1 | 120.9375 | 121.2890625 | 121.640625 |
2.2 编码组合
通过上述计算,纬度产生的编码为10101 10001,经度产生的编码为11010 11001。偶数位放经度,奇数位放纬度(从右往左),把2串编码组合生成新串:
11100 11001 11100 00011。
最后使用用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,首先将11100 11001 11100 00011转成十进制,对应着28、25、28、3,十进制对应的编码就是wtw3。同理,将编码转换成经纬度的解码算法与之相反,具体不再赘述。
最后使用用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,首先将11100 11001 11100 00011转成十进制,对应着28、25、28、3,十进制对应的编码就是wtw3。同理,将编码转换成经纬度的解码算法与之相反,具体不再赘述。
| Decimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| Base32 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | b | c | d | e | f | g |
| Decimal | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| Base32 | h | j | k | m | n | p | q | r | s | t | u | v | w | x | y | z |
3 geohash base32编码长度与经度
通过上述计算可以发现,如果迭代次数的增多,区间边界会越来越接近经纬度的值,精度也就越来越高,geohash具体编码长度需要根据具体的业务应用场景来确定。下表是摘自wikipedia的geohash编码精度和误差的范围表。
| geohash length | lat bits | lng bits | lat error | lng error | km error |
| 1 | 2 | 3 | ±23 | ±23 | ±2500 |
| 2 | 5 | 5 | ±2.8 | ±5.6 | ±630 |
| 3 | 7 | 8 | ±0.70 | ±0.70 | ±78 |
| 4 | 10 | 10 | ±0.087 | ±0.18 | ±20 |
| 5 | 12 | 13 | ±0.022 | ±0.022 | ±2.4 |
| 6 | 15 | 15 | ±0.0027 | ±0.0055 | ±0.61 |
| 7 | 17 | 18 | ±0.00068 | ±0.00068 | ±0.076 |
| 8 | 20 | 20 | ±0.000085 | ±0.00017 | ±0.019 |
4 geohash算法
下面讲解一下定义中提到的Z型曲线的问题。如图所示,我们将二进制编码的结果填写到空间中,当将空间划分为四块时候,编码的顺序分别是左下角00,左上角01,右下脚10,右上角11,也就是类似于Z的曲线,当我们递归的将各个块分解成更小的子块时,编码的顺序是自相似的(分形),每一个子快也形成Z曲线,这种类型的曲线被称为Peano空间填充曲线。
这种类型的空间填充曲线的优点是将二维空间转换成一维曲线(事实上是分形维),对大部分而言,编码相似的距离也相近, 但Peano空间填充曲线最大的缺点就是突变性,有些编码相邻但距离却相差很远,比如0111与1000,编码是相邻的,但距离相差很大[2]。
4 具体使用注意点
由于geohash是将区域划分为一个一个规则的矩形,所以在边界区域会有突变,可能查询POI信息的时候,自身处于一块区域的边界处,而所需要查询的POI虽然很近,但是在另一块geohash区域。解决的思路是,我们另外查询周围8个区域的信息,称之为geohash一对九查询,另外可以查询时进行距离的计算,解决突变问题。