以Uber打车服务举一个例子,比如有乘客需要打车,调度系统默认会给乘客筛选附近4KM的司机,然后可能会经过一系列的过滤条件,筛选出最合适的一个司机去为乘客服务,要注意的是这里的最合适并不是最近,因为也许最近的一个司机并非空载,或者乘客是一个VIP,而最近的司机并非是服务VIP客户的等等
如下图

虽然说最合适不等于最近,但司机空载寻找乘客,考虑成本不可能调度一个最远的司机过来,因此对于调度筛选的过程,在满足其它必须条件的前提下,肯定是距离越近越好
试想一下,假如乘客4KM周围车辆过多,首先按半径过滤,然后范围内每一辆车都要一一进行比较,最终筛选出合适的一辆车,就算乘客周围很近就有空车,还是必须要遍历,比较完范围内所有的车,效率比较低下
在此基础上,Uber的基于六边形的空间索引算法更为优良
1:将整个空间分为大量的六边形格子,每个格子对应一个空间索引,那么对于任何一个位置的点必然会落在某一个格子里,也就会按照经纬度建立起了空间索引
2:知道了经纬度,计算出当前位置空间索引,以及相邻格子索引,如果当前位置空间索引里找不到合适的(一些过滤条件),就扩充到相邻索引,当前索引已记录
3:选择六边形的原因是相邻索引只有6个,三角形和四边形都多余6个,相邻索引越少越好,再慢慢层级扩大,这样就能兼顾距离尽可能较近

到了这里还没有结束,对于格子大小的选择,也是要考虑的,假如格子太大,如果世界就一个格子,显然是神经病,按一开始的例子,试想如果格子边长,半径差不多4KM,那么和一开始算法没什么两样,无法体现出该方法优势,如果格子太小,边长几米,也没必要,因为一辆车都有2米厂,一个格子塞不了几辆车,所以对于格子大小的配置是要好好考量的,也就是空间索引的精度
对此Uber给了一些参考数据
具体参考h3/src/h3lib/lib/geoCoord.c这个C文件,一共提供了16种精度
/*
* The following functions provide meta information about the H3 hexagons at
* each zoom level. Since there are only 16 total levels, these are current
* handled with hardwired static values, but it may be worthwhile to put these
* static values into another file that can be autogenerated by source code in
* the future.
*/
double H3_EXPORT(hexAreaKm2)(int res) {
static const double areas[] = {
4250546.848, 607220.9782, 86745.85403, 12392.26486,
1770.323552, 252.9033645, 36.1290521, 5.1612932,
0.7373276, 0.1053325, 0.0150475, 0.0021496,
0.0003071, 0.0000439, 0.0000063, 0.0000009};
return areas[res];
}
double H3_EXPORT(hexAreaM2)(int res) {
static const double areas[] = {
4.25055E+12, 6.07221E+11, 86745854035, 12392264862,
1770323552, 252903364.5, 36129052.1, 5161293.2,
737327.6, 105332.5, 15047.5, 2149.6,
307.1, 43.9, 6.3, 0.9};
return areas[res];
}
double H3_EXPORT(edgeLengthKm)(int res) {
static const double lens[] = {
1107.712591, 418.6760055, 158.2446558, 59.81085794,
22.6063794, 8.544408276, 3.229482772, 1.220629759,
0.461354684, 0.174375668, 0.065907807, 0.024910561,
0.009415526, 0.003559893, 0.001348575, 0.000509713};
return lens[res];
}
double H3_EXPORT(edgeLengthM)(int res) {
static const double lens[] = {
1107712.591, 418676.0055, 158244.6558, 59810.85794,
22606.3794, 8544.408276, 3229.482772, 1220.629759,
461.3546837, 174.3756681, 65.90780749, 24.9105614,
9.415526211, 3.559893033, 1.348574562, 0.509713273};
return lens[res];
}
/** @brief Number of unique valid H3Indexes at given resolution. */
int64_t H3_EXPORT(numHexagons)(int res) {
static const int64_t nums[] = {122L,
842L,
5882L,
41162L,
288122L,
2016842L,
14117882L,
98825162L,
691776122L,
4842432842L,
33897029882L,
237279209162L,
1660954464122L,
11626681248842L,
81386768741882L,
569707381193162L};
return nums[res];
}
double H3_EXPORT(hexAreaKm2)(int res):记录的是单个六边形的大小,单位是KM * KM
double H3_EXPORT(hexAreaM2)(int res):记录的是单个六边形的大小,单位是M * M
double H3_EXPORT(edgeLengthKm)(int res):记录的是单个六边形的边长,单位是KM
double H3_EXPORT(edgeLengthM)(int res):记录的是单个六边形的边长,单位是M
int64_t H3_EXPORT(numHexagons)(int res):记录的是全球六边形的总数,也就是空间索引的总数
把这些精度值做一个列表

可以想象一下,精度越小,基本没啥用,神经质;精度越大,基本不好用,因为格子太小,正常情况下可能居中才有用途,一般7~10可能才是城市,小区街道界别,比较适用
