JavaRedis通过中心经纬度与半径获取范围内的结果集(类似附近的人)
Posted 谢哥哥blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaRedis通过中心经纬度与半径获取范围内的结果集(类似附近的人)相关的知识,希望对你有一定的参考价值。
文章目录
需求
通过百度地图的覆盖物功能,用户在页面上画圈选定某个区域,前端传输中心点经纬度与半径给后端,后端需要返回位置在圈内的设备
解决方案
经过网上查阅资料,最终决定使用Redis + GeoHash来做,效率也很高
什么是Redis + GeoHash
redis 实现附近的人功能主要通过Geo模块的六个命令
关键字 | 解释 |
---|---|
GEOADD | 将给定的位置对象(纬度、经度、名字)添加到指定的key |
GEOPOS | 从key里面返回所有给定位置对象的位置(经度和纬度) |
GEODIST | 返回两个给定位置之间的距离 |
GEOHASH | 返回一个或多个位置对象的Geohash表示 |
GEORADIUS | 以给定的经纬度为中心,返回目标集合中与中心的距离不超过给定最大距离的所有位置对象 |
GEORADIUSBYMEMBER | 以给定的位置对象为中心,返回与其距离不超过给定最大距离的所有位置对象 |
以GEOADD 命令和GEORADIUS 命令简单举例:
GEOADD key longitude latitude member [longitude latitude member …]
其中,key为集合名称,member为该经纬度所对应的对象。
GEOADD 添加多个商户“火锅店”位置信息:
GEOADD hotel 119.98866180732716 30.27465803229662 火锅店
GEORADIUS 根据给定的经纬度为中心,获取目标集合中与中心的距离不超过给定最大距离(500米内)的所有位置对象,也就是“附近的人”。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count] [STORE key] [STORedisT key]
范围单位:m | km | ft | mi --> 米 | 千米 | 英尺 | 英里
关键字 | 解释 |
---|---|
WITHDIST | 在返回位置对象的同时,将位置对象与中心之间的距离也一并返回。距离的单位和用户给定的范围单位保持一致 |
WITHCOORD | 将位置对象的经度和维度也一并返回 |
WITHHASH | 以 52 位有符号整数的形式,返回位置对象经过原始 geohash 编码的有序集合分值。这个选项主要用于底层应用或者调试,实际中的作用并不大 |
ASC DESC | 从近到远返回位置对象元素 、从远到近返回位置对象元素 |
COUNT count | 选取前N个匹配位置对象元素。(不设置则返回所有元素) |
STORE key | 将返回结果的地理位置信息保存到指定key |
STORedisT key | 将返回结果离中心点的距离保存到指定key |
例如下边命令:获取当前位置周边500米内的所有饭店
GEORADIUS hotel 119.98866180732716 30.27465803229662 500 m WITHCOORD
Redis内部使用有序集合(zset)保存用户的位置信息,zset中每个元素都是一个带位置的对象,元素的score值为通过经、纬度计算出的52位geohash值
Java实现
建立一个任务,当项目启动时,把大量设备的经纬度信息存入redis中
InitEquLongLatTask.java
/**
* 项目启动初始化设备的经纬度到redis中(以圆形中心经纬度 获取在中心半径以内的设备数据)
*
* @author xiegege
* @date 2023/1/12/012 14:21
*/
@Service
public class InitEquLongLatTask implements ApplicationListener<ContextRefreshedEvent>
private final static Logger logger = LogManager.getLogger(InitEquLongLatTask.class);
public final static String REDIS_KEY = "equLongLat-task";
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private IbmsEqdBasicDao eqdBasicDao;
@Override
public void onApplicationEvent(ContextRefreshedEvent event)
ApplicationContext context = event.getApplicationContext().getParent();
if (context == null)
logger.info("++++++++ 开始:初始化设备的经纬度到redis中 ++++++++");
int size = 0;
try
// 删除该key的所有值
redisTemplate.delete(REDIS_KEY);
List<IbmsEqdBasic> eqdBasicList = eqdBasicDao.getAllEquLongLat();
eqdBasicList.forEach(item ->
// 将用户地理位置信息存入 Redis
RedisGeoCommands.GeoLocation geoLocation =
new RedisGeoCommands.GeoLocation(
item.getEqudatasysid(),
// Point(经度, 纬度)
new Point(Double.parseDouble(item.getGislong()), Double.parseDouble(item.getGislat()))
);
redisTemplate.opsForGeo().add(REDIS_KEY, geoLocation);
);
size = eqdBasicList.size();
catch (Exception e)
logger.error("++++++++ 失败:初始化设备的经纬度到redis中 ++++++++", e);
logger.info("++++++++ 结束:初始化设备的经纬度到redis中【共" + size + "台设备】 ++++++++");
Controller
@Controller
@RequestMapping(value = "$apiPath/hp/heavyProtect")
public class HeavyProtectController extends BaseController
@Autowired
private HeavyProtectService heavyProtectService;
/**
* 获取命中的设备经纬度
*
* @param gislong 圆心经度
* @param gislat 圆心纬度
* @param radius 半径/米
* @return 命中集合
*/
@RequestMapping("getHitEquList")
@ResponseBody
public Map<String, Object> getHitEquList(String gislong, String gislat, Integer radius)
if (StringUtils.isBlank(gislong))
return error("请传入经度");
if (StringUtils.isBlank(gislat))
return error("请传入纬度");
if (radius == null)
return error("请传入半径");
// (a.gislong >= -180 and a.gislong <= 180) and (a.gislat >= -85.05112878 and a.gislat <= 85.05112878)
double gislongD = Double.parseDouble(gislong);
double gislatD = Double.parseDouble(gislat);
logger.info("getHitEquList=====经度:=====纬度:", gislongD, gislatD);
if (!(gislongD >= -180 && gislongD <= 180))
return error("经度不合法");
if (!(gislatD >= -85.05112878 && gislatD <= 85.05112878))
return error("纬度不合法");
List<IbmsEqdBasic> hitEquList = heavyProtectService.getHitEquList(gislongD, gislatD, radius);
return success("获取数据成功", hitEquList);
service
@Service
@Transactional(readOnly = true)
public class HeavyProtectService
private static final Logger logger = LoggerFactory.getLogger(HeavyProtectService.class);
@Autowired
private RedisTemplate redisTemplate;
public List<IbmsEqdBasic> getHitEquList(Double gislong, Double gislat, Integer radius)
List<IbmsEqdBasic> hitEquList = new ArrayList<>();
// 初始化 Geo 命令参数对象
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
// 限制50台设备,包含距离,按由近到远排序
// args.limit(50).includeDistance().sortAscending();
args.includeDistance().sortAscending();
// 经纬度为圆心,范围前端传来的半径/米
Point point = new Point(gislong, gislat);
// 初始化距离对象,单位/米
Distance distance = new Distance(radius, RedisGeoCommands.DistanceUnit.METERS);
Circle circle = new Circle(point, distance);
// 获取附近的设备 GeoLocation 信息
GeoResults<RedisGeoCommands.GeoLocation> geoResults = redisTemplate.opsForGeo().radius(InitEquLongLatTask.REDIS_KEY, circle, args);
List<GeoResult<RedisGeoCommands.GeoLocation>> contentList = geoResults.getContent();
if (CollectionUtils.isNotEmpty(contentList))
// 封装数据
contentList.forEach(content ->
RedisGeoCommands.GeoLocation<String> geoLocation = content.getContent();
IbmsEqdBasic eqdBasic = new IbmsEqdBasic();
eqdBasic.setEqudatasysid(geoLocation.getName());
// 获取距离
// double dist = content.getDistance().getValue();
// 四舍五入精确到小数点后 1 位,方便客户端显示
// String distanceStr = NumberUtil.round(dist, 1).toString() + "m";
// eqdBasic.setDistance(distanceStr);
hitEquList.add(eqdBasic);
);
logger.info("getHitEquList=====命中设备台", hitEquList.size());
// 设备id结果集,可根据结果集封装更详细的信息
return hitEquList;
xml sql语句
<select id="getAllEquLongLat" resultType="IbmsEqdBasic">
select
equdatasysid,
gislong,
gislat
from ibms_brm_eqm_eqd_basic a
where a.is_del='0'
and a.gislong is not null
and a.gislat is not null
<![CDATA[
and (a.gislong >= -180 and a.gislong <= 180)
and (a.gislat >= -85.05112878 and a.gislat <= 85.05112878)
]]>
<!-- -180 <= a.gislong <= 180-->
<!-- -85.05112878 <= a.gislat <= 85.05112878-->
</select>
引用的pom依赖
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
以上是关于JavaRedis通过中心经纬度与半径获取范围内的结果集(类似附近的人)的主要内容,如果未能解决你的问题,请参考以下文章