背景
打开美团,可以通过自身定位查看附近的商品。打开社交软件,可以查看附近的人交友。打开滴滴,可以查看的附近的共享单车,那这些是如何实现?
Redis GEO
Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增。
Redis GEO 操作方法有:
- geoadd:添加地理位置的坐标。
- geopos:获取地理位置的坐标。
- geodist:计算两个位置之间的距离。
- georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
- georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
- geohash:返回一个或多个位置对象的 geohash 值。
- zrem:删除地理位置
基础使用
数据准备
先用百度地图提供的经纬度查询工具 拾取坐标系统,准备一些坐标信息:
选择三个点的坐标作为测数据,如下
地点 | 坐标 |
---|---|
翠湖公园(a) | 102.710039,25.054179 |
大观公园(b) | 102.679209,25.027989 |
动物园(c) | 102.714992,25.061773 |
添加地理位置
geoadd
geoadd 用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。
geoadd 语法格式如下:
代码语言:shell复制GEOADD key longitude latitude member [longitude latitude member ...]
重点参数说明如下:
- longitude 表示经度
- latitude 表示纬度
- member 是为此经纬度起的名字
此命令支持一次添加一个或多个位置信息。
以下实例中key为km
代码语言:shell复制geoadd km 102.710039 25.054179 "a"
geoadd km 102.679209 25.027989 "b"
geoadd km 102.714992 25.061773 "c"
查询位置信息
geopos
geopos 用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
geopos 语法格式如下:
代码语言:shell复制GEOPOS key member [member ...]
此命令支持查看一个或多个位置信息
代码语言:shell复制127.0.0.1:6379> geopos km a
1) 1) "102.69033282995223999"
2) "25.04997624927904809"
距离统计
geodist
geodist 用于返回两个给定位置之间的距离。
geodist 语法格式如下:
代码语言:shell复制GEODIST key member1 member2 [m|km|ft|mi]
member1 member2 为两个地理位置。
最后一个距离单位参数说明:
m :米,默认单位。
km :千米。
mi :英里。
ft :英尺。
代码语言:shell复制# 计算翠湖公园(a)到大观公园(b)的距离,单位km
127.0.0.1:6379> geodist km a b km
"4.2587"
计算翠湖公园(a)到大观公园(b)的距离是4.25公里,跟地图比对,结果基本吻合
查询某位置内的其他成员信息
georadius
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
georadiusbymember
georadiusbymember 和 georadius 命令一样, 都可以找出位于指定范围内的元素, 但是 georadiusbymember 的中心点是由给定的位置元素决定的, 而不是使用经度和纬度来决定中心点。georadius 与 georadiusbymember 语法格式如下:
代码语言:shell复制GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
参数说明:
- m 米,默认单位。
- km :千米。
- mi :英里。
- t :英尺。
- WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。
- WITHCOORD: 将位置元素的经度和纬度也一并返回。
- WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
- COUNT 限定返回的记录数。
- ASC: 查找结果根据距离从近到远排序。
- DESC: 查找结果根据从远到近排序。
根据给定的经纬度坐标查询附近的成员
代码语言:shell复制# 根据查询的坐标,查询5km以内的成员信息
127.0.0.1:6379> georadius km 102.705224 25.0499233 5 km
1) "b"
2) "a"
3) "c"
# 根据坐标查询5km以内的成员信息,并返回位置元素与中心之间的距离
127.0.0.1:6379> georadius km 102.705224 25.0499233 5 km withdist
1) 1) "b"
2) "3.5812"
2) 1) "a"
2) "0.6776"
3) 1) "c"
2) "1.6450"
#根据坐标查询5km以内的成员信息,并返回位置元素与中心之间的距离,从近到远,返回2个成员
georadius km 102.705224 25.0499233 5 km withdist count 2 asc
1) 1) "a"
2) "0.6776"
2) 1) "c"
2) "1.6450"
# 根据成员的坐标查询5km以内的成员信息,并返回位置元素与中心之间的距离,从近到远,返回2个成员
127.0.0.1:6379> georadiusbymember km a 5 km withdist count 2 asc
1) 1) "a"
2) "0.0000"
2) 1) "c"
2) "0.9813"
删除地理位置
zrem
删除地理位置信息
代码语言:shell复制zrem key member [member ...]
比如c成员的坐标信息
代码语言:shell复制127.0.0.1:6379> zrem km c
(integer) 1
场景实践
引入jedis
代码语言:java复制<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.0</version>
</dependency>
附近的门店
查找附近的门店,本地生活这种场景特别多。
我想喝一杯瑞幸咖啡,打开瑞幸的微信小程序,根据自身当前定位,查看下我附近都有哪些门店,哪个离我最近,那我就在哪个门店下单。
实现思路
- 门店的地址固定的,除非搬迁或者闭店,才会发生改变,根据门店地址换算为经纬度存储在redis
- 根据gps定位,获取自身当前的经纬度
- 根据定位的经纬度查询附近的门店
代码实现
代码语言:java复制public static void main(String[] args) {
Jedis jedis = new Jedis("10.1.250.157",6379);
jedis.auth("google00");
//添加门店a
jedis.geoadd("store",102.710039,25.054179,"a");
//添加门店b
jedis.geoadd("store",102.679209,25.027989,"b");
//添加门店c
jedis.geoadd("store",102.714992 ,25.061773,"c");
//我当前定位的坐标,查询附近的门店,从近到远的门店,并显示距离
GeoRadiusParam geoRadiusParam = new GeoRadiusParam();
geoRadiusParam.withDist().sortAscending();
List<GeoRadiusResponse> storeList = jedis.georadius("store",102.705224 ,25.0499233,5,GeoUnit.KM,geoRadiusParam);
for (GeoRadiusResponse r : storeList) {
System.out.println("门店" r.getMemberByString() " 距离我:" r.getDistance() "km");
}
}
结果:
代码语言:java复制门店a 距离我:0.6776km
门店c 距离我:1.645km
门店b 距离我:3.5812km
注意点:所有门店数据放入到store这个key里,查询如果频繁,这个key容易成为热key,可以使用redis集群,同样的数据多设置几个key,比如:store_1,store_2,store_3 ,查询的时候可以根据用户id取余,命中不同的key查询
附近的人
社交软件,经常看到附近的人,怎么做呢
实现思路
- 打开软件,当点击附近的人,定位自身的坐标
- 根据自身的坐标,去查询附近的附近的人
代码实现
代码语言:java复制 public static void main(String[] args) {
Jedis jedis = new Jedis("10.1.250.157",6379);
jedis.auth("google00");
//添加张三
jedis.geoadd("person_member",102.710039,25.054179,"zhangsan");
//添加小明
jedis.geoadd("person_member",102.679209,25.027989,"xiaoming");
//添加李四
jedis.geoadd("person_member",102.714992 ,25.061773,"lisi");
//我当前定位的坐标,查询附近5km范围内的人,从近到远排序,并显示距离
GeoRadiusParam geoRadiusParam = new GeoRadiusParam();
geoRadiusParam.withDist().sortAscending();
List<GeoRadiusResponse> storeList = jedis.georadius("person_member",102.705224 ,25.0499233,5,GeoUnit.KM,geoRadiusParam);
for (GeoRadiusResponse r : storeList) {
System.out.println(r.getMemberByString() " 距离我:" r.getDistance() "km");
}
}
结果
代码语言:java复制zhangsan 距离我:0.6776km
lisi 距离我:1.645km
xiaoming 距离我:3.5812km
计算距离
计算两个坐标之间的距离
代码实现
代码语言:java复制public static void main(String[] args) {
Jedis jedis = new Jedis("10.1.250.157",6379);
jedis.auth("google00");
//添加张三
jedis.geoadd("person_member",102.710039,25.054179,"zhangsan");
//添加小明
jedis.geoadd("person_member",102.679209,25.027989,"xiaoming");
//添加李四
jedis.geoadd("person_member",102.714992 ,25.061773,"lisi");
//我当前定位的坐标,查询附近5km的人,从近到远排序,并显示距离
GeoRadiusParam geoRadiusParam = new GeoRadiusParam();
geoRadiusParam.withDist().sortAscending();
Double dist = jedis.geodist("person_member","xiaoming","lisi",GeoUnit.KM);
System.out.println("xiaoming与lisi距离:" dist "km");
}
结果:
代码语言:java复制zhangsan与lisi距离:5.208km
写作不易,刚好你看到,刚好对你有帮助,动动小手,点点赞,欢迎转发,有疑问的欢迎留言或者私信讨论,有问必回。