Redis GEO地理位置信息的应用

Redis GEO

概述

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

应用场景

地理围栏:通过设置地理位置的经纬度信息,可以将用户或者车辆等实体绑定在地理围栏内,当实体进出围栏时,可以触发相应的事件。

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

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

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

动态信息:在滴滴、Uber等打车应用中,可以实时更新车辆的位置信息,以提供更加准确的车辆推荐和路线规划服务

Redis GEO命令

1.GEOADD添加位置信息

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

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命令对于经纬度是有要求的:

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

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

2.GEODIST查询距离

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

GEODIST key member1 member2 [unit]

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

m 表示单位为米

km 表示单位为千米

mi 表示单位为英里

ft 表示单位为英尺

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

扫描二维码关注公众号,回复: 15475712 查看本文章
GEODIST cities Beijing Shanghai km

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

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

GEOHASH key member [member ...]

获取北京的Geohash值

GEOHASH cities Beijing

4.GEOPOS查询地理位置坐标

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

GEOPOS key member [member ...]

查询广州的经纬度

GEOPOS cities Guangzhou

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

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

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

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

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

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

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

GEORADIUSBYMEMBER cities Shanghai 100 km WITHDIST

7.ZREM删除成员

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

ZREM key member [member ...]

从cities键中删除广州

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"

Redis GEO实现附近人的功能

基础类

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

@Data
public class NearMeUserVO {
    
    

    public Integer id;
   /**
	* 距离
	*/
   public String distance;
}

创建User对象模拟操作用户

    @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;
    }
}

接口实现

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;
    }
}

执行测试

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

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

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

猜你喜欢

转载自blog.csdn.net/qq_38628046/article/details/130794658