GEO简介:
- 目前软件中附近的人是怎么实现的呢?
-
- 使用SQL : select taxi from position where x0-r < x < x0 + r and y0-r < y < y0+r
但是这样会有什么问题呢?
1.查询性能问题,如果并发高,数据量大这种查询是要搞垮数据库的
2.这个查询的是一个矩形访问,而不是以我为中心r公里为半径的圆形访问。
3.精准度的问题,我们知道地球不是平面坐标系,而是一个圆球,这种矩形计算在长距离计算时会有很大误差
-
- 使用Redis GEO
原理:
- 将三维的地球变为二维的坐标
- 在将二维的坐标转换为一维的点块
- 最后将一维的点块转换为二进制再通过base32编码
命令
-
geoadd添加经纬度坐标
-
GEOPOS 返回经纬度
-
GEOHASH返回坐标的geohash表示:
geohash算法生成的base32编码值,3维变2维变1维 -
GEODIST 两个位置之间距离:
-
GEORADIUS:
georadius 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
GEORADIUS city 116.418017 39.914402 10 km withdist withcoord count 10 withhash desc
- WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
- WITHCOORD: 将位置元素的经度和维度也一并返回。
- WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大
- COUNT 限定返回的记录数。
- GEORADIUSBYMEMBER
-
找出指定范围内的元素
@RestController
public class GeoController
{
public static final String CITY =“city”;@Autowired private RedisTemplate redisTemplate; @RequestMapping("/geoadd") public String geoAdd() { Map<String, Point> map= new HashMap<>(); map.put("天安门",new Point(116.403963,39.915119)); map.put("故宫",new Point(116.403414 ,39.924091)); map.put("长城" ,new Point(116.024067,40.362639)); redisTemplate.opsForGeo().add(CITY,map); return map.toString(); } @GetMapping(value = "/geopos") public Point position(String member) { //获取经纬度坐标 List<Point> list= this.redisTemplate.opsForGeo().position(CITY,member); return list.get(0); } @GetMapping(value = "/geohash") public String hash(String member) { //geohash算法生成的base32编码值 List<String> list= this.redisTemplate.opsForGeo().hash(CITY,member); return list.get(0); } @GetMapping(value = "/geodist") public Distance distance(String member1, String member2) { Distance distance= this.redisTemplate.opsForGeo().distance(CITY,member1,member2, RedisGeoCommands.DistanceUnit.KILOMETERS); return distance; } /** * 通过经度,纬度查找附近的 * 北京王府井位置116.418017,39.914402 */ @GetMapping(value = "/georadius") public GeoResults radiusByxy() { //这个坐标是北京王府井位置 Circle circle = new Circle(116.418017, 39.914402, Metrics.KILOMETERS.getMultiplier()); //返回50条 RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(50); GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults= this.redisTemplate.opsForGeo().radius(CITY,circle, args); return geoResults; } /** * 通过地方查找附近 */ @GetMapping(value = "/georadiusByMember") public GeoResults radiusByMember() { String member="天安门"; //返回50条 RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(50); //半径10公里内 Distance distance=new Distance(10, Metrics.KILOMETERS); GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults= this.redisTemplate.opsForGeo().radius(CITY,member, distance,args); return geoResults; }
}