Redis - Redis GEO实现经纬度测算距离,附近搜索范围

Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增

一、Redis GEO 操作方法

geoadd:添加地理位置的坐标

geopos:获取地理位置的坐标

geodist:计算两个位置之间的距离

georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集

georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合

geohash:返回一个或多个位置对象的 geohash 值

GeoOperations 的 add 方法

org.springframework.data.redis.core.GeoOperations

// Add RedisGeoCommands.GeoLocation into key.
Long add(K key, RedisGeoCommands.GeoLocation<M> location)

GeoOperations 的 distance 方法

org.springframework.data.redis.core.GeoOperations// Get the Distance between member1 and member2.
Distance distance(K key, M member1, M member2)

Spring整合

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class GeoUtil {
 
    @Autowired
    private RedisTemplate redisTemplate;
    
    /**
     * 作为存储经纬度列表的key值
     */
    private static final String GEO_KEY = "DISTANCE";
 
    /**
     * 将经纬度信息添加到redis中
     * @param certId 标识
     * @param longitude 经度
     * @param latitude 纬度
     */
    public void geoAdd(String certId, double longitude, double latitude) {
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        Point point = new Point(longitude, latitude);
        RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands.GeoLocation(certId, point);
        geoOperations.add(GEO_KEY, geoLocation);
    }
 
    /**
     * 两个人之间的距离
     * @param certId1
     * @param certId2
     * @return
     */
    public double distanceBetween(String certId1, String certId2) {
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        Distance distance = geoOperations.distance(GEO_KEY, certId1, certId2);
        return distance.getValue();
    }
 
    /**
     * 查询距离某个人指定范围内的人,包括距离多少米
     * @param certId
     * @param distance
     * @return
     */
    public Map<String, Double> distanceInclude(String certId, double distance) {
        Map<String, Double> map = new LinkedHashMap<>();
 
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = geoOperations.radius(GEO_KEY, certId, new Distance(distance), geoRadiusCommandArgs.includeDistance());
        if (geoResults != null) {
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = geoResults.iterator();
            while (iterator.hasNext()) {
                GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult = iterator.next();
                // 与目标点相距的距离信息
                Distance geoResultDistance = geoResult.getDistance();
                // 该点的信息
                RedisGeoCommands.GeoLocation<String> geoResultContent = geoResult.getContent();
                map.put(geoResultContent.getName(), geoResultDistance.getValue());
            }
        }
        return map;
    }
}

二、附近搜索功能

引入redis依赖

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

domain

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel("位置信息")
public class Location {
    @ApiModelProperty("经度")
    private Double longitude;
    @ApiModelProperty("纬度")
    private Double latitude;
    @ApiModelProperty("半径")
    private Double radius;
    @ApiModelProperty("条数")
    private Long limit;
}

Service

        String baiduKey =  RedisConstant.KEY_BAIDU_POI + "cities";

        Map<String, Point> points = new HashMap<>();
        points.put("shijiazhuang", new Point(114.48, 38.03));
        points.put("xingtang", new Point(114.54, 38.42));
        points.put("guangcheng", new Point(114.84, 38.03));
        points.put("gaoyi", new Point(114.58, 37.62));
        points.put("zhaoxian", new Point(114.78, 37.76));
        points.put("jinxing", new Point(114.13, 38.03));
        points.put("luquan", new Point(114.03, 38.08));
        points.put("xinle", new Point(114.67, 38.33));
        points.put("zhengding", new Point(114.56, 38.13));
        RedisUtil.opsForGeo().add(baiduKey,points);


        //设置当前位置
        // // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
        Point point = new Point(114.56, 38.13);
        //设置半径范围 (KILOMETERS 千米;METERS 米)
        Metric metric = RedisGeoCommands.DistanceUnit.KILOMETERS;
        Distance distance = new Distance(3, metric);
        Circle circle = new Circle(point, distance);
        //设置参数 包括距离、坐标、条数
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs
                        .newGeoRadiusArgs()
                .includeDistance()//包含距离
                .includeCoordinates();//包含经纬度
//                .sortAscending()//正序排序
//                .limit(50); //条数
        GeoResults<RedisGeoCommands.GeoLocation<String>> radius = RedisUtil.opsForGeo().radius(baiduKey,circle, geoRadiusCommandArgs);

        if (geoResults != null) {
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = geoResults.iterator();
            while (iterator.hasNext()) {
                GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult = iterator.next();
                // 与目标点相距的距离信息
                Distance geoResultDistance = geoResult.getDistance();
                // 该点的信息
                RedisGeoCommands.GeoLocation<String> geoResultContent = geoResult.getContent();
            }
        }

Redis存入单个,批量存

/**
     * Redis缓存酒店ID,以及经纬度
     * 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
     * @param newHotelRecommend
     * @return
     */
    private void redisCacheLongitudeAndLatitude(HotelRecommendNew newHotelRecommend){
        String baiduKey = RedisKeyConstants.KEY_RECOMMEND_HOTEL + newHotelRecommend.getCityCode();

        // 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
        if (StringUtils.isNotEmpty(newHotelRecommend.getCoordinatesBaidu())){
            // 纬度 latitude;经度 longitude;苏州经纬度:纬度31.29.9468;经度121.16.0530
            String[] splitBaidu = newHotelRecommend.getCoordinatesBaidu().split(",");
            String latBaidu = splitBaidu[0];
            String lngBaidu = splitBaidu[1];

            // prefix + 城市code + 高德,百度类型
            // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
            redisCache.redisTemplate.opsForGeo().add(baiduKey ,
                    new Point(Double.parseDouble(lngBaidu),Double.parseDouble(latBaidu)),
                    newHotelRecommend.getHotelId().toString());
        }
    }

    /**
     * Redis缓存酒店ID,以及经纬度
     * 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
     * @param newHotelRecommendList
     * @return
     */
    private void batchRedisCacheLongitudeAndLatitude(List<HotelRecommendNew> newHotelRecommendList){

        Map<Integer,List<HotelRecommendNew>> groupCityMap = newHotelRecommendList.stream().collect(Collectors.groupingBy(HotelRecommendNew::getCityCode));
        groupCityMap.entrySet().stream().forEach((Map.Entry<Integer, List<HotelRecommendNew>> entry) -> {
            String baiduKey = RedisKeyConstants.KEY_RECOMMEND_HOTEL + entry.getKey();

            Map<String,Point> pointMap = new HashMap<>();
            entry.getValue().stream().forEach(hotelRecommendNew -> {
                // 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
                if (StringUtils.isNotEmpty(hotelRecommendNew.getCoordinatesBaidu())){
                    // 纬度 Latitude;经度 longitude;苏州经纬度:纬度31.29.9468;经度121.16.0530
                    String[] splitBaidu = hotelRecommendNew.getCoordinatesBaidu().split(",");
                    String latBaidu = splitBaidu[0];
                    String lngBaidu = splitBaidu[1];

                    // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
                    pointMap.put(hotelRecommendNew.getHotelId().toString(),new Point(Double.parseDouble(lngBaidu),Double.parseDouble(latBaidu)));
                }
            });

            // prefix + 城市code + 高德,百度类型 (批量加入)
            redisCache.redisTemplate.opsForGeo().add(baiduKey,pointMap);
        });
    }

存入Redis数据

测试数据

POST http://localhost:6001/geo/city
Content-Type: application/json

{
  "longitude": 114.56,
  "latitude": 38.13,
  "radius": 100000,
  "limit": 10
}

返回结果


{
  "code": 200,
  "message": "操作成功",
  "data": {
    "averageDistance": {
      "value": 31642.19217777778,
      "metric": "METERS",
      "unit": "m",
      "normalizedValue": 0.004961039905191403
    },
    "content": [
      {
        "content": {
          "name": "zhengding",
          "point": {
            "x": 114.55999821424484,
            "y": 38.12999923666221
          }
        },
        "distance": {
          "value": 0.1778,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 2.787647866453794E-8
        }
      },
      {
        "content": {
          "name": "shijiazhuang",
          "point": {
            "x": 114.55999821424484,
            "y": 38.02999941748397
          }
        },
        "distance": {
          "value": 13144.3531,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.0020608452123245394
        }
      },
      {
        "content": {
          "name": "xinle",
          "point": {
            "x": 114.55999821424484,
            "y": 38.329998875018696
          }
        },
        "distance": {
          "value": 24232.5609,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.0037993164618445796
        }
      },
      {
        "content": {
          "name": "guangcheng",
          "point": {
            "x": 114.55999821424484,
            "y": 38.02999941748397
          }
        },
        "distance": {
          "value": 26919.7324,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.004220626242427844
        }
      },
      {
        "content": {
          "name": "xingtang",
          "point": {
            "x": 114.55999821424484,
            "y": 38.419999219223335
          }
        },
        "distance": {
          "value": 32302.7819,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.005064610857371048
        }
      },
      {
        "content": {
          "name": "jinxing",
          "point": {
            "x": 114.55999821424484,
            "y": 38.02999941748397
          }
        },
        "distance": {
          "value": 39255.7243,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.006154732063610425
        }
      },
      {
        "content": {
          "name": "zhaoxian",
          "point": {
            "x": 114.55999821424484,
            "y": 37.760000919591185
          }
        },
        "distance": {
          "value": 45453.0791,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.007126388018946599
        }
      },
      {
        "content": {
          "name": "luquan",
          "point": {
            "x": 114.55999821424484,
            "y": 38.07999932707309
          }
        },
        "distance": {
          "value": 46718.8049,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.00732483559070619
        }
      },
      {
        "content": {
          "name": "gaoyi",
          "point": {
            "x": 114.55999821424484,
            "y": 37.62000066579741
          }
        },
        "distance": {
          "value": 56752.5152,
          "metric": "METERS",
          "unit": "m",
          "normalizedValue": 0.00889797682301274
        }
      }
    ]
  }
}

Response code: 200; Time: 92ms; Content length: 1844 bytes

RedisGEO实现附近搜索功能_华安小书童的博客-CSDN博客

利用Redis的Geo功能实现查找附近的位置! - 知乎

RedisGEO实现附近搜索功能_华安小书童的博客-CSDN博客

猜你喜欢

转载自blog.csdn.net/MinggeQingchun/article/details/131504606