如何实现查找附近的人-GEO

背景

打开美团,可以通过自身定位查看附近的商品。打开社交软件,可以查看附近的人交友。打开滴滴,可以查看的附近的共享单车,那这些是如何实现?

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 语法格式如下:

sql 复制代码
GEOADD key longitude latitude member [longitude latitude member ...]

重点参数说明如下:

  • longitude 表示经度
  • latitude 表示纬度
  • member 是为此经纬度起的名字

此命令支持一次添加一个或多个位置信息。

以下实例中key为km

arduino 复制代码
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 语法格式如下:

sql 复制代码
GEOPOS key member [member ...]

此命令支持查看一个或多个位置信息

ruby 复制代码
127.0.0.1:6379> geopos km a
1) 1) "102.69033282995223999"
   2) "25.04997624927904809"

距离统计

geodist

geodist 用于返回两个给定位置之间的距离。

geodist 语法格式如下:

css 复制代码
GEODIST key member1 member2 [m|km|ft|mi]

member1 member2 为两个地理位置。

最后一个距离单位参数说明:

m :米,默认单位。

km :千米。

mi :英里。

ft :英尺。

ruby 复制代码
# 计算翠湖公园(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 语法格式如下:

css 复制代码
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: 查找结果根据从远到近排序。

根据给定的经纬度坐标查询附近的成员

ruby 复制代码
# 根据查询的坐标,查询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

删除地理位置信息

sql 复制代码
zrem key member [member ...]

比如c成员的坐标信息

ruby 复制代码
127.0.0.1:6379> zrem km c
(integer) 1

场景实践

引入jedis

xml 复制代码
<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>5.1.0</version>
        </dependency>

附近的门店

查找附近的门店,本地生活这种场景特别多。

我想喝一杯瑞幸咖啡,打开瑞幸的微信小程序,根据自身当前定位,查看下我附近都有哪些门店,哪个离我最近,那我就在哪个门店下单。

实现思路

  1. 门店的地址固定的,除非搬迁或者闭店,才会发生改变,根据门店地址换算为经纬度存储在redis
  2. 根据gps定位,获取自身当前的经纬度
  3. 根据定位的经纬度查询附近的门店

代码实现

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");
        }
    }

结果:

less 复制代码
门店a 距离我:0.6776km
门店c 距离我:1.645km
门店b 距离我:3.5812km

注意点:所有门店数据放入到store这个key里,查询如果频繁,这个key容易成为热key,可以使用redis集群,同样的数据多设置几个key,比如:store_1,store_2,store_3 ,查询的时候可以根据用户id取余,命中不同的key查询

附近的人

社交软件,经常看到附近的人,怎么做呢

实现思路

  1. 打开软件,当点击附近的人,定位自身的坐标
  2. 根据自身的坐标,去查询附近的附近的人

代码实现

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");
        }
    }

结果

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");
    }

结果

zhangsan与lisi距离:5.208km

写作不易,刚好你看到,刚好对你有帮助,动动小手,点点赞,欢迎转发,有疑问的欢迎留言或者私信讨论,有问必回。

相关推荐
海绵波波10713 分钟前
flask后端开发(1):第一个Flask项目
后端·python·flask
weisian1511 小时前
Redis篇--常见问题篇8--缓存一致性3(注解式缓存Spring Cache)
redis·spring·缓存
HEU_firejef1 小时前
Redis——缓存预热+缓存雪崩+缓存击穿+缓存穿透
数据库·redis·缓存
小k_不小1 小时前
C++面试八股文:指针与引用的区别
c++·面试
weisian1512 小时前
Redis篇--常见问题篇7--缓存一致性2(分布式事务框架Seata)
redis·分布式·缓存
白云coy2 小时前
Redis 安装部署[主从、哨兵、集群](linux版)
linux·redis
Logintern092 小时前
Linux如何设置redis可以外网访问—执行使用指定配置文件启动redis
linux·运维·redis
AI人H哥会Java3 小时前
【Spring】控制反转(IoC)与依赖注入(DI)—IoC容器在系统中的位置
java·开发语言·spring boot·后端·spring
凡人的AI工具箱3 小时前
每天40分玩转Django:Django表单集
开发语言·数据库·后端·python·缓存·django
奔跑草-3 小时前
【数据库】SQL应该如何针对数据倾斜问题进行优化
数据库·后端·sql·ubuntu