GeoHash介绍与改进的GeoHash-Int

##关于GeoHash
GeoHash是一种将二维坐标数据编码为一维数据的算法,具体可以参考GeoHash Wiki.
简单的描述下算法, 首先将整个平面按x, y轴对半, 如下则得到四个区域, 分别二进制编码为(00, 01, 10, 11).

*  -----------------
*  |       |       |
*  |       |       |
*  | 0,1   | 1,1   |
*  -----------------
*  |       |       |
*  |       |       |
*  | 0,0   | 1,0   |
*  -----------------

每个编码代表一个矩形区域范围, 若某坐标落在区域01中, 则置首两位编码为01, 其中0为x轴的编码, 1为y轴的编码; 以此迭代类推下去,累积的编码长度越长, 则编码代表的区域与坐标本身的误差越小,无限逼近下去,是可以认为是完全等同的。 实际上,针对地球坐标,GeoHash编码在大约52bit,即类似上述步骤重复26次, 精度已经小于1m了,足够精确。

##基于GeoHash的空间索引实现
基于GeoHash的空间索引的实现,本质上就是将一组坐标通过GeoHash编码, 将编码后的值保存到存储系统中。后续的查找, 都基于GeoHash编码完成。
通常的GeoHash的实现,均是将GeoHash的编码为Base32的字符串。 按照以上的GeoHash算法描述, 由于相近的坐标有很大几率的GeoHash的开头部分是相同的, 这样在一般的存储服务例如mysql中,可以很容易的基于前缀匹配找到相近的点。相关的做法, 网上有不少详细描述,这里不再赘述。
大家可能也发现到, 简单基于前缀匹配的做法,对于目标坐标在GeoHash区域的边缘情况是无法解决的。针对这一点,通常的GeoHash实现也提供一个接口, 用于计算某个GeoHash编码的周围8个区域的编码(东,南, 西, 北, 东北, 东南, 西北, 西南)。分别对于周边8个区域查找,可以解决这类边缘情况。
这里也简单介绍下计算周边8个相邻区域的编码方法:
由于GeoHash的编码实际上是X轴的编码与Y轴的编码错位合并而成, 如X轴编码为11(扩展为1010), Y轴编码为01(扩展为0001), 则GeoHash的值为1011 = 1010 + 0001.

  • 东方的区域实际上是X轴编码+1, Y轴编码不变, 则为 1010 + 1 + 0001 = 1100
  • 西方的区域实际上是X轴编码-1, Y轴编码不变
  • 北方的区域实际上是X轴编码不变, Y轴编码+1
  • 南方的区域实际上是X轴编码不变, Y轴编码-1
  • 东北的区域实际上是X轴编码+1, Y轴编码+1
  • 西北的区域实际上是X轴编码-1, Y轴编码+1
  • 西南的区域实际上是X轴编码-1, Y轴编码-1
  • 东南的区域实际上是X轴编码+1, Y轴编码-1

##改进的GeoHash-Int
目前GeoHash实现大部分均只提供Base32编码的结果, Base32编码的优点是直观可视, 例如某些地图(包括Google)就用Base32的GeoHash字符串作为坐标的url。缺点则是占用空间大, 以及每增加一个字符,跨越的空间范围过大,不规则(考虑下每字符代表5bit, 以及GeoHash原理是每2bit算一个步进)。
鉴于上述的问题, 这里提供一个高性能的C99实现的GeoHash库,提供64bit int的GeoHash结果, 以及快速计算相邻区域的方法。这个库已经用于Ardb的空间索引实现, 表现极为优秀。 项目的地址在这里。 GeoHash-Int