GeoHash原理介绍以及在redis中的应用

GeoHash将二维信息编码成了一个一维信息。降维后有三个好处:

  • 编码后数据长度变短,利于节省存储。
  • 利于使用前缀检索
  • 当分割的足够细致,能够快速的对双方距离进行快速查询

GeoHash是一种地址编码方法。他能够把二维的空间经纬度数据编码成一个字符串。

1、介绍

我们知道,经度范围是东经180到西经180,纬度范围是南纬90到北纬90,我们设定西经为负,南纬为负,所以地球上的经度范围就是[-180, 180],纬度范围就是[-90,90]。

如果以本初子午线、赤道为界,地球可以分成4个部分。如果纬度范围[-90°, 0°)用二进制0代表,(0°, 90°]用二进制1代表,经度范围[-180°, 0°)用二进制0代表,(0°, 180°]用二进制1代表,那么地球可以分成如下4个部分:

如果在小块范围内递归对半划分呢?

 

可以看到,划分的区域更多了,也更精确了。geohash算法就是基于这种思想,划分的次数更多,区域更多,区域面积更小了。通过将经纬度编码,给地理位置分区。

1.1)Peano空间填充曲线:

从上面区间划分上来看,当将空间划分为四块时候,编码的顺序分别是左下角00,左上角01,右下脚10,右上角11,也就是类似于Z的曲线,当我们递归的将各个块分解成更小的子块时,编码的顺序是自相似的(分形),每一个子快也形成Z曲线,这种类型的曲线被称为Peano空间填充曲线。

这种类型的空间填充曲线的优点是将二维空间转换成一维曲线(事实上是分形维),对大部分而言,编码相似的距离也相近, 但Peano空间填充曲线最大的缺点就是突变性,有些编码相邻但距离却相差很远,比如上图中,0111与1000,编码是相邻的,但距离相差很大。

除Peano空间填充曲线外,还有很多空间填充曲线,如图所示,其中效果公认较好是Hilbert空间填充曲线,相较于Peano曲线而言,Hilbert曲线没有较大的突变。为什么GeoHash不选择Hilbert空间填充曲线呢?可能是Peano曲线思路以及计算上比较简单吧,事实上,Peano曲线就是一种四叉树线性编码方式。

1.2)在线GeoHash工具:

Geohash Converter

Go Free Range build outstanding software for the web.

Geohash Explorer

2、GeoHash编码算法

GeoHash算法以下三步:

2.1)首先将经纬度变成二进制:

比如这样一个点(39.923201, 116.390705)

纬度的范围是(-90,90),其中间值为0。对于纬度39.923201,在区间(0,90)中,因此得到一个1;(0,90)区间的中间值为45度,纬度39.923201小于45,因此得到一个0,依次计算下去,即可得到纬度的二进制表示,如下表:

 

最后得到纬度的二进制表示为:10111000110001111001

同理可以得到经度116.390705的二进制表示为:11010010110001000100

2.2)经纬度合并

经度占偶数位,纬度占奇数位,注意,0也是偶数位。

 合并后:11100 11101 00100 01111 00000 01101 01011 00001

2.3)按照Base32进行编码

Base32编码表的其中一种如下,是用0-9、b-z(去掉a, i, l, o)这32个字母进行编码。具体操作是先将上一步得到的合并后二进制转换为10进制数据,然后对应生成Base32码。需要注意的是,将5个二进制位转换成一个base32码。

上例最终得到的值为:wx4g0ec1

3、GeoHash编码说明

3.1)特点:

Geohash比直接用经纬度的高效很多,而且使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。

  • GeoHash用一个字符串表示经度和纬度两个坐标。在数据库中可以实现在一列上应用索引(某些情况下无法在两列上同时应用索引)
  • GeoHash表示的并不是一个点,而是一个矩形区域
  • GeoHash编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索
  • GeoHash编码越长,表示的范围越小,位置也越精确。因此我们就可以通过比较GeoHash匹配的位数来判断两个点之间的大概距离。
  • 字符串相似的表示距离相近(Z曲线突变情况除外)

 

3.2)使用时注意事项:

1)临界问题

由于GeoHash是将区域划分为一个个规则矩形,并对每个矩形进行编码,这样在查询附近POI信息时会导致以下问题,比如车在红点位置,区域内还有一个黄点。相邻区域内的绿点明显离红点更近。但因为黄点的编码和红点一样,最终找到的将是黄点。这就有问题了。

解决的思路很简单,我们查询时,除了使用定位点的GeoHash编码进行匹配外,还使用周围8个区域的GeoHash编码,这样可以避免这个问题。

2)曲线突变

我们已经知道现有的GeoHash算法使用的是Peano空间填充曲线,这种曲线会产生突变,造成了编码虽然相似但距离可能相差很大的问题,因此在查询附近餐馆时候,首先筛选GeoHash编码相似的POI(point of interest)点,然后进行实际距离计算。

3)GeoHash只是空间索引的一种方式:

GeoHash特别适合点数据,而对线、面数据采用R树索引更有优势。滴滴打车使用的google s2算法, 基于b tree 的R tree,基于二叉树的四叉树,还有网格索引。

4、java中GeoHash编码:

<dependency>
    <groupId>ch.hsr</groupId>
    <artifactId>geohash</artifactId>
    <version>1.4.0</version>
</dependency>

示例:

public static void main(String[] args) {
      String shipGeohash = GeoHash.geoHashStringWithCharacterPrecision(39.916527, 116.397128, 6);
      System.out.println(shipGeohash); //wx4g0d

      GeoHash fromGeohashString = GeoHash.fromGeohashString(shipGeohash);
      //1110011101001000111100000011000000000000000000000000000000000000 -> (39.9188232421875,116.38916015625) -> (39.913330078125,116.400146484375) -> wx4g0d
      System.out.println(fromGeohashString);
}

5、redis中的GEO

在 Redis 3.2 版本中,新增了存储地理位置信息的功能,即 GEO( geographic),它的底层通过 Redis 有序集合(zset)实现。不过 Redis GEO 并没有与 zset 共用一套的命令,而是拥有自己的一套命令。Redis GEO 提供了 6 个常用命令:

  • GEOADD
  • GEOPOS
  • GEODIST
  • GEORADIUS
  • GEORADIUSBYMEMBER
  • GEOHASH

这里我们仅介绍GEOHASH命令,其余命令可以到官网中去查看使用方式。

Redis GEO 使用 geohash 来保存地理位置的坐标。geohash 命令用于获取一个或多个位置元素的 geohash 值。语法格式:

GEOHASH key member [member ...]

示例:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEOHASH Sicily Palermo Catania
1) "sqc8b49rny0"
2) "sqdtr74hyu0"

 

 

 

 

 

 

 

  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值