一、基本概念
地球是一个球,我们常用经纬度来表示某个点的坐标,例如:北海公园的坐标为(39.928167,116.389550)。GeoHash的基本原理就是将地球理解为一个展开的二维平面,将这个二维平面按照一定的规则划分为不同的单元格,每个单元格拥有具有唯一性的编码,如图一所示。至于这个编码如何计算,将会在后面的内容予以介绍。
图1:GeoHash原理示意图
从图中可以看出,每一个单元格的唯一性编码其实表示的是单元格所在的区域,而不是某个特定点的信息。矩形区域内的所有点共享相同的GeoHash值,这样做的目的一方面是为了保证数据的私密性,既可以大概表达位置,又不用准备描述所在位置;另一方面,则是把二维位置转换为一维数据,方便数据的缓存和检索操作。而GeoHash值的相似度则代表了距离的远近。所以,利用GeoHash的code可以快速进行空间关系判断。
我们都知道,地球纬度的范围是[-90, 90],经度的范围是[-180, 180]。这里我们以上面提到的北海公园坐标(39.928167,116.389550)为例演示GeoHash算法。
-
区间[-90,90]进行二分为[-90,0),[0,90],称为左右区间,可以确定39.928167属于右区间[0,90],给标记为1;
-
接着将区间[0,90]进行二分为[0,45),[45,90],可以确定39.928167属于左区间[0,45),给标记为0;
-
递归上述过程39.928167总是属于某个区间[a,b]。随着每次迭代区间[a,b]总在缩小,并越来越逼近39.928167;
-
如果给定的纬度x(39.928167)属于左区间,则记录0,如果属于右区间则记录1,这样随着算法的进行会产生一个序列1011100,序列的长度跟给定的区间划分次数有关。
计算过程如下图所示,最终的计算结果为 10111 00011
同样的,对经度值116.389550进行这种操作,如下图所示,最终得到的结果为11010 01011
最后,将经度放在偶数位置,纬度放在奇数位置:
序号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
值 经度 | 1 |
| 1 |
| 0 |
| 1 |
| 0 |
| 0 |
| 1 |
| 0 |
| 1 |
| 1 |
|
值 纬度 |
| 1 |
| 0 |
| 1 |
| 1 |
| 1 |
| 0 |
| 0 |
| 0 |
| 1 |
| 1 |
最终得到的GeoHash的值为:11100 11101 00100 01111,按照base32编码
GeoHash的值为:28 29 4 15,最终的GeoHash值为wx4g。解码算法与上述算法相反。GeoHash划分的单元格的大小其实表示了不同的精度,精度表如下:
三、GeoHash作用
首先,geohash用一个字符串表示经度和纬度两个坐标。某些情况下无法在两列上同时应用索引(例如MySQL 4之前的版本,Google App Engine的数据层等),利用geohash,只需在一列上应用索引即可。
其次,geohash表示的并不是一个点,而是一个矩形区域。比如编码wx4g0ec19,它表示的是一个矩形区域。使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。
第三,编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。这个特性可以用于附近地点搜索。首先根据用户当前坐标计算geohash(例如wx4g0ec1)然后取其前缀进行查询(SELECT * FROM placeWHERE geohash LIKE 'wx4g0e%'),即可查询附近的所有地点。Geohash比直接用经纬度的高效很多。
具体应用:
1、Mysql使用GeoHash转换成字符串存储
2、Postgresql的PostGis插件,支持空间计算
3、Mongodb自带距离计算
4、Redis自带距离计算
5、Mysql spitial