基于Redis的GEO实现查找附近XX功能

Redis的GEO实现查找附近银行功能

1.前言
在一些比较特殊的业务中,经常有查找附近房源或者摇一摇添加附近的人的功能,而Redis在3.2版本以上新增了GEO实现 地理位置的功能,只有3.2以上的Redis版本才能使用。GEO的功能可以帮我们实现类似摇一摇和查找附近银行的功能。

2. GEO命令
Redis为我们提供如下5个命令可以对地图进行操作,实际还有GEOHASH,就不介绍了。
- GEOADD
- GEOPOS
- GEODIST
- GEORADIUS
- GEORADIUSBYMEMBER
1 GEOADD
添加我们的地理位置,次命令用于将地理位置添加到集合中
语法:

 geoadd key longitude latitude name[longitude loatitude name... ]

参数介绍:
key:集合的键
longitude:经度
latitude:维度
name:名字
可以添加多个地址

geoadd coordinates 113.986156 22.544673 "华侨城" 114.110793 22.568287 "红岭北"

2 GEOPOS
根据名字来获取经纬度信息
语法:

geopos key name[name... ]

参数介绍:
key:集合的键
name:名字

geopos coordinates 华侨城 红岭北

3 GEODIST
计算二个地理位置之间的距离
语法:

geodist key name1 name2 [UNIT]

参数介绍:
key:集合的键
unit:指定单位的参数,km表示单位为千米,mi表示单位为英里,ft表示单位为英尺

geodist coordinates 华侨城 红岭北

4 GEORADIUS
此命令可以用于获取指定范围的地理信息位置,也既查找特定范围之间的其他存在的地点。和georadiusbymember类似,只是georadiusbymember是通过已有的member名字进行获取。
语法:

 georadius key longitude latitude radius [UNIT] [WITHCOORD] [WITHDIST] [WITHHASH]

参数介绍:
key:集合的键
longitude:经度
latitude:维度
radius:距离,数字
unit:指定单位的参数,km表示单位为千米,mi表示单位为英里,ft表示单位为英尺
withdist: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
withcoord: 将位置元素的经度和维度也一并返回。
withhash:以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
可以指定查询集合的排序方式:
asc: 按照从近到远的方式返回位置元素。
desc: 按照从远到近的方式返回位置元素。

 georadius coordinates 113.986156 22.544673 20 km withdist

5 GEORADIUSBYMEMBER
此命令都可以用于获取指定的名字,也既查找特定范围之间的其他存在的地点。
语法:

 georadiusbymember key name radius [UNIT] [WITHCOORD] [WITHDIST] [WITHHASH]

参数介绍:
key:集合的键
name:名字
radius:距离,数字
unit:指定单位的参数,km表示单位为千米,mi表示单位为英里,ft表示单位为英尺
withdist: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
withcoord: 将位置元素的经度和维度也一并返回。
withhash:以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
可以指定查询集合的排序方式:
asc: 按照从近到远的方式返回位置元素。
desc: 按照从远到近的方式返回位置元素。

georadiusbymember coordinates 华侨城 20 km withdist

3. Java实现Redis的功能

引入相关需要操作Redis的jar包

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

这边测试Redis已有5个招商银行的经纬度
基于Redis的GEO实现查找附近XX功能_第1张图片
这是相关的pojo类的属性

package com.bank.bank.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Coordinates {

    private String title;   //名称

    private String address; //地址

    private Double x;       //经度

    private Double y;       //维度 

}

返回数据给前端的类

package com.bank.bank.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CoordinatesVO {

    private Coordinates coordinates;  //基本信息

    private String distance;          //距离

}

自定义响应数据结构代码

package com.bank.bank.utils;

/**
 * @Description: 自定义响应数据结构
 * 				200:表示成功
 * 				500:表示错误,错误信息在msg字段中
 * 				501:bean验证错误,不管多少个错误都以map形式返回
 * 				502:拦截器拦截到用户token出错
 * 				555:异常抛出信息
 */
public class JSONResult {

    // 响应业务状态
    private Integer status;

    // 响应消息
    private String msg;

    // 响应中的数据
    private Object data;
    
    // 不使用
    private String ok;	

    public static JSONResult build(Integer status, String msg, Object data) {
        return new JSONResult(status, msg, data);
    }

    public static JSONResult ok(Object data) {
        return new JSONResult(data);
    }

    public static JSONResult ok() {
        return new JSONResult(null);
    }
    
    public static JSONResult errorMsg(String msg) {
        return new JSONResult(500, msg, null);
    }
    
    public static JSONResult errorMap(Object data) {
        return new JSONResult(501, "error", data);
    }
    
    public static JSONResult errorTokenMsg(String msg) {
        return new JSONResult(502, msg, null);
    }
    
    public static JSONResult errorException(String msg) {
        return new JSONResult(555, msg, null);
    }

    public JSONResult() {

    }

    public JSONResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public JSONResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }

    public Boolean isOK() {
        return this.status == 200;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

	public String getOk() {
		return ok;
	}

	public void setOk(String ok) {
		this.ok = ok;
	}

}

核心业务代码

package com.bank.bank.controller;

import com.bank.bank.pojo.Coordinates;
import com.bank.bank.pojo.CoordinatesVO;
import com.bank.bank.utils.JSONResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;

@RestController
public class CoordinatesController {


    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 基于前端传过来的坐标和指定的距离,进行查询
     *
     * @param position 坐标 如:113.986156,22.544673
     * @param distance 距离 如:20
     * @return
     */
    @GetMapping("/outlets")
    public JSONResult outlets(String position, double distance) {
        if (position == null || distance == 0.0D) {
            return JSONResult.errorMsg("错误返回");
        }
        //分割距离
        String[] split = position.split(",");
        Double x = new Double(split[0]);   //经度
        Double y = new Double(split[1]);   //维度
        Point point = new Point(x, y);     //建立中心点
        Distance distanceInfo = new Distance(distance, Metrics.KILOMETERS); //千米
        Circle circle = new Circle(point, distanceInfo);
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        GeoResults<RedisGeoCommands.GeoLocation<String>> coordResult = redisTemplate.boundGeoOps("coordinates").radius(circle, args); //Redis查询
        List<CoordinatesVO> coordinatesVOArrayList = new ArrayList<>();      //封装
        for (GeoResult<RedisGeoCommands.GeoLocation<String>> geoLocationGeoResult : coordResult) {
            //循环遍历
            Distance distanceGet = geoLocationGeoResult.getDistance();
            //基本信息
            RedisGeoCommands.GeoLocation<String> location = geoLocationGeoResult.getContent();
            //名称
            String name = location.getName();
            //获取网点基本信息
            Coordinates coordinatesInfo = (Coordinates) redisTemplate.boundHashOps("coordinatesInfo").get(name);  //根据名字获取
            //距离
            double distanceResult = distanceGet.getValue();
            //封装成VO
            CoordinatesVO coordinatesVO = new CoordinatesVO(coordinatesInfo, distanceResult);
            //放入List集合
            coordinatesVOArrayList.add(coordinatesVO);
        }
        return JSONResult.ok(coordinatesVOArrayList);  //结果返回前端
    }
}

Get请求
在这里插入图片描述
查看返回效果
基于Redis的GEO实现查找附近XX功能_第2张图片
2. 结尾
这样的需求也类似于摇一摇添加好友,摇一摇只不过登录以后获取用户的位置,然后每隔一段定时上传用户所在的位置,根据用户的位置的中心点查找附近的用户.本质的业务是不变的。

你可能感兴趣的:(笔记)