使用Redis geo实现
redis geo是基于Sorted Set来实现的
Redis 3.2 版本新增了geo相关命令,用于存储和操作地理位置信息。提供的命令包括添加、计算位置之间距离、根据中心点坐标和距离范围来查询地理位置集合等,说明如下:
geoadd:添加地理位置的坐标。
geopos:获取地理位置的坐标。
geodist:计算两个位置之间的距离。
georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
geohash:返回一个或多个位置对象的 geohash 值
测试:
添加坐标点: geoadd region 117.130313 31.84504 "shuxihu" 117.143635 31.838962 "chanyeyuan" 117.143635 31.838962 "shiyanxiaoxue" 117.123423 31.842912 "aolai" 117.207504 31.879882 "zhiwuyuan"
// 查询距离117.130313 31.84504半径1000米地点信息
georadius region 117.130313 31.84504 1000 m
执行结果:
SpringBoot与redis geo整合
springframework中已经加入了对redis geo的支持,相关的类都在org.springframework.data.geo包下。而对redis的命令行交互,也提供了org.springframework.data.redis相关的类来支持相关开发。
为了在项目中方便使用,整理工具代码如下,主要封装了:
1、添加元素到redisgeo;
2、计算某指定集合下,给定中心和查询范围,获取区域内成员的方法;
3、计算两个成员的距离
4、查询某指定成员(数组)的位置信息
package com.octv.cloud.ota.controller;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author xiaoss
* @since 1.0, 2023年02月27日 11:31:45
*/
@RestController
@RequestMapping("test")
public class TestController {
@Autowired
private RedisTemplate<String,String> redisTemplate;
/**
* 添加节点及位置信息
* @param geoKey 位置集合
* @param pointName 位置点标识
* @param longitude 经度
* @param latitude 纬度
*/
public void geoAdd(String geoKey, double longitude, double latitude,String pointName){
Point point = new Point(longitude, latitude);
redisTemplate.opsForGeo().add(geoKey, point, pointName);
}
/**
*
* @param longitude
* @param latitude
* @param radius
* @param geoKey
* @param metricUnit 距离单位,例如 Metrics.KILOMETERS
* @param metricUnit
* @return
*/
public List<GeoResult<RedisGeoCommands.GeoLocation<String>>> findRadius(String geoKey
, double longitude, double latitude, double radius, Metrics metricUnit, int limit){
// 设置检索范围
Point point = new Point(longitude, latitude);
Circle circle = new Circle(point, new Distance(radius, metricUnit));
// 定义返回结果参数,如果不指定默认只返回content即保存的member信息
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
.newGeoRadiusArgs().includeDistance().includeCoordinates()
.sortAscending()
.limit(limit);
GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(geoKey, circle, args);
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
return list;
}
/**
* 计算指定key下两个成员点之间的距离
* @param geoKey
* @param member1
* @param member2
* @param unit 单位
* @return
*/
public Distance calDistance(String geoKey, String member1, String member2
, RedisGeoCommands.DistanceUnit unit){
Distance distance = redisTemplate.opsForGeo()
.distance(geoKey, member1, member2, unit);
return distance;
}
/**
* 根据成员点名称查询位置信息
* @param geoKey geo key
* @param members 名称数组
* @return
*/
public List<Point> geoPosition(String geoKey, String[] members){
List<Point> points = redisTemplate.opsForGeo().position(geoKey, members);
return points;
}
@GetMapping("test")
public void test(){
geoAdd("region",117.130313, 31.84504,"shuxihu");
geoAdd("region",117.143635,31.838962,"chanyeyuan");
geoAdd("region",117.143635,31.838962,"shiyanxiaoxue");
geoAdd("region", 117.123423,31.842912,"aolai");
geoAdd("region",117.207504,31.879882,"zhiwuyuan");
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list=findRadius("region",117.130313 ,31.84504 ,0.5,Metrics.KILOMETERS,5);
for(GeoResult<RedisGeoCommands.GeoLocation<String>> object:list){
System.out.println(object.getContent().getName());
}
}
}