Redis GEO地理位置信息的应用

Redis GEO地理位置信息的应用

  • Redis GEO
    • 概述
    • 应用场景
    • Redis GEO命令
    • GEO命令演示
  • Redis GEO实现附近人的功能
    • 基础类
    • API接口
    • 接口实现
    • 执行测试

Redis GEO

概述

Redis的GEO操作是一种基于地理位置信息进行操作的功能。它使用经度和纬度坐标来表示地理位置,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。

应用场景

地理围栏:通过设置地理位置的经纬度信息,可以将用户或者车辆等实体绑定在地理围栏内,当实体进出围栏时,可以触发相应的事件。

附近的人:在类似于约会、社交、旅游等场景下,可以通过Redis GEO快速查询周围的人员或景点。

配送服务:通过获取配送地址的经纬度信息,可以找到距离最近的配送员或仓库,并对订单进行分配。

地址查找:在地图应用中,可以通过Redis GEO快速查询某个地址周围的商家或服务设施。

动态信息:在滴滴、Uber等打车应用中,可以实时更新车辆的位置信息,以提供更加准确的车辆推荐和路线规划服务

Redis GEO命令

1.GEOADD添加位置信息

将一个或多个指定的地理位置(经度、纬度、名称)添加到指定的键中。

GEOADD key longitude latitude member [longitude latitude member ...]

添加一个名为cities的键,并将北京、上海、广州三个城市的经纬度和名称添加到该键中

GEOADD cities  116.4074 39.9042 Beijing 121.4737 31.2304 Shanghai 113.2644 23.1291 Guangzhou

GEOADD命令对于经纬度是有要求的:

有效的经度从-180度到180度

有效的纬度从-85.05112878度到85.05112878度

2.GEODIST查询距离

返回两个位置之间的距离。可以选择以米或千米为单位。

GEODIST key member1 member2 [unit]

可选参数unit用于指定计算距离时的单位:

m 表示单位为米

km 表示单位为千米

mi 表示单位为英里

ft 表示单位为英尺

查询北京和上海之间的距离,单位为千米

GEODIST cities Beijing Shanghai km

3.GEOHASH获取指定位置的Geohash值

返回一个或多个位置的Geohash值,该值用于对地理位置进行更快速的范围查找。

GEOHASH key member [member ...]

获取北京的Geohash值

GEOHASH cities Beijing

4.GEOPOS查询地理位置坐标

返回一个或多个位置的经度和纬度。

GEOPOS key member [member ...]

查询广州的经纬度

GEOPOS cities Guangzhou

5.GEORADIUS查找指定范围内的元素

查询给定坐标范围内的所有元素。可以通过设置排序选项来获取按距离排序的结果。

GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

查找距离北京1000公里以内的城市,并按距离升序排列

GEORADIUS cities 116.4074 39.9042 1000 km ASC

6.GEORADIUSBYMEMBER查询给定成员周围的所有元素

查询给定成员周围的所有元素。可以通过设置排序选项来获取按距离排序的结果。

GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

查找距离上海最近的城市,并返回它们的名称和距离

GEORADIUSBYMEMBER cities Shanghai 100 km WITHDIST

7.ZREM删除成员

从指定键中删除一个或多个成员。

ZREM key member [member ...]

从cities键中删除广州

ZREM cities Guangzhou

GEO命令演示

# 添加位置信息
本机:0>geoadd user:location 121.48941  31.40527 'shagnhai'
"1"
# 添加多个位置信息
本机:0>geoadd user:location 121.47941 31.41527 'shanghai1'  121.47941 31.43527 'shagnhai2'  121.47941 31.40527 'shagnhai3'
"3"



# 计算距离,单位有m km ft(英尺) mi(英里)
# 计算两点间的距离,单位m
本机:0>geodist user:location shanghai shanghai1 m
"1462.1834"
# 千米:km
本机:0>geodist user:location shanghai shanghai1 km
"1.4622"



# geohash 返回一个或多个位置元素的geohash,保存Redis中是用geohash位置52点整数编码
# geohash 将二维经纬度转换成字符串,每个字符串代表一个矩形区域,该矩形区域内的经纬度点都共享一个相同的geohash字符串。
本机:0>geohash user:location shanghai shanghai1
1) "wtw6st1uuq0"
2) "wtw6sqfx5q0"



# geopos 从key里返回指定成员的位置信息
本机:0>geopos user:location shanghai shanghai1
1) 1) "121.48941010236740112"
   2) "31.40526993848380499"

2) 1) "121.47941082715988159"
   2) "31.41526941345740198"



# georadius:给定经纬度为中心,返回键包含的位置元素中,与中心的距离不超过给定最大距离的所有位置元素
# 范围单位:m km mi ft
# withcoord:将位置元素的经纬度一并返回
本机:0>georadius user:location 121.48941 31.40527 3000 m withcoord
1) 1) "shagnhai3"
   2) 1) "121.47941082715988159"
      2) "31.40526993848380499"


2) 1) "shanghai1"
   2) 1) "121.47941082715988159"
      2) "31.41526941345740198"


3) 1) "shanghai"
   2) 1) "121.48941010236740112"
      2) "31.40526993848380499"

# withdist:返回位置元素的同时,将位置元素与中心点间的距离一并返回
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist
1) 1) "shagnhai3"
   2) "949.2411"

2) 1) "shanghai1"
   2) "1462.1719"

3) 1) "shanghai"
   2) "0.0119"

# asc:根据中心位置,按照从近到远的方式返回位置元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist asc
1) 1) "shanghai"
   2) "0.0119"

2) 1) "shagnhai3"
   2) "949.2411"

3) 1) "shanghai1"
   2) "1462.1719"


# desc: 根据中心位置,按照从远到近的方式返回位置元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist desc
1) 1) "shanghai1"
   2) "1462.1719"

2) 1) "shagnhai3"
   2) "949.2411"

3) 1) "shanghai"
   2) "0.0119"


# count:获取指定数量的元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist desc count 2
1) 1) "shanghai1"
   2) "1462.1719"

2) 1) "shagnhai3"
   2) "949.2411"



# georadiusbymember:和georadius命令类似,都可以找出指定位置范围内的元素,但是georadiusbymember的中心点是由给定位置元素决定的,而不像georadius使用经纬度决定中心点
本机:0>georadiusbymember user:location shanghai 3 km 
1) "shagnhai3"
2) "shanghai1"
3) "shanghai"

Redis GEO实现附近人的功能

基础类

创建NearMeUserVO视图对象,封装响应视图的基本数据信息

@Data
public class NearMeUserVO {

    public Integer id;
   /**
	* 距离
	*/
   public String distance;
}

创建User对象模拟操作用户

    @Data
    public class User {
        private  int id;
    }

API接口

创建添加、更新用户位置信息查询附近用户信息的2个API接口

@RestController
public class NearMeUserController {
    @Autowired
    private INearMeUserService nearMeUserService;

    /**
     * 添加、更新坐标
     *
     * @param lon
     * @param lat
     * @return
     */
    @PostMapping("/updateUserLocation")
    public String updateUserLocation(@RequestParam Float lon, @RequestParam Float lat) {
        nearMeUserService.updateUserLocation(lon, lat);
        return "OK";
    }

    /**
     * 获取附近的人
     * 查询距离指定经纬度一定范围内的用户,并返回结果列表
     *
     * @param radius
     * @param lon
     * @param lat
     * @return
     */
    @GetMapping("/getNearMe")
    public Object nearMe(Integer radius, Float lon, Float lat) {
        List<NearMeUserVO> nearMe = nearMeUserService.findNearMe(radius, lon, lat);
        return nearMe;
    }
}

接口实现

public interface INearMeUserService {

    /**
     * 更新坐标
     *
     * @param lon 经度
     * @param lat 纬度
     */
    void updateUserLocation(Float lon, Float lat);


    /**
     * 获取附近的人
     *
     * @param radius 半径,默认 1000m
     * @param lon    经度
     * @param lat    纬度
     * @return
     */
    List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat);
}
@Service
public class NearMeUserServiceImpl implements INearMeUserService {

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * 用户定位信息cKEY
     */
    private static final String USER_LOCATION_KEY = "user:location";


    public void updateUserLocation(Float lon, Float lat) {
        if (lon == null || lat == null) {
            throw new RuntimeException("获取经度、维度失败");
        }

        // 获取登录用户信息
        User user = this.getLoginUser();
        // 定义key
        String key = "user:location";

        // 将用户地理位置信息存入 Redis
        RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands.GeoLocation(user.getId(), new Point(lon, lat));
        redisTemplate.opsForGeo().add(key, geoLocation);
    }

    /**
     * 获取附近的人
     *
     * @param radius 半径,默认 1000m
     * @param lon    经度
     * @param lat    纬度
     * @return
     */
    public List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat) {
        // 获取登录用户信息
        User user = this.getLoginUser();
        int userId = user.getId();

        // 处理半径,默认 1000m
        if (radius == null) {
            radius = 1000;
        }

        // 获取用户经纬度
        Point point = null;
        if (lon == null || lat == null) {
            // 如果经纬度没传,那么从 Redis 中获取
            List<Point> points = redisTemplate.opsForGeo().position(USER_LOCATION_KEY, userId);
            if (points == null || points.isEmpty()) {
                throw new RuntimeException("获取经纬度失败");
            }
            point = points.get(0);
        } else {
            point = new Point(lon, lat);
        }

        // 初始化距离对象,单位 m
        Distance distance = new Distance(radius, RedisGeoCommands.DistanceUnit.METERS);
        // 初始化 Geo 命令参数对象
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        // 附近的人限制3,包含距离,按由近到远排序
        args.limit(3).includeDistance().sortAscending();
        // 以用户经纬度为圆心,范围 1000m
        Circle circle = new Circle(point, distance);
        // 获取附近的人 GeoLocation 信息
        GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResult = redisTemplate.opsForGeo().radius(USER_LOCATION_KEY, circle, args);

        // 构建有序 Map
        Map<Integer, NearMeUserVO> nearMeUserVOMap = Maps.newLinkedHashMap();
        // 完善用户信息
        geoResult.forEach(result -> {
            RedisGeoCommands.GeoLocation<Object> geoLocation = result.getContent();
            // 初始化Vo对象
            NearMeUserVO nearMeUserVO = new NearMeUserVO();
            nearMeUserVO.setId((Integer) geoLocation.getName());
            // 获取距离
            Double dist = result.getDistance().getValue();
            // 四舍五入精确到小数点后 1 位,方便客户端显示
            String distanceStr = NumberUtil.round(dist, 1).toString() + "m";
            nearMeUserVO.setDistance(distanceStr);
            nearMeUserVOMap.put((Integer) geoLocation.getName(), nearMeUserVO);
        });


        // 附近的人,可进一步完善用户信息
//        Integer[] userIds = nearMeUserVOMap.keySet().toArray(new Integer[0]);
        
        ArrayList<NearMeUserVO> nearMeUserVO = Lists.newArrayList(nearMeUserVOMap.values());

        return nearMeUserVO;
    }

    /**
     * 模拟获取真实登录用户信息
     *
     * @return
     */
    public User getLoginUser() {
        User user = new User();
        user.setId(1);
        return user;
    }
}

执行测试

访问执行添加用户地理位置数据接口,得到如下所示数据:

在这里插入图片描述
访问执行获取附近人接口,得到如下所示数据:

	"data": [
		{
			"id": 1,
			"distance": "0.0m"
		},
		{
			"id": 3,
			"distance": "949.3m"
		},
		{
			"id": 2,
			"distance": "1462.2m"
		}
	]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/28436.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flutter 库:提升开发体验——Quick

Flutter 库&#xff1a;提升开发体验——Quick 文章目录 Flutter 库&#xff1a;提升开发体验——Quick一、概述1、简介2、功能3、官方资料4、思考 二、基本使用1、安装2、基本使用3、运行结果 三、List 列表扩展示例四、Map 映射扩展示例五、其它示例 一、概述 1、简介 Quic…

MySQL-索引详解(五)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

设计模式(十三):行为型之模板方法模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

微服务_Hystrix

在每个服务中引用该组件&#xff0c;监控当前组件。可被GateWay、Fegin集成。简介 作用&#xff1a;防止服务雪崩 Hystrix是一个由Netflix开源的容错框架&#xff0c;它主要用于分布式系统中的服务间通信。Hystrix通过在调用服务的过程中添加各种容错机制&#xff0c;来保护系…

助你更好的理解 Python 字典

助你更好的理解 Python 字典 字典是Python中的常用数据类型之一&#xff0c;可将数据存储在键/值对中&#xff0c;同 Java 中的 Map 相似。 1、什么是字典理解&#xff1f; 字典理解是创建字典的一种优雅简洁的方法。 字典理解优化 使用字典理解优化函数。 示例&#xff…

深入理解Linux虚拟内存管理(七)

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核 Linux 设备驱动程序 Linux设备驱动开发详解 深入理解Linux虚拟内存管理&#xff08;一&#xff09; 深入理解Linux虚拟内存管理&#xff08;二&#xff09; 深入理解Linux虚拟内存管理&#xff08;三&#xff09; 深入理…

chatgpt赋能python:Introduction

Introduction 在机器学习中&#xff0c;模型的训练是非常重要的步骤之一。模型训练意味着为数据拟合合适的参数&#xff0c;以便能够准确地预测未来的值。Python是一种功能强大的编程语言&#xff0c;提供许多库和框架来训练机器学习模型。在本文中&#xff0c;我们将探讨如何…

mysql 最常用的一些语句

1 数据库相关操作 CREATE DATABASE IF NOT EXISTS daily-test DEFAULT CHARSET utf8 COLLATE utf8_general_ci&#xff1b; drop database daily_test; use daily_test 具体操作如下图上所示&#xff1a; 2 mysql常用数据类型 MySQL 数据类型 | 菜鸟教程 3 数据库表相关操作…

CSS基础学习--13 Display(显示) 与 Visibility(可见性)

一、定义 display属性设置一个元素应如何显示 visibility属性指定一个元素应可见还是隐藏 二、隐藏元素 - display:none或visibility:hidden 隐藏一个元素可以通过把display属性设置为"none"&#xff0c;或把visibility属性设置为"hidden"。但是请注意&am…

chatgpt赋能python:Python怎么抢优惠券?优惠不再是梦想!

Python怎么抢优惠券&#xff1f;优惠不再是梦想&#xff01; 在如今的消费社会&#xff0c;优惠券已成为人们购物时追逐的目标。而优惠券的数量有限且抢手&#xff0c;往往仅能在短时间内领取&#xff0c;因此初次抢到心仪的优惠券可谓令人欣喜不已。而对于程序员们而言&#…

上线客流人数统计系统实现资源的最大化利用

在流量管理方面&#xff0c;智慧客流采集系统的应用可以帮助商家实现资源的最大化利用。通过对客流量数据的分析&#xff0c;商家可以准确把握客流量变化规律&#xff0c;进而制定出最优化的资源配置方案。 AI客流视觉监控 一、某汽车4S店智慧客流采集系统案例展示 以某汽车4S…

ESXi 7.0 U3m Dell (戴尔) 定制版 OEM Custom Installer CD

VMware ESXi 7.0 Update 3m - 领先的裸机 Hypervisor (All OEM Customized Installer CDs) ESXi 7.0 U3m Standard (标准版) ESXi 7.0 U3m Dell (戴尔) 定制版 OEM Custom Installer CD ESXi 7.0 U3m HPE (慧与) 定制版 OEM Custom Installer CD ESXi 7.0 U3m Lenovo (联想) 定…

python中变量与字符串详解!!

❄️作者介绍&#xff1a;奇妙的大歪❄️ &#x1f380;个人名言&#xff1a;但行前路&#xff0c;不负韶华&#xff01;&#x1f380; &#x1f43d;个人简介&#xff1a;云计算网络运维专业人员&#x1f43d; 前言 初学者经常会遇到的困惑是&#xff0c;看书上或者是听课都懂…

django中url和视图函数path re_path views.py

目录 url的定义url的格式django中的urldjango中的创建自己的urldjango访问测试django中的path动态django中的path动态案例django中的path动态类型django中的path动态案例-计算器django的正则路由re_path() url的定义 url 统一资源定位符 url 用来表示互联网上某个资源的地址 …

详解c++---set的介绍

目录标题 set容器的介绍set的构造函数insert函数的介绍find函数erase函数count函数lower_boundupper_boundmultiset set容器的介绍 set容器可以看成我们上一篇文章学习的K结构的搜索二叉树&#xff0c;所以set容器不仅可以存储数据&#xff0c;还可以对数据进行排序和去重&…

新项目之初性能测试工作如何前移?

最近刚接手一个新项目&#xff0c;在最开始的时候要求对这个项目做性能测试&#xff0c;产品经理也给不出性能需求&#xff0c;只因为这个项目是电商项目&#xff0c;可能会有高并发&#xff0c;秒杀的场景&#xff0c;所以产品经理要求我们对这个项目必须做性能测试&#xff0…

【Linux:进程间信号】

文章目录 1 生活角度的信号2 技术应用角度的信号3 信号的产生3.1 由系统调用向进程发信号3.1.1 signal3.1.2 kill3.1.3 raise 3.2 由软件条件产生信号3.3 硬件异常产生信号3.4 通过终端按键产生信号3.5 总结思考一下 4 信号的保存4.1信号其他相关常见概念4.2在内核中的表示4.3 …

命令行创建uniapp项目

命令行创建uniapp项目 除了使用HBuilderX工具可视化搭建项目外&#xff0c;DCloud官方还提供了一个脚手架用于命令行搭建项目。 环境安装 全局安装vue-cli npm i vue/cli4 -g建议使用vue-cli 4.x版本&#xff0c;vue-cli 5.x与webpack存在冲突&#xff0c;会导致运行报错 …

【软件测试】测试用例设计要点总结

文章目录 考试题型简答题(一) 等价类划分1.1 划分等价类1.2 设计测试用例 (二) 边界值分析2.1 列出边界值分析表2.2 设计测试用例 (三) 因果图分析3.1 确定原因和结果3.2 确定原因和结果之间的逻辑关系3.3 在因果图上使用标准的符号标明约束条件 (四) 判定表驱动4.1 将因果图转…

如何利用ChatGPT写毕业论文

如何利用ChatGPT写毕业论文 ChatGPT是什么&#xff1f;利用ChatGPT写毕业论文的步骤1.准备数据2.训练模型3.生成论文4.检查论文 总结地址 ChatGPT是什么&#xff1f; ChatGPT是一个基于GPT-2模型的开源聊天机器人&#xff0c;它可以回答用户的问题&#xff0c;进行闲聊和提供各…