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

系统管理员 2023-10-13 10:03 176阅读 0赞

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

一、Redis GEO 操作方法

geoadd:添加地理位置的坐标

geopos:获取地理位置的坐标

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

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

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

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

GeoOperations 的 add 方法

  1. org.springframework.data.redis.core.GeoOperations
  2. // Add RedisGeoCommands.GeoLocation into key.
  3. Long add(K key, RedisGeoCommands.GeoLocation<M> location)

GeoOperations 的 distance 方法

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

Spring整合

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.data.geo.Distance;
  3. import org.springframework.data.geo.Point;
  4. import org.springframework.data.redis.connection.RedisGeoCommands;
  5. import org.springframework.data.redis.core.GeoOperations;
  6. import org.springframework.data.redis.core.RedisTemplate;
  7. import org.springframework.stereotype.Component;
  8. @Component
  9. public class GeoUtil {
  10. @Autowired
  11. private RedisTemplate redisTemplate;
  12. /**
  13. * 作为存储经纬度列表的key值
  14. */
  15. private static final String GEO_KEY = "DISTANCE";
  16. /**
  17. * 将经纬度信息添加到redis中
  18. * @param certId 标识
  19. * @param longitude 经度
  20. * @param latitude 纬度
  21. */
  22. public void geoAdd(String certId, double longitude, double latitude) {
  23. GeoOperations geoOperations = redisTemplate.opsForGeo();
  24. Point point = new Point(longitude, latitude);
  25. RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands.GeoLocation(certId, point);
  26. geoOperations.add(GEO_KEY, geoLocation);
  27. }
  28. /**
  29. * 两个人之间的距离
  30. * @param certId1
  31. * @param certId2
  32. * @return
  33. */
  34. public double distanceBetween(String certId1, String certId2) {
  35. GeoOperations geoOperations = redisTemplate.opsForGeo();
  36. Distance distance = geoOperations.distance(GEO_KEY, certId1, certId2);
  37. return distance.getValue();
  38. }
  39. /**
  40. * 查询距离某个人指定范围内的人,包括距离多少米
  41. * @param certId
  42. * @param distance
  43. * @return
  44. */
  45. public Map<String, Double> distanceInclude(String certId, double distance) {
  46. Map<String, Double> map = new LinkedHashMap<>();
  47. GeoOperations geoOperations = redisTemplate.opsForGeo();
  48. RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
  49. GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = geoOperations.radius(GEO_KEY, certId, new Distance(distance), geoRadiusCommandArgs.includeDistance());
  50. if (geoResults != null) {
  51. Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = geoResults.iterator();
  52. while (iterator.hasNext()) {
  53. GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult = iterator.next();
  54. // 与目标点相距的距离信息
  55. Distance geoResultDistance = geoResult.getDistance();
  56. // 该点的信息
  57. RedisGeoCommands.GeoLocation<String> geoResultContent = geoResult.getContent();
  58. map.put(geoResultContent.getName(), geoResultDistance.getValue());
  59. }
  60. }
  61. return map;
  62. }
  63. }

#

二、附近搜索功能

引入redis依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. <version>2.3.0.RELEASE</version>
  5. </dependency>

domain

  1. import io.swagger.annotations.ApiModel;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. @Data
  5. @ApiModel("位置信息")
  6. public class Location {
  7. @ApiModelProperty("经度")
  8. private Double longitude;
  9. @ApiModelProperty("纬度")
  10. private Double latitude;
  11. @ApiModelProperty("半径")
  12. private Double radius;
  13. @ApiModelProperty("条数")
  14. private Long limit;
  15. }

Service

  1. String baiduKey = RedisConstant.KEY_BAIDU_POI + "cities";
  2. Map<String, Point> points = new HashMap<>();
  3. points.put("shijiazhuang", new Point(114.48, 38.03));
  4. points.put("xingtang", new Point(114.54, 38.42));
  5. points.put("guangcheng", new Point(114.84, 38.03));
  6. points.put("gaoyi", new Point(114.58, 37.62));
  7. points.put("zhaoxian", new Point(114.78, 37.76));
  8. points.put("jinxing", new Point(114.13, 38.03));
  9. points.put("luquan", new Point(114.03, 38.08));
  10. points.put("xinle", new Point(114.67, 38.33));
  11. points.put("zhengding", new Point(114.56, 38.13));
  12. RedisUtil.opsForGeo().add(baiduKey,points);
  13. //设置当前位置
  14. // // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
  15. Point point = new Point(114.56, 38.13);
  16. //设置半径范围 (KILOMETERS 千米;METERS 米)
  17. Metric metric = RedisGeoCommands.DistanceUnit.KILOMETERS;
  18. Distance distance = new Distance(3, metric);
  19. Circle circle = new Circle(point, distance);
  20. //设置参数 包括距离、坐标、条数
  21. RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands.GeoRadiusCommandArgs
  22. .newGeoRadiusArgs()
  23. .includeDistance()//包含距离
  24. .includeCoordinates();//包含经纬度
  25. // .sortAscending()//正序排序
  26. // .limit(50); //条数
  27. GeoResults<RedisGeoCommands.GeoLocation<String>> radius = RedisUtil.opsForGeo().radius(baiduKey,circle, geoRadiusCommandArgs);
  28. if (geoResults != null) {
  29. Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = geoResults.iterator();
  30. while (iterator.hasNext()) {
  31. GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult = iterator.next();
  32. // 与目标点相距的距离信息
  33. Distance geoResultDistance = geoResult.getDistance();
  34. // 该点的信息
  35. RedisGeoCommands.GeoLocation<String> geoResultContent = geoResult.getContent();
  36. }
  37. }

Redis存入单个,批量存

  1. /**
  2. * Redis缓存酒店ID,以及经纬度
  3. * 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
  4. * @param newHotelRecommend
  5. * @return
  6. */
  7. private void redisCacheLongitudeAndLatitude(HotelRecommendNew newHotelRecommend){
  8. String baiduKey = RedisKeyConstants.KEY_RECOMMEND_HOTEL + newHotelRecommend.getCityCode();
  9. // 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
  10. if (StringUtils.isNotEmpty(newHotelRecommend.getCoordinatesBaidu())){
  11. // 纬度 latitude;经度 longitude;苏州经纬度:纬度31.29.9468;经度121.16.0530
  12. String[] splitBaidu = newHotelRecommend.getCoordinatesBaidu().split(",");
  13. String latBaidu = splitBaidu[0];
  14. String lngBaidu = splitBaidu[1];
  15. // prefix + 城市code + 高德,百度类型
  16. // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
  17. redisCache.redisTemplate.opsForGeo().add(baiduKey ,
  18. new Point(Double.parseDouble(lngBaidu),Double.parseDouble(latBaidu)),
  19. newHotelRecommend.getHotelId().toString());
  20. }
  21. }
  22. /**
  23. * Redis缓存酒店ID,以及经纬度
  24. * 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
  25. * @param newHotelRecommendList
  26. * @return
  27. */
  28. private void batchRedisCacheLongitudeAndLatitude(List<HotelRecommendNew> newHotelRecommendList){
  29. Map<Integer,List<HotelRecommendNew>> groupCityMap = newHotelRecommendList.stream().collect(Collectors.groupingBy(HotelRecommendNew::getCityCode));
  30. groupCityMap.entrySet().stream().forEach((Map.Entry<Integer, List<HotelRecommendNew>> entry) -> {
  31. String baiduKey = RedisKeyConstants.KEY_RECOMMEND_HOTEL + entry.getKey();
  32. Map<String,Point> pointMap = new HashMap<>();
  33. entry.getValue().stream().forEach(hotelRecommendNew -> {
  34. // 百度经纬度(默认存百度经纬度,即使前端传的 高德,谷歌,也将其转为百度,否则即数据错误)
  35. if (StringUtils.isNotEmpty(hotelRecommendNew.getCoordinatesBaidu())){
  36. // 纬度 Latitude;经度 longitude;苏州经纬度:纬度31.29.9468;经度121.16.0530
  37. String[] splitBaidu = hotelRecommendNew.getCoordinatesBaidu().split(",");
  38. String latBaidu = splitBaidu[0];
  39. String lngBaidu = splitBaidu[1];
  40. // Point 中 x:经度"longitude":114.56,y:纬度"latitude":38.13
  41. pointMap.put(hotelRecommendNew.getHotelId().toString(),new Point(Double.parseDouble(lngBaidu),Double.parseDouble(latBaidu)));
  42. }
  43. });
  44. // prefix + 城市code + 高德,百度类型 (批量加入)
  45. redisCache.redisTemplate.opsForGeo().add(baiduKey,pointMap);
  46. });
  47. }

存入Redis数据

096c81f044c94e24ab81691fb5288a74.png测试数据

  1. POST http://localhost:6001/geo/city
  2. Content-Type: application/json
  3. {
  4. "longitude": 114.56,
  5. "latitude": 38.13,
  6. "radius": 100000,
  7. "limit": 10
  8. }

返回结果

  1. {
  2. "code": 200,
  3. "message": "操作成功",
  4. "data": {
  5. "averageDistance": {
  6. "value": 31642.19217777778,
  7. "metric": "METERS",
  8. "unit": "m",
  9. "normalizedValue": 0.004961039905191403
  10. },
  11. "content": [
  12. {
  13. "content": {
  14. "name": "zhengding",
  15. "point": {
  16. "x": 114.55999821424484,
  17. "y": 38.12999923666221
  18. }
  19. },
  20. "distance": {
  21. "value": 0.1778,
  22. "metric": "METERS",
  23. "unit": "m",
  24. "normalizedValue": 2.787647866453794E-8
  25. }
  26. },
  27. {
  28. "content": {
  29. "name": "shijiazhuang",
  30. "point": {
  31. "x": 114.55999821424484,
  32. "y": 38.02999941748397
  33. }
  34. },
  35. "distance": {
  36. "value": 13144.3531,
  37. "metric": "METERS",
  38. "unit": "m",
  39. "normalizedValue": 0.0020608452123245394
  40. }
  41. },
  42. {
  43. "content": {
  44. "name": "xinle",
  45. "point": {
  46. "x": 114.55999821424484,
  47. "y": 38.329998875018696
  48. }
  49. },
  50. "distance": {
  51. "value": 24232.5609,
  52. "metric": "METERS",
  53. "unit": "m",
  54. "normalizedValue": 0.0037993164618445796
  55. }
  56. },
  57. {
  58. "content": {
  59. "name": "guangcheng",
  60. "point": {
  61. "x": 114.55999821424484,
  62. "y": 38.02999941748397
  63. }
  64. },
  65. "distance": {
  66. "value": 26919.7324,
  67. "metric": "METERS",
  68. "unit": "m",
  69. "normalizedValue": 0.004220626242427844
  70. }
  71. },
  72. {
  73. "content": {
  74. "name": "xingtang",
  75. "point": {
  76. "x": 114.55999821424484,
  77. "y": 38.419999219223335
  78. }
  79. },
  80. "distance": {
  81. "value": 32302.7819,
  82. "metric": "METERS",
  83. "unit": "m",
  84. "normalizedValue": 0.005064610857371048
  85. }
  86. },
  87. {
  88. "content": {
  89. "name": "jinxing",
  90. "point": {
  91. "x": 114.55999821424484,
  92. "y": 38.02999941748397
  93. }
  94. },
  95. "distance": {
  96. "value": 39255.7243,
  97. "metric": "METERS",
  98. "unit": "m",
  99. "normalizedValue": 0.006154732063610425
  100. }
  101. },
  102. {
  103. "content": {
  104. "name": "zhaoxian",
  105. "point": {
  106. "x": 114.55999821424484,
  107. "y": 37.760000919591185
  108. }
  109. },
  110. "distance": {
  111. "value": 45453.0791,
  112. "metric": "METERS",
  113. "unit": "m",
  114. "normalizedValue": 0.007126388018946599
  115. }
  116. },
  117. {
  118. "content": {
  119. "name": "luquan",
  120. "point": {
  121. "x": 114.55999821424484,
  122. "y": 38.07999932707309
  123. }
  124. },
  125. "distance": {
  126. "value": 46718.8049,
  127. "metric": "METERS",
  128. "unit": "m",
  129. "normalizedValue": 0.00732483559070619
  130. }
  131. },
  132. {
  133. "content": {
  134. "name": "gaoyi",
  135. "point": {
  136. "x": 114.55999821424484,
  137. "y": 37.62000066579741
  138. }
  139. },
  140. "distance": {
  141. "value": 56752.5152,
  142. "metric": "METERS",
  143. "unit": "m",
  144. "normalizedValue": 0.00889797682301274
  145. }
  146. }
  147. ]
  148. }
  149. }
  150. Response code: 200; Time: 92ms; Content length: 1844 bytes

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

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

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

发表评论

表情:
评论列表 (有 0 条评论,176人围观)

还没有评论,来说两句吧...

相关阅读