获取Geohash当前区域周围8个区域编码 | ini's Blog (jianlei.github.io)
由于GeoHash是用的Peano空间填充曲线实现的,因此会存在突变。如下图所示,很近的两个点分布在不同矩形区域,而两个较远的点缺分布在相同的区域,为了解决这问题,我们需要计算出该hash值附近的8个区域,再计算距离。
参考 Geohash
这种方式比较好理解,贴下例子就行了。
[116.3967,44.9999]和[116.3967,45.0009]分别出现在各自附近的区域中,周围8个区域的Geohash怎么计算得到呢?很简单,当Geohash长度是8时,对应的每个最小单元
1 2 | double latUnit = (Max_Lat - Min_Lat) / (1 << 20); double lngUnit = (Max_Lng - Min_Lng) / (1 << 20); |
其中20是因为二进制拆分了20次,对应长度为8.
这样可以计算出8个分别分布在周围8个区域的地点,根据地点便可以计算出周围8个区域的Geohash.
1 2 3 4 5 6 7 8 | [lat + latUnit, lng] [lat - latUnit, lng] [lat, lng + lngUnit] [lat, lng - lngUnit] [lat + latUnit, lng + lngUnit] [lat + latUnit, lng - lngUnit] [lat - latUnit, lng + lngUnit] [lat - latUnit, lng - lngUnit] |
参考资料
======================================================================
GitHub - GongDexing/Geohash: GeoHash是目前比较主流实现位置服务的技术,用最简洁的Java实现GeoHash算法
GeoHash是目前比较主流实现位置服务的技术,Geohash算法将经纬度二维数据编码为一个字符串,本质是一个降维的过程,
| 地点 | 经纬度 | Geohash |
|---|---|---|
| 鸟巢 | 116.402843,39.999375 | wx4g8c9v |
| 水立方 | 116.3967,39.99932 | wx4g89tk |
| 故宫 | 116.40382,39.918118 | wx4g0ffe |
水立方就在鸟巢在附近,距离600米左右,而故宫到鸟巢直线距离9公里左右,体现在Geohash上,鸟巢和水立方的前五位是一样的,而鸟巢和故宫只有前4位是一样的,也就是说Geohash前面相同的越多,两个位置越近,但是反过来说,却不一定正确,这个在后面会详细介绍。
将经纬度转换为Geohash大体可以分为三步曲:
将经纬度转换为二进制编码
private void convert(double min, double max, double value, Listlist) { if (list.size() > (length - 1)) { return; } double mid = (max + min) / 2; if (value < mid) { list.add('0'); convert(min, mid, value, list); } else { list.add('1'); convert(mid, max, value, list); } }
合并经纬度的二进制编码
ListlatList = new ArrayList (); List lngList = new ArrayList (); convert(Min_Lat, Max_Lat, lat, latList); convert(Min_Lng, Max_Lng, lng, lngList); StringBuilder sb = new StringBuilder(); for (int index = 0; index < latList.size(); index++) { sb.append(lngList.get(index)).append(latList.get(index)); }
base32编码
private final String[] base32Lookup =
{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "b", "c", "d", "e", "f", "g", "h",
"j", "k", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"};
private String base32Encode(final String str) {
String unit = "";
StringBuilder sb = new StringBuilder();
for (int start = 0; start < str.length(); start = start + 5) {
unit = str.substring(start, start + 5);
sb.append(base32Lookup[convertToIndex(unit.split(""))]);
}
return sb.toString();
}
private int convertToIndex(String str) {
int length = str.length();
int result = 0;
for (int index = 0; index < length; index++) {
result += str.charAt(index) == '0' ? 0 : 1 << (length - 1 - index);
}
return result;
}
两个位置距离得越近是否意味着Geohash前面相同的越多呢?答案是否定的,两个很近的地点[116.3967,44.9999]和[116.3967,45.0009]的Geohash分别是wxfzbxvr和y84b08j2,这就是Geohash存在的边界问题,这两个地点虽然很近,但是刚好在分界点45两侧,导致Geohash完全不同,单纯依靠Geohash匹配前缀的方式并不能解决这种问题 正在上传…重新上传取消
在一维空间解决不了这个问题,回到二维空间中,将当前Geohash这块区域周围的八块区域的Geohash计算出来 [116.3967,44.9999] 周围8块区域的Geohash
y84b08j2, wxfzbxvq, wxfzbxvx, wxfzbxvp, y84b08j8, y84b08j0, wxfzbxvw, wxfzbxvn
[116.3967,45.0009] 周围8块区域的Geohash
y84b08j3, wxfzbxvr, y84b08j8, y84b08j0, y84b08j9, y84b08j1, wxfzbxvx, wxfzbxvp
[116.3967,44.9999]和[116.3967,45.0009]分别出现在各自附近的区域中,周围8个区域的Geohash怎么计算得到呢?很简单,当Geohash长度是8时,对应的每个最小单元
double latUnit = (Max_Lat - Min_Lat) / (1 << 20);
double lngUnit = (Max_Lng - Min_Lng) / (1 << 20);
这样可以计算出8个分别分布在周围8个区域的地点,根据地点便可以计算出周围8个区域的Geohash
- [lat + latUnit, lng]
- [lat - latUnit, lng]
- [lat, lng + lngUnit]
- [lat, lng - lngUnit]
- [lat + latUnit, lng + lngUnit]
- [lat + latUnit, lng - lngUnit]
- [lat - latUnit, lng + lngUnit]
- [lat - latUnit, lng - lngUnit]
打开饿了么这样的应用,除了可以看到附近的商家外,还能清晰看到离每个商家的距离,这个距离的怎么计算出呢?这完全是一个数学问题,把地球看着一个球体,先根据经纬度算出空间坐标,进而算出两点直线距离,最后算出弧长,便是两个位置的距离
public static double distance(double lat1, double lng1, double lat2, double lng2) {
double x1 = Math.cos(lat1) * Math.cos(lng1);
double y1 = Math.cos(lat1) * Math.sin(lng1);
double z1 = Math.sin(lat1);
double x2 = Math.cos(lat2) * Math.cos(lng2);
double y2 = Math.cos(lat2) * Math.sin(lng2);
double z2 = Math.sin(lat2);
double lineDistance =
Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
double realDistance = EARTH_RADIUS * Math.PI * 2 * Math.asin(0.5 * lineDistance) / 180;
return realDistance;
}
在实际应用中,先根据Geohash筛选出附近的地点,然后再算出距离附近地点的距离
====================================================================
(433条消息) geohash网格图_GeoHash算法学习讲解、解析及原理分析_路云飞的博客-CSDN博客
目前比较通行的做法就是我们不仅获取当前我们所在的矩形区域,还获取周围8个矩形块中的点。那么怎样定位周围8个点呢?关键就是需要获取周围8个点的经纬度,那我们已经知道自己的经纬度,只需要用自己的经纬度减去最小划分单位的经纬度就行。因为我们知道经纬度的范围,有知道需要划分的次数,所以很容易就能计算出最小划分单位的经纬度。

通过上面这张图,我们就能很容易的计算出周围8个点的经纬度了。有了经纬度就能定位到周围8个矩形块了。这样我们就能获取包括自己所在矩形块的9个矩形块中的所有的点。最后分别计算这些点和自己的距离(由于范围很小,点的数量就也很少,计算量就很少)过滤掉不满足条件的点就行了。
————————————————
版权声明:本文为CSDN博主「路云飞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_29580123/article/details/112309769
========================================================================
(433条消息) Python实现geohash编码与解码(TransBigData)_小旭学长的博客-CSDN博客_geohash python
Python实现geohash编码与解码(TransBigData)