这里写自定义目录标题
GEOHash算法
经纬度范围
纬度:[90S, 90N] -> [-90, 90]
经度:[180W, 180E] -> [-180, 180]
编码示例
第一步:经纬度处理
以经纬度值:(116.389550, 39.928167)进行算法说明,对纬度39.928167进行逼近编码 (地球纬度区间是[-90,90])
- 区间[-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,序列的长度跟给定的区间划分次数有关,如下图
第二步:合并经纬度处理结果
合并:偶数位放经度,奇数位放纬度,把2串编码组合生成新串如下图
第三步:对合并结果进行base32编码
上面两步没啥疑问,但是在这一步,网上的各种资料给出的base32编码所省略的字母不同,比如有的说会是用字母A-Z和数字2-7,有的说是数字0-9,字母不要a,i,l,o,但是原理都一样,对合并结果每5个二进制位进行组合然后进行编码。例如这个示例中使用这个编码:
然后对11100 11101 00100 01111 0000 01101转成十进制,对应着28、29、4、15,0,13 十进制对应的base32编码就是wx4g0e。这样一个经纬度坐标就变成了一个区域坐标(看精度,精度越高越准确,但是精度低也可以保护用户地址信息安全:
现在,对于整个地球来说分成了32个区域:
这是Geohash计算出的第一个字符对应的地区,每个地区应该也是按照32个区域继续划分。
GeoHash缺陷
如图所示,我们将二进制编码的结果填写到空间中,当将空间划分为四块时候,编码的顺序分别是左下角00,左上角01,右下脚10,右上角11,也就是类似于Z的曲线,当我们递归的将各个块分解成更小的子块时,编码的顺序是自相似的(分形),每一个子快也形成Z曲线,这种类型的曲线被称为Peano空间填充曲线。
这种类型的空间填充曲线的优点是将二维空间转换成一维曲线(事实上是分形维),对大部分而言,编码相似的距离也相近,但Peano空间填充曲线最大的缺点就是突变性,有些编码相邻但距离却相差很远,比如0111与1000,编码是相邻的,但距离相差很大。
除Peano空间填充曲线外,还有很多空间填充曲线,如图所示,其中效果公认较好是Hilbert空间填充曲线,相较于Peano曲线而言,Hilbert曲线没有较大的突变。但是由于Peano曲线实现更加简单,在使用的时候配合一定的解决手段,可以很好的满足大部分需求,因此TD内部Geohash算法采用的是Peano空间填充曲线。
GeoHash的特点
Geohash比直接用经纬度的高效很多,而且使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。
GeoHash用一个字符串表示经度和纬度两个坐标。在数据库中可以实现在一列上应用索引(某些情况下无法在两列上同时应用索引)
GeoHash表示的并不是一个点,而是一个矩形区域。
GeoHash编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索。
编码越长,表示的范围越小,位置也越精确。因此我们就可以通过比较GeoHash匹配的位数来判断两个点之间的大概距离。
判断点与多边形的关系
https://blog.csdn.net/okiwilldoit/article/details/118897109
计算
理解了geohash算法的基本原理之后,本节将介绍一个实际应用中常见的场景:计算围栏范围内所有的Geohash编码。该场景封装为函数可以表示如下:输入组成围栏的点经纬度坐标集合和指定的geohash长度,输出一组geohash编码。
`public static Set getHashByFence(List points, int length)`
具体算法步骤如下:
-
输入围栏点坐标集合List points和指定的geohash长度length
-
计算围栏的外包矩形的左上角和右下角坐标lat_min、lat_max、lng_min、lng_max
-
根据lat_min、lat_max、lng_min、lng_max,计算外包矩形对角定点的距离d
-
以外包矩形中心点为圆心,以d/2为半径做一个圆,计算圆覆盖范围内的geohash
4.1 获取圆的外包矩形左上角和右下角定点坐标经纬度,存储到double[] locs
4.2 根据geohash字符长度计算该长度geohash编码对应的经纬度间隔(latA,lngA)
4.3 根据latA和lngA,计算出locs组成的矩形的左上角和右下角定点的经纬度,在geohash划分的网格的索引(也就是第几个),分别记为lat_min,lat_max,lng_min,lng_max
4.4 计算lat_min,lat_max,lng_min,lng_max对应范围内左右geohash的二进制编码,然后将经纬度二进制编码uncode为geohash字符编码,保存为Set sets
-
剔除sets中geohash编码对应矩形的中心点不在points围栏范围内的geohash,得到最终的geohash结果集