随着使用GPS坐标位置的手机或者设备越来越多,越来越多的应用使用gps坐标位置,为此redis增加了一种新的结构geo,可以很方便的计算最近距离的点。
首先添加pom.xml的jar包依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<!-- redis -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2、做一个添加坐标点,一个查询附近坐标点的查询方法
@RestController
@RequestMapping("redisGeo")
public class RedisGeoController {
@Autowired
private RedisGeoService redisGeoService;
private final String GEO_KEY="geo_key";
/**
* 使用redis+geo 上传位置
* @param cityId
* @param driverId
* @param lng
* @param lat
* @return
*/
@PostMapping("addDriverPosition")
public Long addDriverPosition(String cityId,String driverId,Double lng,Double lat){
String redisKey= CommonUtil.buildRedisKey(GEO_KEY,cityId);
long addnum=redisGeoService.geoAdd(redisKey,new Point(lng,lat),driverId);
List<Point> points=redisGeoService.geoGet(redisKey,driverId);
System.out.println("添加坐标点的位置"+points);
return addnum;
}
/**
* 使用reidis+geo 查找附近司机位置
* @param cityId
* @param lng
* @param lat
* @return
*/
@GetMapping("getNearDrivers")
public List<DriverPosition> getNearDrivers(String cityId,Double lng,Double lat){
String redisKey=CommonUtil.buildRedisKey(GEO_KEY,cityId);
Circle circle=new Circle(lng,lat, Metrics.KILOMETERS.getMultiplier());
GeoResults<RedisGeoCommands.GeoLocation<String>> results=redisGeoService.nearByXY(redisKey,circle,5);
System.out.println("查询附近司机位置"+results);
List<DriverPosition> list =new ArrayList<>();
results.forEach(item->{
RedisGeoCommands.GeoLocation<String> location=item.getContent();
Point point=location.getPoint();
DriverPosition position=DriverPosition.builder().cityCode(cityId).driverId(location.getName()).lng(point.getX()).lat(point.getY()).build();
list.add(position);
});
return list;
}
}
3、添加bean DriverPosition
import lombok.*;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DriverPosition {
private String driverId;
private String cityCode;
private double lng;
private double lat;
}
4、添加geo的service方法
import lombok.extern.slf4j.Slf4j;
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.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
public class RedisGeoService {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 添加经纬度信息
* redis 命令; geoadd key 120.223213 39.123123 "上海"
* @param key
* @param point
* @param member
* @return
*/
public Long geoAdd(String key, Point point, String member){
log.info((redisTemplate==null)+"=====");
if(redisTemplate.hasKey(key)){
redisTemplate.opsForGeo().remove(key,member);
}
return redisTemplate.opsForGeo().add(key,point,member);
}
/**
* 根据key members批量获取坐标点
*
* redis 命令: geopos key 上海
* @param key
* @param members
* @return
*/
public List<Point> geoGet(String key, String... members){
return redisTemplate.opsForGeo().position(key,members);
}
/**
* 返回两个地方的距离,可以指定单位,比如:米m千米km,英里mi 英尺ft
* redis 命令 : deodist key 北京 上海
* @param key
* @param member1
* @param member2
* @param metric
* @return
*/
public Distance geoDist(String key, String member1, String member2, Metric metric){
return redisTemplate.opsForGeo().distance(key, member1, member2, metric);
}
/**
* 根据给定的经纬度,返回半径不超过指定距离的元素
*
* redis命令 georedius key 116.2323 39.123123123 100 Km WITHDIST WITHCOORDASC COUNT 5
* @param key
* @param circle
* @param count
* @return
*/
public GeoResults<RedisGeoCommands.GeoLocation<String>> nearByXY(String key, Circle circle, long count){
// includeDIstance 包含距离
// includeCoordinates 包含经纬度
// sortAscending 正序排序
// limit 限定返回的记录数
RedisGeoCommands.GeoRadiusCommandArgs args=RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(count);
return redisTemplate.opsForGeo().radius(key, circle, args);
}
/**
* 根据指定的地点查询半径在指定范围内的位置
*
* redis命令 : georadiusbymember key 北京 100 Km WITHDIST WITHCOORD ASC COUNT 5
* @param key
* @param member
* @param distance
* @param count
* @return
*/
public GeoResults<RedisGeoCommands.GeoLocation<String>> nearByPlace(String key,String member,Distance distance,long count){
//includeDistance 包含距离
//includeCoordinates 包含经纬度
//sortAscending 正序排序
//limit 限定返回的记录数
RedisGeoCommands.GeoRadiusCommandArgs args=RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(count);
return redisTemplate.opsForGeo().radius(key,member,distance,args);
}
/**
* 返回 geohash值
* redis命令: geohash key 北京
* @param key
* @param member
* @return
*/
public List<String> geoHash(String key,String member){
return redisTemplate.opsForGeo().hash(key,member);
}
}
5、添加application.yml文件
spring:
redis:
host: 127.0.0.1
port: 6379
# password: redis2020
database: 1
第二部分:测试
1、使用postman调用增加坐标的方法
http://localhost:8080/redisGeo/addDriverPosition?cityId=420000&driverId=000001&lng=114.366386&lat=30.408199
http://localhost:8080/redisGeo/addDriverPosition?cityId=420000&driverId=000002&lng=114.365281&lat=30.406869
http://localhost:8080/redisGeo/addDriverPosition?cityId=420000&driverId=000003&lng=114.368049&lat=30.412896
http://localhost:8080/redisGeo/addDriverPosition?cityId=420000&driverId=000004&lng=114.365248&lat=30.537860
2.查看redis存的值
注意:如果调用redis遇到
io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'GEOADD'
错误,说明自己windows版本的redis版本太低,需要下载更新版本的redis,大家可以下载redis4或者redis5的版本,下载地址:
https://github.com/tporadowski/redis/releases
3、调用查看附近坐标点的接口,查看是不是距离最近的坐标
参考文章:https://www.cnblogs.com/xuwenjin/p/12715339.html
完整的工程代码:
https://github.com/sunyuhuakeyboard/georedisdemo