前言
账号开篇之作,记录一下自己实现自己的项目的时候运用的一些功能
如果有错误的地方,欢迎评论区指正👏🏻
一、高德地图基本的一些API
这个在这里不列举了,前端会用到,而且开发者文档写得十分详细了,读者可以直接点链接去看看🔗
二、使用RedisGeo的原因
本项目设计的思路为:调用百度地图的API,实时获得用户当前的定位(longitude和latitude),然后作为参数调用后端提供的接口,返回当前位置周围指定范围大小(半径大小)内的所有目标对象。
三、RedisGeo的基本使用方法
注意:以下所有的代码里面都是使用StringRedisTemplate
考虑到操作地图相关的数据里面包含比较多的数据类型,为了提高开发的效率,统一字符串形式来存储
@Autowired
private StringRedisTemplate stringRedisTemplate;
- 添加位置成员,返回当前集合里面的成员数量(Long类型)
这里面的Point对应一个含经纬度数据的对象(需要说明的是在Redis里面key对应的是集合的名字,member对应的是集合里面的成员的名字,与成员一一对应的是其Score)public Long geoAdd(String key, double lon, double lat, String member){ return stringRedisTemplate.opsForGeo().add(key, new Point(lon, lat), member); } // 添加位置成员
大家可能会注意到里面的score的值是比较奇怪的值,其实是Redis使用特定算法把经纬度信息转换位一个64位的整数,以此保证地理位置的全球性和唯一性 - 提供key和member返回一个包含经纬度信息的Point对象
public List<Point> geoPos(String key, String... members){ return stringRedisTemplate.opsForGeo().position(key, members); } // 获得不限数量成员经纬度
这个方法可以传入多个member组成的集合members,返回的是一个经纬度对象列表
- 返回两个地点之间的距离(可以指定距离单位)
public Distance geoDist(String key, String member1, String member2, Metrics metric){ return stringRedisTemplate.opsForGeo().distance(key, member1, member2, metric); }
metric形如Metric.KILOMETERS、Metric.METERS
- 返回指定成员附近所有的成员对象,返回的类型为:
List<RedisGeoCommands.GeoLocation<String>>
public List<RedisGeoCommands.GeoLocation<String>> geoRadiusByMember(String key,double lon, double lat,Double minDistanceKm, Double maxDistanceKm, Metrics metric, RedisGeoCommands.GeoRadiusCommandArgs args){ return stringRedisTemplate.opsForGeo().radius(key, new Circle(new Point(lon,lat),new Distance(maxDistanceKm, metric)), args).getContent().stream() .filter(result->result.getDistance().getValue()>=minDistanceKm) .map(GeoResult::getContent) .collect(Collectors.toList()); }
这个方法实际调用的是
GeoResults<RedisGeoCommands.GeoLocation<M>> radius(K key, Circle within, RedisGeoCommands.GeoRadiusCommandArgs args);
这里面的Circle对象包含经纬度信息和圆周半径
-
当然还有一个很重要的参数args,这个参数可以对返回的结果进行各种各样的设置,解释一下这个参数的具体值的含义:
-
RedisGeoCommands.GeoRadiusCommandArgs .newGeoRadiusArgs() .includeCoordinates() // 包含坐标信息 .includeDistance() // 包含距离信息 .limit(9) // 限制返回数量 .sortAscending() // 按照距离排序
这里我设置了filter来实现一个同心圆的范围,在实际应用中可以排除过于接近的对象
四、实际应用例子
目标:调用百度地图获得当前位置经纬度信息,并且作为参数调用接口返回附近(用半径自行定义附近)的所有目标地点,从而实现为用户提供周围指定营业点的地图位置和导航路线
(营业点位置信息由后台管理系统添加,本人测试的时候采用爬虫爬取足量地点数据,以CSV文件导入Redis内)
public void syncLogisticsLocationData(MultipartFile file) {
List<Logistics> data = CSV2RedisUtils.parseCSVFile(file);
data.forEach(obj -> {
double longitude = obj.getLongitude().doubleValue();
double latitude = obj.getLatitude().doubleValue();
String logisticsName = obj.getLogisticsName();
geoAdd(LOGISTICS_KEY, longitude, latitude, logisticsName);
});
}
接口的完整代码:
// 展现周围所有地点信息
@RequestMapping("/showLogisticsAround")
public Result showLogisticsAround(double lon, double lat,Double minDistanceKm,Double maxDistanceKm){
// String member = "广东省广州市荔湾区大沙河桥";
// double radius = 9.0;
if (minDistanceKm == null || maxDistanceKm == null) {
throw new IllegalArgumentException("minDistanceKm and maxDistanceKm must not be null");
}
Object aroundMember = location2RedisService.geoRadiusByMember(LOGISTICS_KEY,
lon,
lat,
minDistanceKm,
maxDistanceKm,
Metrics.KILOMETERS,
RedisGeoCommands.GeoRadiusCommandArgs
.newGeoRadiusArgs()
.includeCoordinates()
.includeDistance()
.limit(12)
.sortAscending()
);
return Result.success(aroundMember);
}
前端页面效果: