redis实现坐标附近查询

源码:https://gitee.com/Jakewabc/small-study-case

根据经纬度搜索附近店铺,主要实现技术有redisMongoDBelasticsearch技术。这里就讲解redis如何实现附近坐标搜索。

一、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

如果使用springboot,那么这个依赖就不用指定版本号了。

二、位置信息操作

引入redis操作模板

@Autowired
private RedisTemplate redisTemplate;

2.1、向redis添加单个位置

  • 使用的方法
redisTemplate.boundGeoOps(K key)
                .add(Point var1, M var2);

key:集合名称

var1:位置信息

var2:位置名称

  • 范例
// x:经度,Y:维度
Point point = new Point(106.63658, 26.653324);
// outlets 集合名称
redisTemplate.boundGeoOps("outlets")
    .add(point, "这个位置的名称");

2.2、批量添加位置信息

  • 使用的方法
redisTemplate.boundGeoOps(K key)
               .add(Map<M, Point> var1);

key:集合名称

var1:是一个Map集合,这个Map集合的key就是位置名称,value保存 Point对象,这个对象就是保存经纬度信息的。

  • 范例:
// 创建Map集合保存多个位置
Map<String,Point> map = new HashMap<>();
map.put("位置名称",new Point(106.777434,26.925769));
map.put("位置1",new Point(106.944735,26.888657));
map.put("位置2",new Point(106.623357,26.781373));
// 批量添加位置信息
redisTemplate.boundGeoOps("outlets")
    .add(map);

2.3、根据位置名称查询坐标信息

  • 使用的方法
redisTemplate.boundGeoOps(K key)
               .position(M... var1);

key:集合名称

M:位置信息,可以传输多个。就是保存位置时保存的位置名称,保存位置的名称是什么类型,这里就是什么类型。

  • 范例:
List<Point> position = redisTemplate.boundGeoOps("outlets").position("这个位置的名称");
System.out.println(position);
//输出: [Point [x=106.636581, y=26.653324]]

List<Point> list = redisTemplate.boundGeoOps("outlets").position("位置名称", "位置1", "位置2");
System.out.println(list);
// 输入:[Point [x=106.777435, y=26.925769], Point [x=106.944735, y=26.888658], Point [x=106.623358, y=26.781373]]

2.4、计算两点间距离

  • 使用方法
redisTemplate.boundGeoOps(K key)
               .distance(M var1, M var2, Metric var3);

key:集合名称

M:位置信息,可以传输多个。就是保存位置时保存的位置名称,保存位置的名称是什么类型,这里就是什么类型。

var3:返回距离的单位,默认为米。可以使用 org.springframework.data.geo.Metrics枚举类。 Metrics.KILOMETERS:千米

注意:distance(M var1, M var2, Metric var3);如果只传var1参数,不传 var3参数,默认单位为米

  • 范例:
// 直接这样计算,默认单位为 米M
Distance distance = redisTemplate.boundGeoOps("outlets").distance("位置1", "位置2");
// 计算两两点间的距离,并单位设置为千米。最后这个参数就是设置单位的。
Distance distance = redisTemplate.boundGeoOps("outlets").distance("位置1", "位置2", Metrics.KILOMETERS);
// 距离
double value = distance.getValue();
// 单位
String unit = distance.getUnit();
System.out.println("两地之间的距离:"+value+unit);

2.5、根据一个坐标查询附近位置

这个用途很广,也是存入坐标的最主要用途。给出当前地址,查询附近多少千米或者米的地点。

  • 使用的方法

只返回坐标位置的名称,坐标信息没有返回的。

redisTemplate.boundGeoOps(K key)
               .radius(Circle var1);

可以根据var2配置返回值的信息。

redisTemplate.boundGeoOps(K key)
               .radius(Circle var1, GeoRadiusCommandArgs var2);

var1:设置当前经纬度坐标和需要查询附近多少距离和单位。

var2:设置返回值的数据。

  • 范例

这个只是看着有点多,其实思路非常简单。

// 指定经度和维度
Point point = new Point(106.6682, 26.896905);
// 指定距离和单位,目前是100千米km
Distance distance = new Distance(100, Metrics.KILOMETERS);
Circle circle = new Circle(point, distance);
// RedisGeoCommands.GeoLocation <String> 中的String 类型就是保存位置时填写的那个位置名称的数据类型
// .radius(circle); 只给 Circle 参数,那么只会返回一个网点名称,其余的参数都没有
//        GeoResults<RedisGeoCommands.GeoLocation<String>> outlets = redisTemplate.boundGeoOps("outlets").radius(circle);


// 指定返回的数据,当前这个点与附近这些点的距离
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
args.sortAscending() // 升序排序
    //                .sortDescending() // 降序排序
    .includeCoordinates() // 包含坐标信息信息
    .includeDistance() // 包含距离信息
    .limit(10); // 显示返回数量
// 根据坐标查询,并设置返回的参数
GeoResults<RedisGeoCommands.GeoLocation<String>> outlets = redisTemplate.boundGeoOps("outlets").radius(circle,args);
// 循环输出
for (GeoResult<RedisGeoCommands.GeoLocation<String>> result : outlets){

    // 返回网点信息
    RedisGeoCommands.GeoLocation<String> location = result.getContent();
    // 网点名
    String name = location.getName();
    // 网点坐标
    Point point1 = location.getPoint();

    // 返回距离
    Distance dis = result.getDistance();
    // 距离
    double value = dis.getValue();
    // 单位
    String unit = dis.getUnit();

    // 输出
    System.out.println("网点名:"+ name + "x坐标"+point1.getX() + "y坐标" + point1.getY() + "距离:" + value + "单位:" + unit);
}

在这里插入图片描述

2.6、根据地点名称查询附近位置

这个和2.5差不多,只是上面这个是根据一个经纬度坐标查询。而这个是根据地理位置名称查询,这个名称是添加位置的时候添加的名称。注意:如果查询的这个位置名称在redis中不存在,那么是会包异常的。

  • 使用方法
redisTemplate.boundGeoOps(K key)
               .radius(M var1, Distance var2, GeoRadiusCommandArgs var3);

key:集合名称

M:位置名称,添加位置保存的那个名称。

var2:设置距离和单位。

var3:设置返回数据

独一无二的 上面是通过 Circle对象指定坐标和距离。而这里是拆开设置,位置名称和距离信息,其余的一点都没有改变。

特别注意:这里的位置名称一定要存在,如果位置名称不存在就会报异常。org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR could not decode requested zset member

 // 50 千米
        Distance distance = new Distance(50, Metrics.KILOMETERS);
        // 指定返回的数据,当前这个点与附近这些点的距离
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        args.sortAscending() // 升序排序
//                .sortDescending() // 降序排序
                .includeCoordinates() // 包含坐标信息信息
                .includeDistance() // 包含距离信息
                .limit(10); // 显示返回数量
        // 根据坐标查询,并设置返回的参数
        // 只是 .radius("位置1",distance,args); 这里传输参数和testGeoRadius() 方法不同,其余全部一样。
        // 注意:查询这个名称一定要在集合中存在。如果不存在查询就会报异常  org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR could not decode requested zset member
        GeoResults<RedisGeoCommands.GeoLocation<String>> outlets = redisTemplate.boundGeoOps("outlets").radius("位置2",distance,args);

        // 循环输出
        for (GeoResult<RedisGeoCommands.GeoLocation<String>> result : outlets){

            // 返回网点信息
            RedisGeoCommands.GeoLocation<String> location = result.getContent();
            // 网点名
            String name = location.getName();
            // 网点坐标
            Point point1 = location.getPoint();

            // 返回距离
            Distance dis = result.getDistance();
            // 距离
            double value = dis.getValue();
            // 单位
            String unit = dis.getUnit();

            // 输出
            System.out.println("网点名:"+ name + "x坐标"+point1.getX() + "y坐标" + point1.getY() + "距离:" + value + "单位:" + unit);
 // 50 千米
        Distance distance = new Distance(50, Metrics.KILOMETERS);
        // 指定返回的数据,当前这个点与附近这些点的距离
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        args.sortAscending() // 升序排序
//                .sortDescending() // 降序排序
                .includeCoordinates() // 包含坐标信息信息
                .includeDistance() // 包含距离信息
                .limit(10); // 显示返回数量
        // 根据坐标查询,并设置返回的参数
        // 只是 .radius("位置1",distance,args); 这里传输参数和testGeoRadius() 方法不同,其余全部一样。
        // 注意:查询这个名称一定要在集合中存在。如果不存在查询就会报异常  org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR could not decode requested zset member
        GeoResults<RedisGeoCommands.GeoLocation<String>> outlets = redisTemplate.boundGeoOps("outlets").radius("位置2",distance,args);

        // 循环输出
        for (GeoResult<RedisGeoCommands.GeoLocation<String>> result : outlets){

            // 返回网点信息
            RedisGeoCommands.GeoLocation<String> location = result.getContent();
            // 网点名
            String name = location.getName();
            // 网点坐标
            Point point1 = location.getPoint();

            // 返回距离
            Distance dis = result.getDistance();
            // 距离
            double value = dis.getValue();
            // 单位
            String unit = dis.getUnit();

            // 输出
            System.out.println("网点名:"+ name + "x坐标"+point1.getX() + "y坐标" + point1.getY() + "距离:" + value + "单位:" + unit);
  • 范例
// 50 千米
Distance distance = new Distance(50, Metrics.KILOMETERS);
// 指定返回的数据,当前这个点与附近这些点的距离
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
args.sortAscending() // 升序排序
    //                .sortDescending() // 降序排序
    .includeCoordinates() // 包含坐标信息信息
    .includeDistance() // 包含距离信息
    .limit(10); // 显示返回数量
// 根据坐标查询,并设置返回的参数
// 只是 .radius("位置1",distance,args); 这里传输参数和testGeoRadius() 方法不同,其余全部一样。
// 注意:查询这个名称一定要在集合中存在。如果不存在查询就会报异常  org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR could not decode requested zset member
GeoResults<RedisGeoCommands.GeoLocation<String>> outlets = redisTemplate.boundGeoOps("outlets").radius("位置2",distance,args);

// 循环输出
for (GeoResult<RedisGeoCommands.GeoLocation<String>> result : outlets){

    // 返回网点信息
    RedisGeoCommands.GeoLocation<String> location = result.getContent();
    // 网点名
    String name = location.getName();
    // 网点坐标
    Point point1 = location.getPoint();

    // 返回距离
    Distance dis = result.getDistance();
    // 距离
    double value = dis.getValue();
    // 单位
    String unit = dis.getUnit();

    // 输出
    System.out.println("网点名:"+ name + "x坐标"+point1.getX() + "y坐标" + point1.getY() + "距离:" + value + "单位:" + unit);
}

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐