GeoHash通过某种方法将二维的点数据转换成一维的数据,也就是将二维的经纬度转换成一维的字符串,方便对位置进行索引;
字符串越长,表示的范围越精确。5位的编码能表示10平方千米范围的矩形区域,而6位编码能表示更精细的区域(约0.34平方千米)
以点[39.928167,39.928167]为例对该点按上述方法进行编码
根据纬度编码,地球经纬度区间为[-90,90],通过上述算法对纬度39.928167进行逼近编码
根据经度编码,地球经纬度区间为[-180,180],通过上述算法对经度116.389550进行逼近编码
分别计算出经纬度后将经纬度的编码按步骤4的算法组合在一起
通过上述计算,纬度产生的编码为10111 00011,经度产生的编码为11010 01011。偶数位放经度,奇数位放纬度,把2串编码组合生成新串:11100 11101 00100 01111。
最后使用用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,首先将11100 11101 00100 01111以5位为单位进行转成十进制,对应着28、29、4、15,十进制对应的编码就是wx4g。同理,将编码转换成经纬度的解码算法与之相反,具体不再赘述。
在buyer_poi表中新增geoHash字段和对geoHash创建的索引idx_geoHash,geoHash表示的是经度gd_longitude和纬度gd_latitude所对应的geoHash编码,字符串为10位,精度为0.596m米
一个经度和一个纬度一起确定地球上一个地点的精确位置。纬度的每个度的距离大约相当于111km,但经度的每个度的距离从0km到111km不等。
它的距离随纬度的不同而变化,沿同一纬度约等于111km乘纬度的余弦。不过这个距离还不是相隔一经度的两点之间最短的距离,最短的距离是
连接这两点之间的大圆的弧的距离,它比上面所计算出来的距离要小一些。
–from https://zh.wikipedia.org/wiki/经度
上述表示的范围是假定表示的距离范围在赤道附近,而在相同的纬度,经度相差一度在北纬60相差的距离是在赤道相差距离的一半。
由wiki可知,对于高纬度一定距离所覆盖的经度范围要比同样距离在赤道覆盖的经度范围要大,所以可能导致相同的距离在低纬度地区和高纬度地区所对应的geohash长度不同,这个问题有两种解决思路:
1、对于不同的纬度给定不同的精度,不同的的纬度梯度是怎样的?是不是会造成太多的不必要的计算?
2、对于不同的纬度给定的距离,都通过distance/cos(latitude)来换算成赤道地区的距离,然后再通过geohash长度和精度的对应表来确定geohash编码的长度;
具体的搜索算法为:
class RadiusGeohashLengthPair{
double radius;
int geoHashLength;
}
a、将表格中的数据按RadiusGeohashLengthPair组装成对象并存储到一个radiusGeohashLengthPairlist中,按radius升序排序
b、对于给定的距离半径rediusParam(假如rediusParam = 0.01km ),遍历radiusGeohashLengthPairlist直到找到第一个大于rediusParam的半径为止,在此表中半径为0.019km,其对应的geoHash length = 8
根据所需半径获取所需的geohash length,将编码后的字符串截取geohash length的字符串子串
a、根据所需半径r对照步骤8获取所需的geohash length
b、根据要求的点(longitude, latitude)获取geoHash的编码geoHashCodeStr
c、将编码后的字符串geoHashCodeStr截取geohash length的字符串子串geoHashCodeSubStr
去数据库通过like **%经由索引快速定位数据,查出所需的数据,然后再通过距离半径筛选所需的数据即可。
String searchParamStr = geoHashCodeSubStr + “%”
select * from buyer_poi where geoHash like searchParamStr and valid = 1 and status = 1