Redis - Redis GEO实现经纬度测算距离,附近搜索范围

news2024/12/23 10:24:23

Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增

一、Redis GEO 操作方法

geoadd:添加地理位置的坐标

geopos:获取地理位置的坐标

geodist:计算两个位置之间的距离

georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集

georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合

geohash:返回一个或多个位置对象的 geohash 值

GeoOperations 的 add 方法

org.springframework.data.redis.core.GeoOperations

// Add RedisGeoCommands.GeoLocation into key.
Long add(K key, RedisGeoCommands.GeoLocation<M> location)

GeoOperations 的 distance 方法

org.springframework.data.redis.core.GeoOperations// Get the Distance between member1 and member2.
Distance distance(K key, M member1, M member2)

Spring整合

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class GeoUtil {
 
    @Autowired
    private RedisTemplate redisTemplate;
    
    /**
     * 作为存储经纬度列表的key值
     */
    private static final String GEO_KEY = "DISTANCE";
 
    /**
     * 将经纬度信息添加到redis中
     * @param certId 标识
     * @param longitude 经度
     * @param latitude 纬度
     */
    public void geoAdd(String certId, double longitude, double latitude) {
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        Point point = new Point(longitude, latitude);
        RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands.GeoLocation(certId, point);
        geoOperations.add(GEO_KEY, geoLocation);
    }
 
    /**
     * 两个人之间的距离
     * @param certId1
     * @param certId2
     * @return
     */
    public double distanceBetween(String certId1, String certId2) {
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        Distance distance = geoOperations.distance(GEO_KEY, certId1, certId2);
        return distance.getValue();
    }
 
    /**
     * 查询距离某个人指定范围内的人,包括距离多少米
     * @param certId
     * @param distance
     * @return
     */
    public Map<String, Double> distanceInclude(String certId, double distance) {
        Map<String, Double> map = new LinkedHashMap<>();
 
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = geoOperations.radius(GEO_KEY, certId, new Distance(distance), geoRadiusCommandArgs.includeDistance());
        if (geoResults != null) {
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = geoResults.iterator();
            while (iterator.hasNext()) {
                GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult = iterator.next();
                // 与目标点相距的距离信息
                Distance geoResultDistance = geoResult.getDistance();
                // 该点的信息
                RedisGeoCommands.GeoLocation<String> geoResultContent = geoResult.getContent();
                map.put(geoResultContent.getName(), geoResultDistance.getValue());
            }
        }
        return map;
    }
}

二、附近搜索功能

引入redis依赖

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
     <version>2.3.0.RELEASE</version>
</dependency>

domain

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel("位置信息")
public class Location {
    @ApiModelProperty("经度")
    private Double longitude;
    @ApiModelProperty("纬度")
    private Double latitude;
    @ApiModelProperty("半径")
    private Double radius;
    @ApiModelProperty("条数")
    private Long limit;
}

Service

        String baiduKey =  RedisConstant.KEY_BAIDU_POI + "cities";

        Map<String, Point> points = new HashMap<>();
        points.put("shijiazhuang", new Point(114.48, 38.03));
        points.put("xingtang", new Point(114.54, 38.42));
        points.put("guangcheng", new Point(114.84, 38.03));
        points.put("gaoyi", new Point(114.58, 37.62));
        points.put("zhaoxian", new Point(114.78, 37.76));
        points.put("jinxing", new Point(114.13, 38.03));
        points.put("luquan", new Point(114.03, 38.08));
        points.put("xinle", new Point(114.67, 38.33));
        points.put("zhengding", new Point(114.56, 38.13));
        RedisUtil.opsForGeo().add(baiduKey,points);


        //设置当前位置
        // // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
        Point point = new Point(114.56, 38.13);
        //设置半径范围 (KILOMETERS 千米;METERS 米)
        Metric metric = RedisGeoCommands.DistanceUnit.KILOMETERS;
        Distance distance = new Distance(3, metric);
        Circle circle = new Circle(point, distance);
        //设置参数 包括距离、坐标、条数
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs
                        .newGeoRadiusArgs()
                .includeDistance()//包含距离
                .includeCoordinates();//包含经纬度
//                .sortAscending()//正序排序
//                .limit(50); //条数
        GeoResults<RedisGeoCommands.GeoLocation<String>> radius = RedisUtil.opsForGeo().radius(baiduKey,circle, geoRadiusCommandArgs);

        if (geoResults != null) {
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = geoResults.iterator();
            while (iterator.hasNext()) {
                GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult = iterator.next();
                // 与目标点相距的距离信息
                Distance geoResultDistance = geoResult.getDistance();
                // 该点的信息
                RedisGeoCommands.GeoLocation<String> geoResultContent = geoResult.getContent();
            }
        }

Redis存入单个,批量存

/**
     * Redis缓存酒店ID,以及经纬度
     * 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
     * @param newHotelRecommend
     * @return
     */
    private void redisCacheLongitudeAndLatitude(HotelRecommendNew newHotelRecommend){
        String baiduKey = RedisKeyConstants.KEY_RECOMMEND_HOTEL + newHotelRecommend.getCityCode();

        // 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
        if (StringUtils.isNotEmpty(newHotelRecommend.getCoordinatesBaidu())){
            // 纬度 latitude;经度 longitude;苏州经纬度:纬度31.29.9468;经度121.16.0530
            String[] splitBaidu = newHotelRecommend.getCoordinatesBaidu().split(",");
            String latBaidu = splitBaidu[0];
            String lngBaidu = splitBaidu[1];

            // prefix + 城市code + 高德,百度类型
            // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
            redisCache.redisTemplate.opsForGeo().add(baiduKey ,
                    new Point(Double.parseDouble(lngBaidu),Double.parseDouble(latBaidu)),
                    newHotelRecommend.getHotelId().toString());
        }
    }

    /**
     * Redis缓存酒店ID,以及经纬度
     * 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
     * @param newHotelRecommendList
     * @return
     */
    private void batchRedisCacheLongitudeAndLatitude(List<HotelRecommendNew> newHotelRecommendList){

        Map<Integer,List<HotelRecommendNew>> groupCityMap = newHotelRecommendList.stream().collect(Collectors.groupingBy(HotelRecommendNew::getCityCode));
        groupCityMap.entrySet().stream().forEach((Map.Entry<Integer, List<HotelRecommendNew>> entry) -> {
            String baiduKey = RedisKeyConstants.KEY_RECOMMEND_HOTEL + entry.getKey();

            Map<String,Point> pointMap = new HashMap<>();
            entry.getValue().stream().forEach(hotelRecommendNew -> {
                // 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
                if (StringUtils.isNotEmpty(hotelRecommendNew.getCoordinatesBaidu())){
                    // 纬度 Latitude;经度 longitude;苏州经纬度:纬度31.29.9468;经度121.16.0530
                    String[] splitBaidu = hotelRecommendNew.getCoordinatesBaidu().split(",");
                    String latBaidu = splitBaidu[0];
                    String lngBaidu = splitBaidu[1];

                    // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
                    pointMap.put(hotelRecommendNew.getHotelId().toString(),new Point(Double.parseDouble(lngBaidu),Double.parseDouble(latBaidu)));
                }
            });

            // prefix + 城市code + 高德,百度类型 (批量加入)
            redisCache.redisTemplate.opsForGeo().add(baiduKey,pointMap);
        });
    }

存入Redis数据

测试数据

POST http://localhost:6001/geo/city
Content-Type: application/json

{
  "longitude": 114.56,
  "latitude": 38.13,
  "radius": 100000,
  "limit": 10
}

返回结果


{
  "code": 200,
  "message": "操作成功",
  "data": {
    "averageDistance": {
      "value": 31642.19217777778,
      "metric": "METERS",
      "unit": "m",
      "normalizedValue": 0.004961039905191403
    },
    "content": [
      {
        "content": {
          "name": "zhengding",
          "point": {
            "x": 114.55999821424484,
            "y": 38.12999923666221
          }
        },
        "distance": {
          "value": 0.1778,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 2.787647866453794E-8
        }
      },
      {
        "content": {
          "name": "shijiazhuang",
          "point": {
            "x": 114.55999821424484,
            "y": 38.02999941748397
          }
        },
        "distance": {
          "value": 13144.3531,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.0020608452123245394
        }
      },
      {
        "content": {
          "name": "xinle",
          "point": {
            "x": 114.55999821424484,
            "y": 38.329998875018696
          }
        },
        "distance": {
          "value": 24232.5609,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.0037993164618445796
        }
      },
      {
        "content": {
          "name": "guangcheng",
          "point": {
            "x": 114.55999821424484,
            "y": 38.02999941748397
          }
        },
        "distance": {
          "value": 26919.7324,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.004220626242427844
        }
      },
      {
        "content": {
          "name": "xingtang",
          "point": {
            "x": 114.55999821424484,
            "y": 38.419999219223335
          }
        },
        "distance": {
          "value": 32302.7819,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.005064610857371048
        }
      },
      {
        "content": {
          "name": "jinxing",
          "point": {
            "x": 114.55999821424484,
            "y": 38.02999941748397
          }
        },
        "distance": {
          "value": 39255.7243,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.006154732063610425
        }
      },
      {
        "content": {
          "name": "zhaoxian",
          "point": {
            "x": 114.55999821424484,
            "y": 37.760000919591185
          }
        },
        "distance": {
          "value": 45453.0791,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.007126388018946599
        }
      },
      {
        "content": {
          "name": "luquan",
          "point": {
            "x": 114.55999821424484,
            "y": 38.07999932707309
          }
        },
        "distance": {
          "value": 46718.8049,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.00732483559070619
        }
      },
      {
        "content": {
          "name": "gaoyi",
          "point": {
            "x": 114.55999821424484,
            "y": 37.62000066579741
          }
        },
        "distance": {
          "value": 56752.5152,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.00889797682301274
        }
      }
    ]
  }
}

Response code: 200; Time: 92ms; Content length: 1844 bytes

RedisGEO实现附近搜索功能_华安小书童的博客-CSDN博客

利用Redis的Geo功能实现查找附近的位置! - 知乎

RedisGEO实现附近搜索功能_华安小书童的博客-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

client-go初级篇,从操作kubernetes到编写单元测试

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 尽管长篇系列《client-go实战》的内容足够丰富&#xff0c;然而内容太多每个知识点也有一定深度&#xff0c;对于打算快速学习并开始kubernetes开发…

分层架构简介

MVC是架构模式&#xff08;设计模式中的结构性模式&#xff09;&#xff0c;不是系统架构&#xff0c;更不是我们常说的三层架构 MVC的缺陷如下&#xff1a; 1.导致控制器冗余&#xff08;有大量的业务逻辑&#xff0c;可能开始没有&#xff0c;但是后来越来越多&#xff09;…

QT学习笔记5--槽函数重载解决办法

connect函数 connect(sender, signal, receiver, slot); 槽函数示例 void student:: treat(QString foodname) void student:: treat(int index) 由上可见&#xff0c;有两个名字相同&#xff0c;但形参不同的槽函数。 可以通过函数指针的方式 &#xff0c;用指针指向具体…

linux环境安装mysql8.0.32

linux环境安装mysql8.0.32 一、下载安装包二、安装前准备2.1 卸载旧版本mysql2.2 检查是否安装了 mariadb 数据库2.3 安装依赖包创建 mysql 用户 三、安装3.1 上传并解压安装包&#xff08;上传路径没有要求&#xff0c;一般在/usr/local&#xff09;3.2 初始化数据库3.3 注册数…

Java面试题6月

redis有哪些缓存淘汰策略 https://blog.51cto.com/u_11720620/5198874 生产环境内存溢出&#xff08;OOM&#xff09;问题处理方案 https://note.youdao.com/ynoteshare/index.html?id5cc182642eb02bc64197788c7722baae&typenote&_time1688287588653 jstack找出占用…

C++之GNU C的__attribute__((constructor))和((destructor))静态构造函数实现(一百四十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Elasticsearch实战(二十三)---ES数据建模与Mysql对比 一对多模型

Elasticsearch实战—ES数据建模与Mysql对比实现 一对多模型 文章目录 Elasticsearch实战---ES数据建模与Mysql对比实现 一对多模型1.一对多 模型1.1 Mysql建模 2.一对多 Index ES 数据模型2.1 类似Mysql, 依旧创建两个Index索引库2.2 采用ES架构 嵌套数组模型2.3采用ES架构 冗余…

【JUC-2】Synchronized关键字相关知识

Synchronized synchronized是Java中的关键字&#xff0c;是一种同步锁。它修饰的对象有以下几种&#xff1a; 修饰一个代码块&#xff0c;被修饰的代码块称为同步语句块&#xff0c;其作用的范围是大括号{}括起来的代码&#xff0c;作用的对象是调用这个代码块的对象&#xf…

【C++2】进程 信号 dbus

文章目录 1.进程&#xff1a;fork()&#xff0c;ps -ef (同-aux) | more2.信号&#xff1a;signal&#xff08;, EXIT&#xff09;&#xff0c;jps2.1 捕捉信号&#xff1a;ctrlc&#xff1a;22.2 捕捉信号&#xff1a;kill -9&#xff1a;92.3 捕捉信号&#xff1a;kill&#…

欧几里得算法

0x00 前言 改补的内容是一点都不会少。本章来看欧几里得算法 0x01 概述 欧几里得算法又称为辗转相除法&#xff0c;指用于计算两个非负整数a和b的最大公约数。 两个整数的最大公约数是能够同时整除他们的最大的正整数。 基本原理&#xff1a;两个整数的最大公约数等于其中…

【动态规划】子数组系列(上)

子数组问题 文章目录 【动态规划】子数组系列&#xff08;上&#xff09;1. 最大子数组和1.1 题目解析1.2 算法原理1.2.1 状态表示1.2.2 状态转移方程1.2.3 初始化1.2.4 填表顺序1.2.5 返回值 1.3 代码实现 2. 环形子数组的最大和2.1 题目解析2.2 算法原理2.2.1 状态表示2.2.2 …

C++2(表达式和关系运算)

目录 1.表达式基础 1.表达式基础 运算符重载&#xff0c;就是自己定义 - * / 之类的运算符怎么运算 C中的左值和右值 C语言左值在左侧&#xff0c;右值在右侧 在cpp中要复杂的多 能取到地址的表达式是左值 不能取到地址的表达式是右值 常量对象为代表的左值不能作为赋值语句的左…

【Linux】网络相关概念概述以及原理简单分析介绍

文章目录 [toc] Linux 网络概述网络发展独立模式网络互联局域网LAN 和 广域网WAN 认识 "协议"协议的分层网络协议栈OSI七层模型TCP/IP五层(四层)模型TCP/IP网络协议栈 与 操作系统 的关系 **重新以计算机的视角看待 网络协议栈 局域网内部通信原理简单介绍不同局域网…

mybatis web使用02

处理 transfer 请求的 servlet package com.wsd.web;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRe…

GAMES101 笔记 Lecture08 Shading 2(Shading, Pipeline and Texture Mapping)

目录 Specular Term(高光项)Ambient Term(环境光照项)Blinn-Phong Reflection ModelShading Frequencies(着色频率)Shade each triangle(flat shading)在每个三角形上进行着色Shade each vertex (Gouraud shading)(顶点着色)Shade each pixel (Phong shading)Defining Per-Vert…

【C++详解】——哈希

目录 unordered系列关联式容器 unordered_map unordered_map的接口说明 1.unordered_map的构造 2.unordered_map的容量 3.迭代器相关 4.unordered_map的元素访问 5. unordered_map的查询 6.unordered_map的修改操作 unordered_set 性能测试 底层结构——Hash 哈希…

copula简介

二元正态copula最为重要

MySQL - 自连接查询

1. 测试数据 创建 category 表 : CREATE TABLE category(categoryid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主题id,pid INT(10) NOT NULL COMMENT 父id,categoryName VARCHAR(50) NOT NULL COMMENT 主题名字,PRIMARY KEY(categoryid) ) ENGINEINNODB AUTO_INCREM…

cmd的学习

目录 常用的cmd命令 使用cmd的例子 常用的cmd命令 指令作用盘符名称:盘符切换dir查看当前路径下的内容tree以树形结构输出当前路径下的内容cd进入单级目录cd ..回退到上一级目录cd 目录1\目录2\...进入多级目录cd \回退到盘符目录cls清屏exit退出窗口 &#xff08;值得注意的…

Android AlertDialog setView,kotlin

Android AlertDialog setView&#xff0c;kotlin <?xml version"1.0" encoding"utf-8"?> <com.google.android.material.textfield.TextInputLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width…