【Redis】之Geo
概述
Geo就是Geolocation的简写形式,代表地理坐标。在Redis中,构造了能够存储地址坐标信息的一种数据结构,帮助我们根据经纬度来检索数据。
命令行操作方法
GEOADD
可以用来添加一个或者多个地理坐标。
GEODIST
返回一个key中两个成员之间的距离。
GEOHASH
将指定member的坐标转为哈希字符串形式返回。
GEOPOS
返回指定member的坐标。
GEORADIUS
指定圆心、半径,找到该圆内包含的member。
GEOSEARCH
在指定范围内搜索member。最新的一个命令,可以指定中心点的经纬度,也可以使用member;并且还可以指定搜索范围是圆形还是正方形。
StringRedisTemplate操作方法
add
用于添加地理坐标。
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test
public void test(){
Point point = new Point(112.653652,37.809874);
Long add = this.stringRedisTemplate.opsForGeo().add("ShanXi", point, "SXU");
System.out.println(add);
}
radius
用来查找坐标
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test void test() {
// 中心点
Point point = new Point(112.617734,37.797128);
// 半径
Distance radius = new Distance(100, RedisGeoCommands.DistanceUnit.KILOMETERS);
// 由中心点和半径组成的圆圈
Circle circle = new Circle(point, radius);
// 获取值之后要带回来的参数
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance() // 包含距离
.includeCoordinates() // 包含坐标
.sortAscending(); // 升序
// 进行查询
GeoResults<RedisGeoCommands.GeoLocation<String>> search =
this.stringRedisTemplate.opsForGeo().radius("ShanXi", circle, args);
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = search.getContent();
if(content == null || content.isEmpty()) {
System.out.println("没有数据");
}
for (GeoResult<RedisGeoCommands.GeoLocation<String>> geoLocationGeoResult : content) {
// 输出距离
System.out.println("距离:" + geoLocationGeoResult.getDistance());
RedisGeoCommands.GeoLocation<String> locationGeoResultContent = geoLocationGeoResult.getContent();
// 输出名称
System.out.println("member:" + locationGeoResultContent.getName());
// 输出坐标
System.out.println("坐标" + locationGeoResultContent.getPoint());
}
}
search
和radius方法类似,只不过search还可以查找矩形范围之内的坐标。
remove
删除操作
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test void test() {
Long remove = this.stringRedisTemplate.opsForGeo().remove("ShanXi", "TaiYuanXueYuan");
System.out.println(remove);
}
distance
查看两个地理位置之间的直线距离
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test void test() {
Distance distance = this.stringRedisTemplate.opsForGeo()
.distance("ShanXi", "SXU", "ZonBei");
System.out.println(distance);
}
代码示例
需求:山西所有高校建立之后,都会把自己的位置上传到某信息平台,并且还会查询距离自己学校30公里之内的其他学校。当学校换位置之后,就会删除当前信息。
基础配置
@RestController
@RequestMapping("/school")
public class SchoolController {
@Resource
private SchoolService schoolService;
}
@Service
public class SchoolService extends ServiceImpl<SchoolMapper, School> {
@Resource
private StringRedisTemplate stringRedisTemplate;
}
public interface SchoolMapper extends BaseMapper<School> {
}
@Data
public class School {
// 学校id
private Integer id;
// 学校名
private String name;
// 经度
private BigDecimal longitude;
// 纬度
private BigDecimal latitude;
}
上传位置信息
public String add(School school) {
// TODO 更新信息到数据库
boolean save = this.save(school);
// TODO 判断是否更新成功
if(save) {
// TODO 封装位置信息
Point point = new Point(school.getLongitude().doubleValue(),
school.getLatitude().doubleValue());
// TODO 存入数据库
this.stringRedisTemplate
.opsForGeo().add("GEO:SCHOOL", point, school.getName());
}
return "成功";
}
查询附近学校
public void queryMessage() {
// 距离
Distance distance = new Distance(30, RedisGeoCommands.DistanceUnit.KILOMETERS);
// 参数
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance() // 包含距离
.includeCoordinates() // 包含坐标
.sortDescending(); // 降序
// 开始查找
GeoResults<RedisGeoCommands.GeoLocation<String>> results = this.stringRedisTemplate
.opsForGeo()
.radius("GEO:SCHOOL", "SXU", distance, args);
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = results.getContent();
// 输出结果
for (GeoResult<RedisGeoCommands.GeoLocation<String>> geoLocationGeoResult : content) {
System.out.println("距离山西大学30公里之内大学的名称:" + geoLocationGeoResult.getContent().getName());
System.out.println("距离山西大学30公里之内大学的坐标:" + geoLocationGeoResult.getContent().getPoint());
System.out.println("距离山西大学30公里之内大学的距离:" + geoLocationGeoResult.getDistance());
}
}
删除位置信息
public boolean remove(String name) {
LambdaQueryWrapper<School> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(School::getName, name);
boolean subtract = this.remove(lambdaQueryWrapper);
if(!subtract) {
return false;
}
Long remove = this.stringRedisTemplate.opsForGeo()
.remove("GEO:SCHOOL", name);
return remove != null && remove == 1;
}