Redis使用Geo存储经纬度



Geo原理简单介绍

GeoHash 算法将 二维的经纬度 数据映射到 一维 的整数,这样所有的元素都将在挂载到一条线上,距离靠近的二维坐标映射到一维后的点之间距离也会很接近。当我们想要计算 「附近的人时」,首先将目标位置映射到这条线上,然后在这个一维的线上获取附近的点就行了。

它的核心思想就是把整个地球看成是一个 二维的平面,然后把这个平面不断地等分成一个一个小的方格,每一个 坐标元素都位于其中的 唯一一个方格 中,等分之后的 方格越小,那么坐标也就 越精确

RedisGeo 数据结构,它们将 全部放在一个 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);
	    
	}

参考: https://www.it610.com/article/1288713480500748288.html.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值