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个招商银行的经纬度
这是相关的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请求
查看返回效果
2. 结尾
这样的需求也类似于摇一摇添加好友,摇一摇只不过登录以后获取用户的位置,然后每隔一段定时上传用户所在的位置,根据用户的位置的中心点查找附近的用户.本质的业务是不变的。