背景
- 获取周边信息;附近人?附近的公司?附近的餐厅?附近的星巴克?
- 获取两个坐标位置的距离?
实现
可以使用redis、mongo、mysql进行坐标检索换算实现,redis适用版本: >= 3.2.0
GEO算法
wiki:https://en.wikipedia.org/wiki/Geohash
redis使用通用的GeoHash算法进行处理,首先假设有上海站坐标(121.455708,31.249574),然后根据二分法(左标记0;右标识1)
- 纬度[-90,90],分为[-90,0),[0,90],称为左右区间,可以确定31.249574属于右区间[0,90],给标记为1;
- 再将右区间[0,90],分为[0,45),[45,90]左右区间,可以确定31.249574属于左区间[0,90],给标记为0;
- 依次类推最终可以无限接近具体坐标31.249574,经度同理
- 假设给出最后:纬度的pv为:10101 10001,经度的pv为:11010 11001,pv的长度与给定的区间划分次数有关。
- 将偶数放经度,奇数放纬度重新排列组合最后结果为:11011 00110 11010 00011
- 将重新排列后的结果转化为十进制后分别为:27、6、2、3,然后使用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码 结果为:v623 (注:参考下方参照码)
- 在 Redis 中,经纬度使用 52 位的整数进行编码,然后存放至 zset 里面,zset 的 value 是元素的 key,score 是 GeoHash 的 52 位整数值。zset 的 score 虽然是浮点数,但是对于 52 位的整数值,可以做到无损存储。
纬度pv划分计算:
pv | min | mid | max |
1 | -90 | 0 | 90 |
0 | 0 | 45 | 90 |
1 | 0.000 | 22.5 | 45 |
0 | 22.5 | 33.75 | 45 |
1 | 22.5 | 28.125 | 33.75 |
1 | 28.125 | 30.9375 | 33.75 |
0 | 30.9375 | 32.34375 | 33.75 |
0 | 30.9375 | 31.640625 | 32.34375 |
0 | 30.9375 | 31.2890625 | 31.640625 |
1 | 30.9375 | 31.11328125 | 31.2890625 |
经度pv划分计算:
pv | min | mid | max |
1 | -180 | 0 | 180 |
1 | 0 | 90 | 180 |
0 | 90 | 135 | 180 |
1 | 90 | 112.5 | 135 |
0 | 112.5 | 123.75 | 135 |
1 | 112.5 | 118.125 | 123.75 |
1 | 118.125 | 120.9375 | 123.75 |
0 | 120.9375 | 122.34375 | 123.75 |
0 | 120.9375 | 121.640625 | 122.34375 |
1 | 120.9375 | 121.2890625 | 121.640625 |
Base32位编码参照:
Redis Shell命令
注:时间复杂度: 每添加一个元素的复杂度为 O(log(N)) , 其中 N 为键里面包含的位置元素数量。
1、GEOADD -----添加坐标命令(先经度, 后纬度)
GEOADD key longitude latitude member [longitude latitude member …]
eg:
192.168.0.102:4>geoadd coordinate 121.455708 31.249574 "上海站" 121.475164 31.228816 "上海人民广场"
"2"
192.168.0.102:4>geoadd coordinate 121.499717 31.239702 "东方明珠塔"
"1"
2、GEOPOS -----查询坐标信息
GEOPOS key member [member …]
eg:
192.168.0.102:4>geopos coordinate "东方明珠塔"
1) 1) "121.49971514940261841"
2) "31.23970195235578018"
192.168.0.102:4>geopos coordinate "东方明珠塔" "上海站"
1) 1) "121.49971514940261841"
2) "31.23970195235578018"
2) 1) "121.45570546388626099"
2) "31.24957469127141252"
3、GEODIST ----- 查询给定位置之间的距离
注:指定单位的参数 unit
必须是以下单位的其中一个,默认为m:
-
m
表示单位为米(默认)。 -
km
表示单位为千米。 -
mi
表示单位为英里。 -
ft
表示单位为英尺。
GEODIST key member1 member2 [unit]
eg:
192.168.0.102:4>geodist coordinate "上海站" "东方明珠塔"
"4326.7279"
192.168.0.102:4>geodist coordinate "上海站" "东方明珠塔" km
"4.3267"
4、GEORADIUS ----- 查询周围信息
注:以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]
eg:
192.168.0.102:4>georadius coordinate 121.458175 31.242564 3 km WITHDIST asc count 5
1) 1) "上海站"
2) "0.8144"
2) 1) "上海人民广场"
2) "2.2245"
192.168.0.102:4>georadius coordinate 121.458175 31.242564 5 km WITHDIST asc count 5
1) 1) "上海站"
2) "0.8144"
2) 1) "上海人民广场"
2) "2.2245"
3) 1) "东方明珠塔"
2) "3.9632"
192.168.0.102:4>
5、GEORADIUSBYMEMBER ----- 查询周围信息
注:和GEORADIUS 类似只是将坐标换位具体元素
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]
eg:
192.168.0.102:4>georadiusbymember coordinate "上海站" 5 km WITHDIST asc count 5
1) 1) "上海站"
2) "0.0000"
2) 1) "上海人民广场"
2) "2.9589"
3) 1) "东方明珠塔"
2) "4.3267"
6、GEOHASH ----- 查询hash值
注:一般只用于开发调试
GEOHASH key member [member …]
eg:
192.168.0.102:4>geohash coordinate "上海站"
1) "wtw3gbc3gn0"