目录
Geo原理简单介绍
GeoHash 算法将 二维的经纬度 数据映射到 一维 的整数,这样所有的元素都将在挂载到一条线上,距离靠近的二维坐标映射到一维后的点之间距离也会很接近。当我们想要计算 「附近的人时」,首先将目标位置映射到这条线上,然后在这个一维的线上获取附近的点就行了。
它的核心思想就是把整个地球看成是一个 二维的平面,然后把这个平面不断地等分成一个一个小的方格,每一个 坐标元素都位于其中的 唯一一个方格 中,等分之后的 方格越小,那么坐标也就 越精确。
Redis 的 Geo 数据结构,它们将 全部放在一个 zset 集合中。在 Redis 的集群环境中,集合可能会从一个节点迁移到另一个节点,如果单个 key 的数据过大,会对集群的迁移工作造成较大的影响,在集群环境中单个 key 对应的数据量不宜超过 1M,否则会导致集群迁移出现卡顿现象,影响线上服务的正常运行。
所以,这里建议 Geo 的数据使用 单独的 Redis 实例部署,不使用集群环境,如果数据量过亿甚至更大,就需要对 Geo 数据进行拆分,按国家拆分、按省拆分,按市拆分。
Geo常用方法讲解:
这里使用的是spring boot项目。了解如何在spring boot中使用Redis.
add方法:添加地理位置坐标
Point point = new Point(112.24292267773436, 30.337854070185536);//使用point定位经纬度
GeoLocation<String> geo = new GeoLocation<String>("荆州", point);
opsForGeo.add(key, geo);
point = new Point(111.28711213085936, 30.688053698037617);
opsForGeo.add(key, point, "宜昌");
Map<String,Point> memberCoordinateMap = new HashMap<String, Point>();
memberCoordinateMap.put("武汉", new Point(114.29736603710936, 30.59352961254808));
memberCoordinateMap.put("襄阳", new Point(112.12207306835936, 32.00160906017225));
memberCoordinateMap.put("荆门", new Point(112.216009,31.027841));
opsForGeo.add(key, memberCoordinateMap);
pos方法:给定的 key 里返回所有指定名称(member)的位置(经度和纬度)
List<Point> position = opsForGeo.position(key, "荆州","宜昌");
position.forEach(System.out::println);
//dist方法:计算两个位置之间的距离。
Distance distance = opsForGeo.distance(key, "荆州", "宜昌",RedisGeoCommands.DistanceUnit.KILOMETERS);
System.out.println("荆州与宜昌的距离:"+distance.getValue()+"KM");
radius方法:根据给定的位置名称来获取指定范围内的地理位置集合。
GeoRadiusCommandArgs geoRadiusCommandArgs = GeoRadiusCommandArgs.newGeoRadiusArgs()
.sortAscending()// 排序
.limit(3)// 输出元素的个数
.includeCoordinates()// 输出经纬度
.includeDistance();// 距离
// 封装距离参数
distance = new Distance(100d, RedisGeoCommands.DistanceUnit.KILOMETERS);
GeoResults<GeoLocation<String>> radius = opsForGeo.radius(key, "荆州", distance, geoRadiusCommandArgs);
radius.getContent().forEach(s -> System.out.println("名称:" + s.getContent().getName() + " 经纬度:" + s.getContent().getPoint() + " 距离:" + s.getDistance()));
radius方法:根据给定的经纬度坐标来获取指定范围内的地理位置集合。
point = new Point(111.28711213085936, 30.688053698037617);
Circle circle = new Circle(new Point(112.24292267773436, 30.337854070185536), distance);
GeoResults<GeoLocation<String>> radius2 = opsForGeo.radius(key, circle,geoRadiusCommandArgs);
radius2.getContent().forEach(s -> System.out.println("名称:" + s.getContent().getName() + " 经纬度:" + s.getContent().getPoint() + " 距离:" + s.getDistance()));
hash方法:获取位置的hash值
List<String> hash = opsForGeo.hash(key, "荆州","宜昌");
hash.forEach(System.out::println);
完整的demo
/**
* GeoHash 算法将 二维的经纬度 数据映射到 一维 的整数,这样所有的元素都将在挂载到一条线上,距离靠近的二维坐标映射到一维后的点之间距离也会很接近。
* 当我们想要计算 「附近的人时」,首先将目标位置映射到这条线上,然后在这个一维的线上获取附近的点就行了。
* 参考:https://www.it610.com/article/1288713480500748288.htm
* @描述:
* @作者:严磊
* @时间:2020年11月25日 下午11:11:15
*/
public void GeoHash(){
String key = "湖北";
GeoOperations<String, String> opsForGeo = redisTemplate.opsForGeo();
//add方法:添加地理位置坐标
Point point = new Point(112.24292267773436, 30.337854070185536);//使用point定位经纬度
GeoLocation<String> geo = new GeoLocation<String>("荆州", point);
opsForGeo.add(key, geo);
point = new Point(111.28711213085936, 30.688053698037617);
opsForGeo.add(key, point, "宜昌");
Map<String,Point> memberCoordinateMap = new HashMap<String, Point>();
memberCoordinateMap.put("武汉", new Point(114.29736603710936, 30.59352961254808));
memberCoordinateMap.put("襄阳", new Point(112.12207306835936, 32.00160906017225));
memberCoordinateMap.put("荆门", new Point(112.216009,31.027841));
opsForGeo.add(key, memberCoordinateMap);
//pos方法:给定的 key 里返回所有指定名称(member)的位置(经度和纬度)
List<Point> position = opsForGeo.position(key, "荆州","宜昌");
position.forEach(System.out::println);
//dist方法:计算两个位置之间的距离。
Distance distance = opsForGeo.distance(key, "荆州", "宜昌",RedisGeoCommands.DistanceUnit.KILOMETERS);
System.out.println("荆州与宜昌的距离:"+distance.getValue()+"KM");
//radius方法:根据给定的位置名称来获取指定范围内的地理位置集合。
GeoRadiusCommandArgs geoRadiusCommandArgs = GeoRadiusCommandArgs.newGeoRadiusArgs()
.sortAscending()// 排序
.limit(3)// 输出元素的个数
.includeCoordinates()// 输出经纬度
.includeDistance();// 距离
// 封装距离参数
distance = new Distance(100d, RedisGeoCommands.DistanceUnit.KILOMETERS);
GeoResults<GeoLocation<String>> radius = opsForGeo.radius(key, "荆州", distance, geoRadiusCommandArgs);
radius.getContent().forEach(s -> System.out.println("名称:" + s.getContent().getName() + " 经纬度:" + s.getContent().getPoint() + " 距离:" + s.getDistance()));
//radius方法:根据给定的经纬度坐标来获取指定范围内的地理位置集合。
point = new Point(111.28711213085936, 30.688053698037617);
Circle circle = new Circle(new Point(112.24292267773436, 30.337854070185536), distance);
GeoResults<GeoLocation<String>> radius2 = opsForGeo.radius(key, circle,geoRadiusCommandArgs);
radius2.getContent().forEach(s -> System.out.println("名称:" + s.getContent().getName() + " 经纬度:" + s.getContent().getPoint() + " 距离:" + s.getDistance()));
//hash方法:获取位置的hash值
List<String> hash = opsForGeo.hash(key, "荆州","宜昌");
hash.forEach(System.out::println);
}