Redis GEO地理位置信息的应用

Redis GEO

概述

Redis的GEO操作是一种基于地理位置信息进行操作的功能。它使用经度和纬度坐标来表示地理位置,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。

应用场景

地理围栏:通过设置地理位置的经纬度信息,可以将用户或者车辆等实体绑定在地理围栏内,当实体进出围栏时,可以触发相应的事件。
附近的人:在类似于约会、社交、旅游等场景下,可以通过Redis GEO快速查询周围的人员或景点。
配送服务:通过获取配送地址的经纬度信息,可以找到距离最近的配送员或仓库,并对订单进行分配。
地址查找:在地图应用中,可以通过Redis GEO快速查询某个地址周围的商家或服务设施。
动态信息:在滴滴、Uber等打车应用中,可以实时更新车辆的位置信息,以提供更加准确的车辆推荐和路线规划服务
地理围栏:通过设置地理位置的经纬度信息,可以将用户或者车辆等实体绑定在地理围栏内,当实体进出围栏时,可以触发相应的事件。




附近的人:在类似于约会、社交、旅游等场景下,可以通过Redis GEO快速查询周围的人员或景点。


配送服务:通过获取配送地址的经纬度信息,可以找到距离最近的配送员或仓库,并对订单进行分配。



地址查找:在地图应用中,可以通过Redis GEO快速查询某个地址周围的商家或服务设施。


动态信息:在滴滴、Uber等打车应用中,可以实时更新车辆的位置信息,以提供更加准确的车辆推荐和路线规划服务
地理围栏:通过设置地理位置的经纬度信息,可以将用户或者车辆等实体绑定在地理围栏内,当实体进出围栏时,可以触发相应的事件。 附近的人:在类似于约会、社交、旅游等场景下,可以通过Redis GEO快速查询周围的人员或景点。 配送服务:通过获取配送地址的经纬度信息,可以找到距离最近的配送员或仓库,并对订单进行分配。 地址查找:在地图应用中,可以通过Redis GEO快速查询某个地址周围的商家或服务设施。 动态信息:在滴滴、Uber等打车应用中,可以实时更新车辆的位置信息,以提供更加准确的车辆推荐和路线规划服务

Redis GEO命令

1.GEOADD添加位置信息

将一个或多个指定的地理位置(经度、纬度、名称)添加到指定的键中。

GEOADD key longitude latitude member [longitude latitude member ...]
GEOADD key longitude latitude member [longitude latitude member ...]
GEOADD key longitude latitude member [longitude latitude member ...]

添加一个名为cities的键,并将北京、上海、广州三个城市的经纬度和名称添加到该键中

GEOADD cities 116.4074 39.9042 Beijing 121.4737 31.2304 Shanghai 113.2644 23.1291 Guangzhou
GEOADD cities  116.4074 39.9042 Beijing 121.4737 31.2304 Shanghai 113.2644 23.1291 Guangzhou
GEOADD cities 116.4074 39.9042 Beijing 121.4737 31.2304 Shanghai 113.2644 23.1291 Guangzhou

GEOADD命令对于经纬度是有要求的:

有效的经度从-180度到180
有效的纬度从-85.05112878度到85.05112878
有效的经度从-180度到180度




有效的纬度从-85.05112878度到85.05112878度
有效的经度从-180度到180度 有效的纬度从-85.05112878度到85.05112878度

2.GEODIST查询距离

返回两个位置之间的距离。可以选择以米或千米为单位。

GEODIST key member1 member2 [unit]
GEODIST key member1 member2 [unit]
GEODIST key member1 member2 [unit]

可选参数unit用于指定计算距离时的单位:

m 表示单位为米
km 表示单位为千米
mi 表示单位为英里
ft 表示单位为英尺
m 表示单位为米




km 表示单位为千米


mi 表示单位为英里



ft 表示单位为英尺
m 表示单位为米 km 表示单位为千米 mi 表示单位为英里 ft 表示单位为英尺

查询北京和上海之间的距离,单位为千米

GEODIST cities Beijing Shanghai km
GEODIST cities Beijing Shanghai km
GEODIST cities Beijing Shanghai km

3.GEOHASH获取指定位置的Geohash值

返回一个或多个位置的Geohash值,该值用于对地理位置进行更快速的范围查找。

GEOHASH key member [member ...]
GEOHASH key member [member ...]
GEOHASH key member [member ...]

获取北京的Geohash值

GEOHASH cities Beijing
GEOHASH cities Beijing
GEOHASH cities Beijing

4.GEOPOS查询地理位置坐标

返回一个或多个位置的经度和纬度。

GEOPOS key member [member ...]
GEOPOS key member [member ...]
GEOPOS key member [member ...]

查询广州的经纬度

GEOPOS cities Guangzhou
GEOPOS cities Guangzhou
GEOPOS cities Guangzhou

5.GEORADIUS查找指定范围内的元素

查询给定坐标范围内的所有元素。可以通过设置排序选项来获取按距离排序的结果。

GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

查找距离北京1000公里以内的城市,并按距离升序排列

GEORADIUS cities 116.4074 39.9042 1000 km ASC
GEORADIUS cities 116.4074 39.9042 1000 km ASC
GEORADIUS cities 116.4074 39.9042 1000 km ASC

6.GEORADIUSBYMEMBER查询给定成员周围的所有元素

查询给定成员周围的所有元素。可以通过设置排序选项来获取按距离排序的结果。

GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

查找距离上海最近的城市,并返回它们的名称和距离

GEORADIUSBYMEMBER cities Shanghai 100 km WITHDIST
GEORADIUSBYMEMBER cities Shanghai 100 km WITHDIST
GEORADIUSBYMEMBER cities Shanghai 100 km WITHDIST

7.ZREM删除成员

从指定键中删除一个或多个成员。

ZREM key member [member ...]
ZREM key member [member ...]
ZREM key member [member ...]

从cities键中删除广州

ZREM cities Guangzhou
ZREM cities Guangzhou
ZREM cities Guangzhou

GEO命令演示

# 添加位置信息
本机:0>geoadd user:location 121.48941 31.40527 'shagnhai'
"1"
# 添加多个位置信息
本机:0>geoadd user:location 121.47941 31.41527 'shanghai1' 121.47941 31.43527 'shagnhai2' 121.47941 31.40527 'shagnhai3'
"3"
# 计算距离,单位有m km ft(英尺) mi(英里)
# 计算两点间的距离,单位m
本机:0>geodist user:location shanghai shanghai1 m
"1462.1834"
# 千米:km
本机:0>geodist user:location shanghai shanghai1 km
"1.4622"
# geohash 返回一个或多个位置元素的geohash,保存Redis中是用geohash位置52点整数编码
# geohash 将二维经纬度转换成字符串,每个字符串代表一个矩形区域,该矩形区域内的经纬度点都共享一个相同的geohash字符串。
本机:0>geohash user:location shanghai shanghai1
1) "wtw6st1uuq0"
2) "wtw6sqfx5q0"
# geopos 从key里返回指定成员的位置信息
本机:0>geopos user:location shanghai shanghai1
1) 1) "121.48941010236740112"
2) "31.40526993848380499"
2) 1) "121.47941082715988159"
2) "31.41526941345740198"
# georadius:给定经纬度为中心,返回键包含的位置元素中,与中心的距离不超过给定最大距离的所有位置元素
# 范围单位:m km mi ft
# withcoord:将位置元素的经纬度一并返回
本机:0>georadius user:location 121.48941 31.40527 3000 m withcoord
1) 1) "shagnhai3"
2) 1) "121.47941082715988159"
2) "31.40526993848380499"
2) 1) "shanghai1"
2) 1) "121.47941082715988159"
2) "31.41526941345740198"
3) 1) "shanghai"
2) 1) "121.48941010236740112"
2) "31.40526993848380499"
# withdist:返回位置元素的同时,将位置元素与中心点间的距离一并返回
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist
1) 1) "shagnhai3"
2) "949.2411"
2) 1) "shanghai1"
2) "1462.1719"
3) 1) "shanghai"
2) "0.0119"
# asc:根据中心位置,按照从近到远的方式返回位置元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist asc
1) 1) "shanghai"
2) "0.0119"
2) 1) "shagnhai3"
2) "949.2411"
3) 1) "shanghai1"
2) "1462.1719"
# desc: 根据中心位置,按照从远到近的方式返回位置元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist desc
1) 1) "shanghai1"
2) "1462.1719"
2) 1) "shagnhai3"
2) "949.2411"
3) 1) "shanghai"
2) "0.0119"
# count:获取指定数量的元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist desc count 2
1) 1) "shanghai1"
2) "1462.1719"
2) 1) "shagnhai3"
2) "949.2411"
# georadiusbymember:和georadius命令类似,都可以找出指定位置范围内的元素,但是georadiusbymember的中心点是由给定位置元素决定的,而不像georadius使用经纬度决定中心点
本机:0>georadiusbymember user:location shanghai 3 km
1) "shagnhai3"
2) "shanghai1"
3) "shanghai"
# 添加位置信息
本机:0>geoadd user:location 121.48941  31.40527 'shagnhai'
"1"
# 添加多个位置信息
本机:0>geoadd user:location 121.47941 31.41527 'shanghai1'  121.47941 31.43527 'shagnhai2'  121.47941 31.40527 'shagnhai3'
"3"




# 计算距离,单位有m km ft(英尺) mi(英里)
# 计算两点间的距离,单位m
本机:0>geodist user:location shanghai shanghai1 m
"1462.1834"
# 千米:km
本机:0>geodist user:location shanghai shanghai1 km
"1.4622"





# geohash 返回一个或多个位置元素的geohash,保存Redis中是用geohash位置52点整数编码
# geohash 将二维经纬度转换成字符串,每个字符串代表一个矩形区域,该矩形区域内的经纬度点都共享一个相同的geohash字符串。
本机:0>geohash user:location shanghai shanghai1
1) "wtw6st1uuq0"
2) "wtw6sqfx5q0"




# geopos 从key里返回指定成员的位置信息
本机:0>geopos user:location shanghai shanghai1
1) 1) "121.48941010236740112"
   2) "31.40526993848380499"

2) 1) "121.47941082715988159"
   2) "31.41526941345740198"



# georadius:给定经纬度为中心,返回键包含的位置元素中,与中心的距离不超过给定最大距离的所有位置元素
# 范围单位:m km mi ft
# withcoord:将位置元素的经纬度一并返回
本机:0>georadius user:location 121.48941 31.40527 3000 m withcoord
1) 1) "shagnhai3"
   2) 1) "121.47941082715988159"
      2) "31.40526993848380499"



2) 1) "shanghai1"
   2) 1) "121.47941082715988159"
      2) "31.41526941345740198"


3) 1) "shanghai"
   2) 1) "121.48941010236740112"
      2) "31.40526993848380499"

# withdist:返回位置元素的同时,将位置元素与中心点间的距离一并返回
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist
1) 1) "shagnhai3"
   2) "949.2411"

2) 1) "shanghai1"
   2) "1462.1719"

3) 1) "shanghai"
   2) "0.0119"

# asc:根据中心位置,按照从近到远的方式返回位置元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist asc
1) 1) "shanghai"
   2) "0.0119"

2) 1) "shagnhai3"
   2) "949.2411"

3) 1) "shanghai1"
   2) "1462.1719"


# desc: 根据中心位置,按照从远到近的方式返回位置元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist desc
1) 1) "shanghai1"
   2) "1462.1719"

2) 1) "shagnhai3"
   2) "949.2411"


3) 1) "shanghai"
   2) "0.0119"


# count:获取指定数量的元素
本机:0>georadius user:location 121.48941 31.40527 3000 m withdist desc count 2
1) 1) "shanghai1"
   2) "1462.1719"

2) 1) "shagnhai3"
   2) "949.2411"



# georadiusbymember:和georadius命令类似,都可以找出指定位置范围内的元素,但是georadiusbymember的中心点是由给定位置元素决定的,而不像georadius使用经纬度决定中心点
本机:0>georadiusbymember user:location shanghai 3 km 
1) "shagnhai3"
2) "shanghai1"
3) "shanghai"
# 添加位置信息 本机:0>geoadd user:location 121.48941 31.40527 'shagnhai' "1" # 添加多个位置信息 本机:0>geoadd user:location 121.47941 31.41527 'shanghai1' 121.47941 31.43527 'shagnhai2' 121.47941 31.40527 'shagnhai3' "3" # 计算距离,单位有m km ft(英尺) mi(英里) # 计算两点间的距离,单位m 本机:0>geodist user:location shanghai shanghai1 m "1462.1834" # 千米:km 本机:0>geodist user:location shanghai shanghai1 km "1.4622" # geohash 返回一个或多个位置元素的geohash,保存Redis中是用geohash位置52点整数编码 # geohash 将二维经纬度转换成字符串,每个字符串代表一个矩形区域,该矩形区域内的经纬度点都共享一个相同的geohash字符串。 本机:0>geohash user:location shanghai shanghai1 1) "wtw6st1uuq0" 2) "wtw6sqfx5q0" # geopos 从key里返回指定成员的位置信息 本机:0>geopos user:location shanghai shanghai1 1) 1) "121.48941010236740112" 2) "31.40526993848380499" 2) 1) "121.47941082715988159" 2) "31.41526941345740198" # georadius:给定经纬度为中心,返回键包含的位置元素中,与中心的距离不超过给定最大距离的所有位置元素 # 范围单位:m km mi ft # withcoord:将位置元素的经纬度一并返回 本机:0>georadius user:location 121.48941 31.40527 3000 m withcoord 1) 1) "shagnhai3" 2) 1) "121.47941082715988159" 2) "31.40526993848380499" 2) 1) "shanghai1" 2) 1) "121.47941082715988159" 2) "31.41526941345740198" 3) 1) "shanghai" 2) 1) "121.48941010236740112" 2) "31.40526993848380499" # withdist:返回位置元素的同时,将位置元素与中心点间的距离一并返回 本机:0>georadius user:location 121.48941 31.40527 3000 m withdist 1) 1) "shagnhai3" 2) "949.2411" 2) 1) "shanghai1" 2) "1462.1719" 3) 1) "shanghai" 2) "0.0119" # asc:根据中心位置,按照从近到远的方式返回位置元素 本机:0>georadius user:location 121.48941 31.40527 3000 m withdist asc 1) 1) "shanghai" 2) "0.0119" 2) 1) "shagnhai3" 2) "949.2411" 3) 1) "shanghai1" 2) "1462.1719" # desc: 根据中心位置,按照从远到近的方式返回位置元素 本机:0>georadius user:location 121.48941 31.40527 3000 m withdist desc 1) 1) "shanghai1" 2) "1462.1719" 2) 1) "shagnhai3" 2) "949.2411" 3) 1) "shanghai" 2) "0.0119" # count:获取指定数量的元素 本机:0>georadius user:location 121.48941 31.40527 3000 m withdist desc count 2 1) 1) "shanghai1" 2) "1462.1719" 2) 1) "shagnhai3" 2) "949.2411" # georadiusbymember:和georadius命令类似,都可以找出指定位置范围内的元素,但是georadiusbymember的中心点是由给定位置元素决定的,而不像georadius使用经纬度决定中心点 本机:0>georadiusbymember user:location shanghai 3 km 1) "shagnhai3" 2) "shanghai1" 3) "shanghai"

Redis GEO实现附近人的功能

基础类

创建NearMeUserVO视图对象,封装响应视图的基本数据信息

@Data
public class NearMeUserVO {
public Integer id;
/**
* 距离
*/
public String distance;
}
@Data
public class NearMeUserVO {


    public Integer id;
   /**
  * 距离
  */
   public String distance;
}
@Data public class NearMeUserVO { public Integer id; /** * 距离 */ public String distance; }

创建User对象模拟操作用户

@Data
public class User {
private int id;
}
    @Data
    public class User {
        private  int id;
    }
@Data public class User { private int id; }

API接口

创建添加、更新用户位置信息查询附近用户信息的2个API接口

@RestController
public class NearMeUserController {
@Autowired
private INearMeUserService nearMeUserService;
/**
* 添加、更新坐标
*
* @param lon
* @param lat
* @return
*/
@PostMapping("/updateUserLocation")
public String updateUserLocation(@RequestParam Float lon, @RequestParam Float lat) {
nearMeUserService.updateUserLocation(lon, lat);
return "OK";
}
/**
* 获取附近的人
* 查询距离指定经纬度一定范围内的用户,并返回结果列表
*
* @param radius
* @param lon
* @param lat
* @return
*/
@GetMapping("/getNearMe")
public Object nearMe(Integer radius, Float lon, Float lat) {
List<NearMeUserVO> nearMe = nearMeUserService.findNearMe(radius, lon, lat);
return nearMe;
}
}
@RestController
public class NearMeUserController {
    @Autowired
    private INearMeUserService nearMeUserService;

    /**
     * 添加、更新坐标
     *
     * @param lon
     * @param lat
     * @return
     */
    @PostMapping("/updateUserLocation")
    public String updateUserLocation(@RequestParam Float lon, @RequestParam Float lat) {
        nearMeUserService.updateUserLocation(lon, lat);
        return "OK";
    }


    /**
     * 获取附近的人
     * 查询距离指定经纬度一定范围内的用户,并返回结果列表
     *
     * @param radius
     * @param lon
     * @param lat
     * @return
     */
    @GetMapping("/getNearMe")
    public Object nearMe(Integer radius, Float lon, Float lat) {
        List<NearMeUserVO> nearMe = nearMeUserService.findNearMe(radius, lon, lat);
        return nearMe;
    }
}
@RestController public class NearMeUserController { @Autowired private INearMeUserService nearMeUserService; /** * 添加、更新坐标 * * @param lon * @param lat * @return */ @PostMapping("/updateUserLocation") public String updateUserLocation(@RequestParam Float lon, @RequestParam Float lat) { nearMeUserService.updateUserLocation(lon, lat); return "OK"; } /** * 获取附近的人 * 查询距离指定经纬度一定范围内的用户,并返回结果列表 * * @param radius * @param lon * @param lat * @return */ @GetMapping("/getNearMe") public Object nearMe(Integer radius, Float lon, Float lat) { List<NearMeUserVO> nearMe = nearMeUserService.findNearMe(radius, lon, lat); return nearMe; } }

接口实现

public interface INearMeUserService {
/**
* 更新坐标
*
* @param lon 经度
* @param lat 纬度
*/
void updateUserLocation(Float lon, Float lat);
/**
* 获取附近的人
*
* @param radius 半径,默认 1000m
* @param lon 经度
* @param lat 纬度
* @return
*/
List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat);
}
public interface INearMeUserService {




    /**
     * 更新坐标
     *
     * @param lon 经度
     * @param lat 纬度
     */
    void updateUserLocation(Float lon, Float lat);



    /**
     * 获取附近的人
     *
     * @param radius 半径,默认 1000m
     * @param lon    经度
     * @param lat    纬度
     * @return
     */
    List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat);
}
public interface INearMeUserService { /** * 更新坐标 * * @param lon 经度 * @param lat 纬度 */ void updateUserLocation(Float lon, Float lat); /** * 获取附近的人 * * @param radius 半径,默认 1000m * @param lon 经度 * @param lat 纬度 * @return */ List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat); }
@Service
public class NearMeUserServiceImpl implements INearMeUserService {
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
/**
* 用户定位信息cKEY
*/
private static final String USER_LOCATION_KEY = "user:location";
public void updateUserLocation(Float lon, Float lat) {
if (lon == null || lat == null) {
throw new RuntimeException("获取经度、维度失败");
}
// 获取登录用户信息
User user = this.getLoginUser();
// 定义key
String key = "user:location";
// 将用户地理位置信息存入 Redis
RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands.GeoLocation(user.getId(), new Point(lon, lat));
redisTemplate.opsForGeo().add(key, geoLocation);
}
/**
* 获取附近的人
*
* @param radius 半径,默认 1000m
* @param lon 经度
* @param lat 纬度
* @return
*/
public List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat) {
// 获取登录用户信息
User user = this.getLoginUser();
int userId = user.getId();
// 处理半径,默认 1000m
if (radius == null) {
radius = 1000;
}
// 获取用户经纬度
Point point = null;
if (lon == null || lat == null) {
// 如果经纬度没传,那么从 Redis 中获取
List<Point> points = redisTemplate.opsForGeo().position(USER_LOCATION_KEY, userId);
if (points == null || points.isEmpty()) {
throw new RuntimeException("获取经纬度失败");
}
point = points.get(0);
} else {
point = new Point(lon, lat);
}
// 初始化距离对象,单位 m
Distance distance = new Distance(radius, RedisGeoCommands.DistanceUnit.METERS);
// 初始化 Geo 命令参数对象
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
// 附近的人限制3,包含距离,按由近到远排序
args.limit(3).includeDistance().sortAscending();
// 以用户经纬度为圆心,范围 1000m
Circle circle = new Circle(point, distance);
// 获取附近的人 GeoLocation 信息
GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResult = redisTemplate.opsForGeo().radius(USER_LOCATION_KEY, circle, args);
// 构建有序 Map
Map<Integer, NearMeUserVO> nearMeUserVOMap = Maps.newLinkedHashMap();
// 完善用户信息
geoResult.forEach(result -> {
RedisGeoCommands.GeoLocation<Object> geoLocation = result.getContent();
// 初始化Vo对象
NearMeUserVO nearMeUserVO = new NearMeUserVO();
nearMeUserVO.setId((Integer) geoLocation.getName());
// 获取距离
Double dist = result.getDistance().getValue();
// 四舍五入精确到小数点后 1 位,方便客户端显示
String distanceStr = NumberUtil.round(dist, 1).toString() + "m";
nearMeUserVO.setDistance(distanceStr);
nearMeUserVOMap.put((Integer) geoLocation.getName(), nearMeUserVO);
});
// 附近的人,可进一步完善用户信息
// Integer[] userIds = nearMeUserVOMap.keySet().toArray(new Integer[0]);
ArrayList<NearMeUserVO> nearMeUserVO = Lists.newArrayList(nearMeUserVOMap.values());
return nearMeUserVO;
}
/**
* 模拟获取真实登录用户信息
*
* @return
*/
public User getLoginUser() {
User user = new User();
user.setId(1);
return user;
}
}
@Service
public class NearMeUserServiceImpl implements INearMeUserService {


    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;



    /**
     * 用户定位信息cKEY
     */
    private static final String USER_LOCATION_KEY = "user:location";



    public void updateUserLocation(Float lon, Float lat) {
        if (lon == null || lat == null) {
            throw new RuntimeException("获取经度、维度失败");
        }


        // 获取登录用户信息
        User user = this.getLoginUser();
        // 定义key
        String key = "user:location";

        // 将用户地理位置信息存入 Redis
        RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands.GeoLocation(user.getId(), new Point(lon, lat));
        redisTemplate.opsForGeo().add(key, geoLocation);
    }


    /**
     * 获取附近的人
     *
     * @param radius 半径,默认 1000m
     * @param lon    经度
     * @param lat    纬度
     * @return
     */
    public List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat) {
        // 获取登录用户信息
        User user = this.getLoginUser();
        int userId = user.getId();

        // 处理半径,默认 1000m
        if (radius == null) {
            radius = 1000;
        }


        // 获取用户经纬度
        Point point = null;
        if (lon == null || lat == null) {
            // 如果经纬度没传,那么从 Redis 中获取
            List<Point> points = redisTemplate.opsForGeo().position(USER_LOCATION_KEY, userId);
            if (points == null || points.isEmpty()) {
                throw new RuntimeException("获取经纬度失败");
            }
            point = points.get(0);
        } else {
            point = new Point(lon, lat);
        }

        // 初始化距离对象,单位 m
        Distance distance = new Distance(radius, RedisGeoCommands.DistanceUnit.METERS);
        // 初始化 Geo 命令参数对象
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        // 附近的人限制3,包含距离,按由近到远排序
        args.limit(3).includeDistance().sortAscending();
        // 以用户经纬度为圆心,范围 1000m
        Circle circle = new Circle(point, distance);
        // 获取附近的人 GeoLocation 信息
        GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResult = redisTemplate.opsForGeo().radius(USER_LOCATION_KEY, circle, args);

        // 构建有序 Map
        Map<Integer, NearMeUserVO> nearMeUserVOMap = Maps.newLinkedHashMap();
        // 完善用户信息
        geoResult.forEach(result -> {
            RedisGeoCommands.GeoLocation<Object> geoLocation = result.getContent();
            // 初始化Vo对象
            NearMeUserVO nearMeUserVO = new NearMeUserVO();
            nearMeUserVO.setId((Integer) geoLocation.getName());
            // 获取距离
            Double dist = result.getDistance().getValue();
            // 四舍五入精确到小数点后 1 位,方便客户端显示
            String distanceStr = NumberUtil.round(dist, 1).toString() + "m";
            nearMeUserVO.setDistance(distanceStr);
            nearMeUserVOMap.put((Integer) geoLocation.getName(), nearMeUserVO);
        });



        // 附近的人,可进一步完善用户信息
//        Integer[] userIds = nearMeUserVOMap.keySet().toArray(new Integer[0]);
        
        ArrayList<NearMeUserVO> nearMeUserVO = Lists.newArrayList(nearMeUserVOMap.values());

        return nearMeUserVO;
    }

    /**
     * 模拟获取真实登录用户信息
     *
     * @return
     */
    public User getLoginUser() {
        User user = new User();
        user.setId(1);
        return user;
    }
}
@Service public class NearMeUserServiceImpl implements INearMeUserService { @Autowired private RedisTemplate<Object, Object> redisTemplate; /** * 用户定位信息cKEY */ private static final String USER_LOCATION_KEY = "user:location"; public void updateUserLocation(Float lon, Float lat) { if (lon == null || lat == null) { throw new RuntimeException("获取经度、维度失败"); } // 获取登录用户信息 User user = this.getLoginUser(); // 定义key String key = "user:location"; // 将用户地理位置信息存入 Redis RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands.GeoLocation(user.getId(), new Point(lon, lat)); redisTemplate.opsForGeo().add(key, geoLocation); } /** * 获取附近的人 * * @param radius 半径,默认 1000m * @param lon 经度 * @param lat 纬度 * @return */ public List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat) { // 获取登录用户信息 User user = this.getLoginUser(); int userId = user.getId(); // 处理半径,默认 1000m if (radius == null) { radius = 1000; } // 获取用户经纬度 Point point = null; if (lon == null || lat == null) { // 如果经纬度没传,那么从 Redis 中获取 List<Point> points = redisTemplate.opsForGeo().position(USER_LOCATION_KEY, userId); if (points == null || points.isEmpty()) { throw new RuntimeException("获取经纬度失败"); } point = points.get(0); } else { point = new Point(lon, lat); } // 初始化距离对象,单位 m Distance distance = new Distance(radius, RedisGeoCommands.DistanceUnit.METERS); // 初始化 Geo 命令参数对象 RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs(); // 附近的人限制3,包含距离,按由近到远排序 args.limit(3).includeDistance().sortAscending(); // 以用户经纬度为圆心,范围 1000m Circle circle = new Circle(point, distance); // 获取附近的人 GeoLocation 信息 GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResult = redisTemplate.opsForGeo().radius(USER_LOCATION_KEY, circle, args); // 构建有序 Map Map<Integer, NearMeUserVO> nearMeUserVOMap = Maps.newLinkedHashMap(); // 完善用户信息 geoResult.forEach(result -> { RedisGeoCommands.GeoLocation<Object> geoLocation = result.getContent(); // 初始化Vo对象 NearMeUserVO nearMeUserVO = new NearMeUserVO(); nearMeUserVO.setId((Integer) geoLocation.getName()); // 获取距离 Double dist = result.getDistance().getValue(); // 四舍五入精确到小数点后 1 位,方便客户端显示 String distanceStr = NumberUtil.round(dist, 1).toString() + "m"; nearMeUserVO.setDistance(distanceStr); nearMeUserVOMap.put((Integer) geoLocation.getName(), nearMeUserVO); }); // 附近的人,可进一步完善用户信息 // Integer[] userIds = nearMeUserVOMap.keySet().toArray(new Integer[0]); ArrayList<NearMeUserVO> nearMeUserVO = Lists.newArrayList(nearMeUserVOMap.values()); return nearMeUserVO; } /** * 模拟获取真实登录用户信息 * * @return */ public User getLoginUser() { User user = new User(); user.setId(1); return user; } }

执行测试

访问执行添加用户地理位置数据接口,得到如下所示数据:

在这里插入图片描述
访问执行获取附近人接口,得到如下所示数据:

"data": [
{
"id": 1,
"distance": "0.0m"
},
{
"id": 3,
"distance": "949.3m"
},
{
"id": 2,
"distance": "1462.2m"
}
]
  "data": [
    {
      "id": 1,
      "distance": "0.0m"
    },
    {
      "id": 3,
      "distance": "949.3m"
    },
    {
      "id": 2,
      "distance": "1462.2m"
    }
  ]
"data": [ { "id": 1, "distance": "0.0m" }, { "id": 3, "distance": "949.3m" }, { "id": 2, "distance": "1462.2m" } ]

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYCQ7Uqa' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片