空间点索引算法-GeoHash

介绍

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

一个字符串代表某一矩形区域,矩形区域内所有的点都共享相同的GeoHash字符串。相当于给区域内的点做了一个索引。

算法过程

对一个地理坐标编码时,按照初始区间范围纬度[-90,90]和经度[-180,180],计算目标经度和纬度分别落在左区间还是右区间。落在左区间则取0,右区间则取1。然后,对上一步得到的区间继续按照此方法对半查找,得到下一位二进制编码。当编码长度达到业务的进度需求后,根据“偶数位放经度,奇数位放纬度”的规则,将得到的二进制编码穿插组合,得到一个新的二进制串。最后,根据base32的对照表,将二进制串翻译成字符串,即得到地理坐标对应的目标GeoHash字符串。

注意:北纬为正、南纬为负,东经为正、西经为负。

为什么经纬度交错

保证相近的点,有相同的前缀。

base32算法

包含数字、小写字母。去掉a、i、l、o这4个字母。

字符串中的每一位和位置的关系

奇数位、纬度

640?wx_fmt=png

偶数位、经度

640?wx_fmt=png

特征

算法实际上就是对一个区域不断分形,分形的次数越多,表示的区域越小,精度越高。

公共前缀的长度越长,这两个点距离越近。反之,不成立。

精度计算

一般来说8位可以满足一般需求。

preview

计算两个点之间的举例

redis源码

https://github.com/redis/redis/blob/fad0b0d2a680498fce1cd7e153f8ad7396a79edf/src/geohash_helper.c

const double EARTH_RADIUS_IN_METERS = 6372797.560856;
#define D_R (M_PI / 180.0)
static inline double deg_rad(double ang) { return ang * D_R; }

double geohashGetDistance(double lon1d, double lat1d, double lon2d, double lat2d) {
    double lat1r, lon1r, lat2r, lon2r, u, v;
    lat1r = deg_rad(lat1d);
    lon1r = deg_rad(lon1d);
    lat2r = deg_rad(lat2d);
    lon2r = deg_rad(lon2d);
    u = sin((lat2r - lat1r) / 2);
    v = sin((lon2r - lon1r) / 2);
    return 2.0 * EARTH_RADIUS_IN_METERS *
           asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v));
}

当区域较小(10km*10km)时,可以使用泰勒展式简化计算,避免计算三角函数。

获取点附近的八个区域

可以根据位置关系,推算出点附近的八个区域。效率很高。

组件支持

Redis

PostgreSQL

网址

http://api.map.baidu.com/lbsapi/getpoint/index.html 获取经纬度

http://geohash.co/ 经纬度和geohash转换

https://en.wikipedia.org/wiki/Geohash

posted @ 2022-09-05 10:35  天下太平  阅读(112)  评论(0编辑  收藏  举报