Spring Data Redis 教程

GeoOperations 操作接口

相信大家都用过打车软件、或者使用地图软件搜索过附近的公交站台、商场、地铁口等等,这种都离不开基于位置服务(Location-Based Service,LBS)的应用。此类应用都是基于经纬度来查询附近的目标,Redis GEO 就适用于此类场景。

Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增。Redis GEO 支持如下操作方法:

  • geoadd:添加地理位置的坐标。

  • geopos:获取地理位置的坐标。

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

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

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

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

Redis GEO 原理介绍

Redis GEO 并不是一种新的数据结构,而是基于 Sorted Set 实现的。Sorted Set 结构保存的数据形式是 key-score,即一个元素对应一个分值,默认是根据分值排序的,且可以进行范围查询。但是经纬度是一个数据对,比如(104.072206,30.663486),那么 Redis 是如何将经纬度转换成 score 值的呢?转换成 score 值之后,是如何保证分值相邻的元素距离也相近的呢?这一切就依赖于 GeoHash 编码。

GeoOperations 操作接口

在 Spring Data Redis 中,可以使用 opsForGeo() 方法获取到 GeoOperations 操作接口。代码如下:

GeoOperations<String,String> ops = redisTemplate.opsForGeo();

添加地理位置坐标

使用 add() 方法可以向 Redis 中添加一个或多个地理位置信息,方法定义如下:

  • Long add(K key, Iterable<RedisGeoCommands.GeoLocation<M>> locations)  将 RedisGeoCommands.GeoLocations 添加到键

  • Long add(K key, Map<M,org.springframework.data.geo.Point> memberCoordinateMap) 将 member / Point map 键值对添加到键

  • Long add(K key, org.springframework.data.geo.Point point, M member) 将具有给定成员名称的 Point 添加到键

  • Long add(K key, RedisGeoCommands.GeoLocation<M> location) 将 RedisGeoCommands.GeoLocation 添加到键

示例:

GeoOperations<String,String> ops = redisTemplate.opsForGeo();
// 成都天府广场坐标
Point point = new Point(104.072206,30.663486);
ops.add("geo-key", point, "chengdu");

// 北京天安门广场坐标
point = new Point(116.403039,39.91513);
ops.add("geo-key", point, "beijing");

// 获取地理坐标
List<Point> pointList = ops.position("geo-key", "chengdu", "beijing");
for(Point item : pointList) {
    System.out.println("x=" + item.getX() + ", y=" + item.getY());
}

运行示例,输出结果如下:

x=104.0722069144249, y=30.663486325924417
x=116.40304058790207, y=39.915129842271014

计算两个位置间距离

使用 distance() 方法可以计算两个地理位置之间的距离,方法定义如下:

  • org.springframework.data.geo.Distance distance(K key, M member1, M member2) 计算 member1 和 member2 之间的距离

  • org.springframework.data.geo.Distance distance(K key, M member1, M member2, org.springframework.data.geo.Metric metric) 计算 member1 和 member2 之间的距离,使用指定的度量单位返回距离

示例:

GeoOperations<String,String> ops = redisTemplate.opsForGeo();
// 成都天府广场坐标
Point point = new Point(104.072206,30.663486);
ops.add("geo-key", point, "chengdu");

// 北京天安门广场坐标
point = new Point(116.403039,39.91513);
ops.add("geo-key", point, "beijing");

// 计算成都到北京的距离
Distance distance = ops.distance("geo-key", "chengdu", "beijing");
System.out.println("相距:" + distance.getValue() + distance.getUnit());

// Metrics.KILOMETERS 公里
// Metrics.MILES 英里
// Metrics.NEUTRAL 米
distance = ops.distance("geo-key", "chengdu",
        "beijing", Metrics.MILES);
System.out.println("相距:" + distance.getValue() + distance.getUnit());

distance = ops.distance("geo-key", "chengdu",
        "beijing", Metrics.KILOMETERS);
System.out.println("相距:" + distance.getValue() + distance.getUnit());

distance = ops.distance("geo-key", "chengdu",
        "beijing", Metrics.NEUTRAL);
System.out.println("相距:" + distance.getValue() + distance.getUnit());

运行示例,输出结果如下:

相距:1517797.1972m
相距:943.1178mi
相距:1517.7972km
相距:1517797.1972m

获取地理位置的 Geohash 值

使用 hash() 方法获取指定地理位置的 GeoHash 值,方法定义如下:

  • List<String> hash(K key, M... members) 获取一个或多个 member 的 Geohash 表示形式。

示例:

GeoOperations<String,String> ops = redisTemplate.opsForGeo();
// 成都天府广场坐标
Point point = new Point(104.072206,30.663486);
ops.add("geo-key", point, "chengdu");

// 北京天安门广场坐标
point = new Point(116.403039,39.91513);
ops.add("geo-key", point, "beijing");

// 获取地理位置的GeoHash
List<String> geoHashs = ops.hash("geo-key", "chengdu", "beijing");
for(String geoHash : geoHashs) {
        System.out.println(geoHash);
}

运行示例,输出结果如下:

wm6n2np5f00
wx4g0f647r0

获取地理位置的坐标

使用 position() 方法获取指定成员的地理位置信息,方法定义如下:

  • List<org.springframework.data.geo.Point> position(K key, M... members) 获取一个或多个成员的位置的 Point 表示形式。

示例:

GeoOperations<String,String> ops = redisTemplate.opsForGeo();
// 成都天府广场坐标
Point point = new Point(104.072206,30.663486);
ops.add("geo-key", point, "chengdu");

// 北京天安门广场坐标
point = new Point(116.403039,39.91513);
ops.add("geo-key", point, "beijing");

// 获取成都的地理位置坐标
List<Point> pointList = ops.position("geo-key", "chengdu");
if(null != pointList && pointList.size() > 0) {
        Point p = pointList.get(0);
        System.out.println("x=" + p.getX() + ", y=" + p.getY());
}

运行示例,输出结果如下:

x=104.0722069144249, y=30.663486325924417

获取指定范围内地理位置集合

使用 radius() 方法可以获取目标地理位置周围指定半径中的地理位置信息,方法定义如下:

  • org.springframework.data.geo.GeoResults<RedisGeoCommands.GeoLocation<M>> radius(K key, org.springframework.data.geo.Circle within) 获取给定边界内的成员的地理位置信息

  • org.springframework.data.geo.GeoResults<RedisGeoCommands.GeoLocation<M>> radius(K key, org.springframework.data.geo.Circle within, RedisGeoCommands.GeoRadiusCommandArgs args) 使用RedisGeoCommands.GeoRadiusCommandArgs 获取给定边界内的成员的地理位置信息。

  • org.springframework.data.geo.GeoResults<RedisGeoCommands.GeoLocation<M>> radius(K key, M member, org.springframework.data.geo.Distance distance) 使用指定的 Metric(度量单位)和成员坐标,返回给定半径定义的圆内的其他成员地理位置信息。

  • org.springframework.data.geo.GeoResults<RedisGeoCommands.GeoLocation<M>> radius(K key, M member, org.springframework.data.geo.Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args) 获取由成员坐标和 Metric 与 RedisGeoCommands.GeoRadiusCommandArgs 给定半径定义的圆内的成员地理位置信息。

  • org.springframework.data.geo.GeoResults<RedisGeoCommands.GeoLocation<M>> radius(K key, M member, double radius) 获取由成员坐标和给定半径定义的圆内的成员地理位置信息。

示例:

String[] names = {
        "chengdu",
        "chengdudong_railway_station",
        "chengdubei_railway_station",
        "beijing",
        "wuhan"
};
double[][] points = {
        {104.07332,30.664768},
        {104.145184,30.636433},
        {104.145184,30.636433},
        {116.400739,39.920885},
        {114.360454,30.588768}
};
GeoOperations<String,String> ops = redisTemplate.opsForGeo();
// 初始化坐标数据
int i = 0;
for(double[] point : points) {
        ops.add("geo-key", new Point(point[0], point[1]), names[i++]);
}

// 获取成都附近 100 公里范围内的地理位置
GeoResults<RedisGeoCommands.GeoLocation<String>> geos =
        ops.radius("geo-key", "chengdu", 100*1000);
if(null != geos) {
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> geoResults = geos.getContent();
        for(GeoResult<RedisGeoCommands.GeoLocation<String>> geo : geoResults) {
        RedisGeoCommands.GeoLocation<String> geoLocation = geo.getContent();
        System.out.println(geoLocation.getName());
        }
}

运行示例,输出结果如下:

chengdubei_railway_station
chengdudong_railway_station
chengdu

删除指定地理位置

  • Long remove(K key, M... members) 移除指定的成员

示例:

GeoOperations<String,String> ops = redisTemplate.opsForGeo();
// 成都天府广场坐标
Point point = new Point(104.072206,30.663486);
ops.add("geo-key", point, "chengdu");

// 删除成都地理位置
Long size = ops.remove("geo-key", "chengdu");
System.out.println("size=" + size);

// 验证是否已经被删除
List<Point> pointList = ops.position("geo-key", "chengdu");
for(Point p : pointList) {
        System.out.println(p);
}

运行示例,输出结果如下:

size=1
null
说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号