最近使用团油的时候总是觉得他的那个按照距离排序的功能很好,所以就试着研究一下。
1. 新建spring boot项目
1.1 pom.xml
添加redis依赖和lombok依赖
org.springframework.boot
spring-boot-starter-data-redis
org.projectlombok
lombok
1.18.16
provided
1.2 application.yml
# Redis数据库索引(默认为0)
spring:
redis:
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接池最大连接数(使用负值表示没有限制)
jedis:
pool:
max-active: 20
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
# 连接超时时间(毫秒)
timeout: 1000
1.3 新建实体类
/**
* 油站实体类
* @author zhouzhaodong
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ServiceStation implements Serializable {
/** 油站 */
private String serviceStationName;
/** 经度 */
private Double longitude;
/** 纬度 */
private Double latitude;
}
1.4 新建service
/**
* 服务接口定义
* @author zhouzhaodong
*/
public interface RedisGeoService {
/**
* 把油站信息保存到 Redis 中
* @param serviceStations {@link ServiceStation}
* @return 成功保存的个数
* */
Long saveServiceStationToRedis(Collection serviceStations);
/**
* 获取给定油站的坐标
* @param serviceStations 给定油站 key
* @return {@link Point}s
* */
List getServiceStationPos(String[] serviceStations);
/**
* 获取两个油站之间的距离
* @param serviceStation1 第一个油站
* @param serviceStation2 第二个油站
* @param metric {@link Metric} 单位信息, 可以是 null
* @return {@link Distance}
* */
Distance getTwoServiceStationDistance(String serviceStation1, String serviceStation2, Metric metric);
/**
* 根据给定地理位置坐标获取指定范围内的地理位置集合
* @param within {@link Circle} 中心点和距离
* @param args {@link RedisGeoCommands.GeoRadiusCommandArgs} 限制返回的个数和排序方式, 可以是 null
* @return {@link RedisGeoCommands.GeoLocation}
* */
GeoResults> getPointRadius(
Circle within, RedisGeoCommands.GeoRadiusCommandArgs args);
/**
* 根据给定地理位置获取指定范围内的地理位置集合
* @param member 油站名称
* @param distance 距离范围
* @param args {@link RedisGeoCommands.GeoRadiusCommandArgs} 限制返回的个数和排序方式, 可以是 null
* @return
*/
GeoResults> getMemberRadius(
String member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args);
/**
* 获取某个地理位置的 geohash 值
* @param serviceStations 给定油站 key
* @return city geohashs
* */
List getServiceStationGeoHash(String[] serviceStations);
}
1.5 新建service实现类
/**
* 服务接口实现
* @author zhouzhaodong
*/
@Service
@Slf4j
public class RedisGeoServiceImpl implements RedisGeoService {
/**
* redis的key
*/
private final String GEO_KEY = "ah-cities";
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Long saveServiceStationToRedis(Collection serviceStation) {
log.info("start to save station info: {}.", serviceStation);
GeoOperations ops = redisTemplate.opsForGeo();
Set> locations = new HashSet<>();
// 将坐标转为坐标点
serviceStation.forEach(ci -> locations.add(new RedisGeoCommands.GeoLocation<>(
ci.getServiceStationName(), new Point(ci.getLongitude(), ci.getLatitude())
)));
log.info("done to save station info.");
return ops.add(GEO_KEY, locations);
}
@Override
public List getServiceStationPos(String[] serviceStations) {
GeoOperations ops = redisTemplate.opsForGeo();
// 根据油站名称获取油站的坐标
return ops.position(GEO_KEY, serviceStations);
}
@Override
public Distance getTwoServiceStationDistance(String serviceStations1, String serviceStations2, Metric metric) {
GeoOperations ops = redisTemplate.opsForGeo();
return metric == null ?
ops.distance(GEO_KEY, serviceStations1, serviceStations2) : ops.distance(GEO_KEY, serviceStations1, serviceStations2, metric);
}
@Override
public GeoResults> getPointRadius(Circle within, RedisGeoCommands.GeoRadiusCommandArgs args) {
GeoOperations ops = redisTemplate.opsForGeo();
return args == null ?
ops.radius(GEO_KEY, within) : ops.radius(GEO_KEY, within, args);
}
@Override
public GeoResults> getMemberRadius(String member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args) {
GeoOperations ops = redisTemplate.opsForGeo();
return args == null ?
ops.radius(GEO_KEY, member, distance) : ops.radius(GEO_KEY, member, distance, args);
}
@Override
public List getServiceStationGeoHash(String[] serviceStations) {
GeoOperations ops = redisTemplate.opsForGeo();
return ops.hash(GEO_KEY, serviceStations);
}
}
1.6 测试代码
@SpringBootTest
class RedisGeoApplicationTests {
@Autowired
private RedisGeoService geoService;
/**
* 测试 把油站信息保存到 Redis 中
* */
@Test
public void testSaveServiceStationToRedis() {
List serviceStations = new ArrayList<>();
serviceStations.add(new ServiceStation("金盾", 117.17, 31.52));
serviceStations.add(new ServiceStation("中石油", 117.02, 30.31));
serviceStations.add(new ServiceStation("中石化", 116.47, 33.57));
serviceStations.add(new ServiceStation("山东石化", 116.58, 33.38));
serviceStations.add(new ServiceStation("青岛石化", 115.48, 32.54));
serviceStations.add(new ServiceStation("壳牌", 117.21, 32.56));
serviceStations.add(new ServiceStation("中国化工", 118.18, 29.43));
System.out.println(geoService.saveServiceStationToRedis(serviceStations));
}
/**
* 测试 获取给定油站的坐标
* 如果传递的 city 在 Redis 中没有记录, 会返回什么呢 ? 例如, 这里传递的 xxx
* */
@Test
public void testGetServiceStationPos() {
System.out.println(geoService.getServiceStationPos(
Arrays.asList("中石油", "中石化", "xxx").toArray(new String[3])
));
}
/**
* 测试 获取两个油站之间的距离
* */
@Test
public void testGetTwoServiceStationDistance() {
System.out.println(geoService.getTwoServiceStationDistance("壳牌", "金盾", null).getValue());
System.out.println(geoService.getTwoServiceStationDistance("壳牌", "金盾", Metrics.KILOMETERS).getValue());
}
/**
* 测试 根据给定地理位置坐标获取指定范围内的地理位置集合
* */
@Test
public void testGetPointRadius() {
Point center = new Point(117.17, 31.52);
Distance radius = new Distance(140, Metrics.KILOMETERS);
Circle within = new Circle(center, radius);
System.out.println(geoService.getPointRadius(within, null));
// 获取前两个油站位置, 同时返回距离中心点的距离
RedisGeoCommands.GeoRadiusCommandArgs args =
RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending();
System.out.println(geoService.getPointRadius(within, args));
}
/**
* 测试 根据给定地理位置获取指定范围内的地理位置集合
* */
@Test
public void testGetMemberRadius() {
Distance radius = new Distance(200, Metrics.KILOMETERS);
System.out.println(geoService.getMemberRadius("金盾", radius, null));
// order by 距离 limit 2, 同时返回距离中心点的距离
RedisGeoCommands.GeoRadiusCommandArgs args =
RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending();
System.out.println(geoService.getMemberRadius("金盾", radius, args));
}
/**
* 测试 获取某个地理位置的 geohash 值
* */
@Test
public void testGetServiceStationGeoHash() {
System.out.println(geoService.getServiceStationGeoHash(
Arrays.asList("中石化", "中石油", "xxx").toArray(new String[3])
));
}
}
完毕!
源代码地址
https://github.com/zhouzhaodong/springboot/tree/master/redis_geo