在 Redis 的 3.2 版本中加入了地理空间(geospatial)以及索引半径查询的功能,这在需要地理位置功能的应用上或许可以一展身手。
Redis 的地理空间(geospatial)索引允许您存储地理坐标(经纬度)并搜索它们,此数据结构可用于查找给定半径或边界框内的邻近点。本文将介绍怎样通过 jedis 库操作 Redis 的 GeoSpatial 数据类型。
将指定的一个或多个地理位置元素添加 key 中,方法定义如下:
Long geoadd(String key, double longitude, double latitude, String member)
Long geoadd(String key, GeoAddParams params, Map<String,GeoCoordinate> memberCoordinateMap)
Long geoadd(String key, Map<String,GeoCoordinate> memberCoordinateMap)
示例:
// 北京市 116.404188,39.914687 // 天津市 117.225913,39.020948 // 成都市 104.071846,30.58706 // 添加一个点到 mykey 键 jedis.geoadd("mykey", 116.404188, 39.914687, "北京市"); // 一次添加多个点到 mykey 键 Map<String, GeoCoordinate> map = new HashMap<>(); map.put("天津市", new GeoCoordinate(117.225913, 39.020948)); map.put("成都市", new GeoCoordinate(104.071846, 30.58706)); jedis.geoadd("mykey", map);
用来计算指定的两个位置之间的距离,方法定义如下:
Double geodist(String key, String member1, String member2)
Double geodist(String key, String member1, String member2, GeoUnit unit)
注意,GeoUnit 用来指定返回距离的单位,取值为 GeoUnit.FT、GeoUnit.KM、GeoUnit.M 和 GeoUnit.MI。
示例:
// 准备数据 Map<String, GeoCoordinate> map = new HashMap<>(); map.put("天津市", new GeoCoordinate(117.225913, 39.020948)); map.put("成都市", new GeoCoordinate(104.071846, 30.58706)); jedis.geoadd("mykey", map); // 计算成都市和天津市之间的距离 Double dist = jedis.geodist("mykey", "成都市", "天津市"); System.out.println("相距:" + dist); // 相距:1521428.5819 // 使用指定的单位计算成都市和天津市之间的距离 dist = jedis.geodist("mykey", "成都市", "天津市", GeoUnit.KM); System.out.println("相距:" + dist + "KM"); // 相距:1521.4286KM
用来返回一个或多个位置元素的 Geohash 表示,Geohash 是一种经纬度散列算法。方法定义如下:
List<String> geohash(String key, String... members)
示例:
// 准备数据 Map<String, GeoCoordinate> map = new HashMap<>(); map.put("天津市", new GeoCoordinate(117.225913, 39.020948)); map.put("成都市", new GeoCoordinate(104.071846, 30.58706)); jedis.geoadd("mykey", map); // 获取 “天津市” 和 “成都市” 的 Geohash 值 List<String> geohashList = jedis.geohash("mykey", "成都市", "天津市"); System.out.println(Arrays.toString(geohashList.toArray())); // [wm6n00ny9c0, wwgmgx939b0]
用来返回一个或多个位置的经纬度信息,由于采用了 geohash 算法,返回的经纬度和添加时的数据可能会有细小误差。方法定义如下:
List<GeoCoordinate> geopos(String key, String... members)
示例:
// 准备数据 Map<String, GeoCoordinate> map = new HashMap<>(); map.put("天津市", new GeoCoordinate(117.225913, 39.020948)); map.put("成都市", new GeoCoordinate(104.071846, 30.58706)); jedis.geoadd("mykey", map); // 获取 “成都市” 和 “天津市” 的经纬度信息 List<GeoCoordinate> geoCoordinateList = jedis.geopos("mykey", "天津市", "成都市"); for(GeoCoordinate geoCoordinate : geoCoordinateList) { System.out.println("经度:" + geoCoordinate.getLongitude() + " 纬度:" + geoCoordinate.getLatitude()); } // 结果: // 经度:117.22591549158096 纬度:39.02094865472754 // 经度:104.0718474984169 纬度:30.58705941352803
用来搜索以给定位置为中心,半径不超过给定半径的附近所有位置。方法定义如下:
List<GeoRadiusResponse> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit)
List<GeoRadiusResponse> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param)
List<GeoRadiusResponse> georadiusByMember(String key, String member, double radius, GeoUnit unit)
List<GeoRadiusResponse> georadiusByMember(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param)
示例1:使用指定的经纬度搜索附近的位置,如下:
// 准备数据 Map<String, GeoCoordinate> map = new HashMap<>(); map.put("天津市", new GeoCoordinate(117.225913, 39.020948)); map.put("成都市", new GeoCoordinate(104.071846, 30.58706)); map.put("北京市", new GeoCoordinate(116.404188, 39.914687)); jedis.geoadd("mykey", map); // 获取 “北京市” 周边 1000km 范围内的点 List<GeoRadiusResponse> list = jedis.georadius("mykey", 116.404188, 39.914687, 1000, GeoUnit.KM); for(GeoRadiusResponse geoRadiusResponse : list) { System.out.println(geoRadiusResponse.getMemberByString()); } // 结果: // 天津市 // 北京市 // 获取 “北京市” 周边 1000km 范围内的点,以及点的经纬度 GeoRadiusParam geoRadiusParam = new GeoRadiusParam(); geoRadiusParam.withCoord(); // 指示同时返回匹配项的经度、纬度坐标 list = jedis.georadius("mykey", 116.404188, 39.914687, 1000, GeoUnit.KM, geoRadiusParam); for(GeoRadiusResponse geoRadiusResponse : list) { GeoCoordinate geoCoordinate = geoRadiusResponse.getCoordinate(); System.out.println(geoRadiusResponse.getMemberByString() + " 经度:" + geoCoordinate.getLongitude() + " 纬度:" + geoCoordinate.getLatitude()); } // 结果: // 天津市 经度:117.22591549158096 纬度:39.02094865472754 // 北京市 经度:116.40418857336044 纬度:39.91468626606813
示例2:使用指定的成员搜索成员附近的位置,如下:
// 准备数据 Map<String, GeoCoordinate> map = new HashMap<>(); map.put("天津市", new GeoCoordinate(117.225913, 39.020948)); map.put("成都市", new GeoCoordinate(104.071846, 30.58706)); map.put("北京市", new GeoCoordinate(116.404188, 39.914687)); jedis.geoadd("mykey", map); // 获取 “北京市” 周边 1000km 范围内的点,以及点的经纬度 GeoRadiusParam geoRadiusParam = new GeoRadiusParam(); geoRadiusParam.withCoord(); // 指示同时返回匹配项的经度、纬度坐标 List<GeoRadiusResponse> list = jedis.georadiusByMember("mykey", "北京市", 1000, GeoUnit.KM, geoRadiusParam); for(GeoRadiusResponse geoRadiusResponse : list) { GeoCoordinate geoCoordinate = geoRadiusResponse.getCoordinate(); System.out.println(geoRadiusResponse.getMemberByString() + " 经度:" + geoCoordinate.getLongitude() + " 纬度:" + geoCoordinate.getLatitude()); } // 结果: // 天津市 经度:117.22591549158096 纬度:39.02094865472754 // 北京市 经度:116.40418857336044 纬度:39.91468626606813