Geohash编码原理解析(附代码)

本文最后修改于2018-03-26,文章有问题或者转载请及时联系本人,如果对你有帮助,别忘了点下关注和喜欢,感谢!

本文文字内容,图片参考整理自:http://www.cnblogs.com/LBSer/p/3310455.html

0 背景

我们在日常生活中常常遇到这样的需求,在某一地点的时候需要找到当前位置最近的餐馆(地铁站,厕所)等。app接到我们的请求的时候可以计算当前位置和全国所有餐馆的距离,返回给我们最近的餐馆。显然这种方法是低效的,但我们可以通过一定的规则进对所有的餐馆进行过滤。索引就是一个很好的选择,比如用户在北京,那么我们只需要比较北京的餐馆就可以了(暂时不考虑边界问题)。

Geohash即是这样一种技术,由于餐馆等poi通常以经纬度表示,它就用来将二维平面的点以一种编码的方式建立索引。

1 感性认识

Geohash将二维的经纬度转换成一维的字符串。如下图展示,将北京中心划分成9个精度为5的Geohash区域。每一个字符串代表一个矩形区域,这个区域里的所有点拥有相同的Geohash字符串。我们可以将这个字符串作为key来存储餐馆的地址。如果用户WX3ER中进行请求,我们只需要找key为WX4ER的餐馆,进而计算距离即可。对于边界问题的处理通常根据业务来制定,也有一些比较简单的策略,比如我们我们不仅使用当前所在区域,也使用周围的8个区域。

10175177-beebb92d94978b2a..png

image

字符串越长表示的范围越精确,5位编码可以表示大约10平方千米的范围,6位编码可以表示约0.34平方千米的范围。

通常来说距离相近的区域字符串也比较相似。

2 算法步骤

Geohash的算法步骤不难,我们需要知道以下几个信息。这里采用原文的例子(北海公园39.928167 116.389550的坐标点)进行阐释。

坐标的表示方法一般为<纬度,经度>表示,和我们常说的经纬度顺序颠倒。地球纬度的范围是[-90,90],经度的范围是[-180,180]。

2.1 计算二进制编码

以北海公园的坐标点为例,Geohash算法遵循以下步骤处理纬度坐标:

  1. 区间[-90,90]进行二分为[-90,0),[0,90],称为左右区间,39.928167属于右区间[0,90],所以标记为1;
  2. 接着将区间[0,90]进行二分为[0,45),[45,90],可以确定39.928167属于左区间 [0,45),给标记为0;
  3. 递归上述过程39.928167总是属于某个区间[a,b]。随着每次迭代区间[a,b]总在缩小,并越来越逼近39.928167;
  4. 如果给定的纬度x(39.928167)属于左区间,则记录0,如果属于右区间则记录1,这样随着算法的进行会产生一个序列1011100,序列的长度跟给定的区间划分次数有关。

10175177-2f26c7a446281387..png

image

同样的我们按照同样方法处理经度坐标。得到两个二进制串纬度的1011100011,经度的1101001011。串的长度由我们定义的精度所确定,想要更高的精度,则二进制串也会更长

2.2 组码

通过上边计算,我们得到了经纬度的两个二进制串。接下来要将两个二进制串进行组合,组合的方式比较简单。我们在偶数位放经度,计数位放纬度,注意从0还是放,如果从1开始放则相反,长度为两个二进制串的组合。上述两个二进制串组合后得到11100 11101 00100 01111。之后将新的二进制串5个一组转换成base32的编码。

32个字符共包括0-9,a-z(去掉a,i,l,o),如下图所示。

10175177-c9fbb09d48304fb9..png

image

所以,经转换后的字符为wx4g。

3 解码

我们在上述过程中将经纬度坐标转化为了4精度的geohash串。如果给定我们一个geohash串,怎么转化为经纬度坐标呢。

原理和编码时候的原理类似,我们首先把geohash编码转化为二进制串,然后分离成经纬度串。同样根据0和1对区间进行划分,见下图。

10175177-bb261789b8aeb65f..png

image

图中例子是以ezs42为例子,转化后的二进制串为01101 11111 11000 00100 00010,分离后的纬度串为101111001001。绿色的区域表示在划分区间的时候选择的区间,mean value即区间的平均值。在最后一次划分结束后得到了我们的纬度信息。经度信息同理。

4 编码长度与精度

图摘自维基百科:http://en.wikipedia.org/wiki/Geohash

10175177-85e46da9233dc597..png

image

根据上图可以看到,对于奇数精度,按照5bits转换一个字符来说,需要奇数个bits,这种情况下lng比lat多1个bit。8精度的误差在19米左右。

5 出处

上文讲了Geohash的应用以及计算方式,但是不知道为什么要这样做,本节给出解释。

如下图所示:

10175177-604cc94bdd315719..png

image

当空间划分为4块的时候我们,b编码顺序依次是左下角00,左上角01,右下角10,右上角11,类似于倒下的Z字形。当我们递归分解的时候,如右图所示,每一个子块也形成了Z形线。这种类型称为Peano空间曲线。这种类型的空间填充曲线的优点是将二维空间转换成一维曲线(事实上是分形维),对大部分而言,编码相似的距离也相近, 但Peano空间填充曲线最大的缺点就是突变性,有些编码相邻但距离却相差很远,比如0111与1000,编码是相邻的,但距离相差很大。

对经纬度不断划分的过程类似于空间平面不断进行块划分的过程

6 代码

用c++简单实现了一个geohash的程序,需要注意的地方是对于奇数精度的处理。

地址:https://github.com/wj1066/tools/tree/master/geohash

猜你喜欢

转载自blog.csdn.net/wj1066/article/details/81124956
今日推荐