# 使用如下SQL即可
select taxi from position where x0-r < x < x0 + r and y0-r < y < y0+r
经线和纬线
经度、纬度
在一定误差范围内,通常情况下,经纬线和米的换算为:经度或者纬度0.00001度,约等于1米。以下表格列出更细致的换算关系
GeoHash将二维的经纬度转换成字符串,比如下图展示了北京9个区域的GeoHash字符串,分别是WX4ER,WX4G2、WX4G3等等,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存,比如左上角这个区域内的用户不断发送位置信息请求餐馆数据,由于这些用户的GeoHash字符串都是WX4ER,所以可以把WX4ER当作key,把该区域的餐馆信息当作value来进行缓存,而如果不使用GeoHash的话,由于区域内的用户传来的经纬度是各不相同的,很难做缓存
字符串越长,表示的范围越精确。如图所示,5位的编码能表示10平方千米范围的矩形区域,而6位编码能表示更精细的区域(约0.34平方千米)
字符串相似的表示距离相近(特殊情况后文阐述),这样可以利用字符串的前缀匹配来查询附近的POI信息。如下两个图所示,一个在城区,一个在郊区,城区的GeoHash字符串之间比较相似,郊区的字符串之间也比较相似,而城区和郊区的GeoHash字符串相似程度要低些
通过上面的介绍我们知道了GeoHash就是一种将经纬度转换成字符串的方法,并且使得在大部分情况下,字符串前缀匹配越多的距离越近,回到我们的案例,根据所在位置查询来查询附近餐馆时,只需要将所在位置经纬度转换成GeoHash字符串,并与各个餐馆的GeoHash字符串进行前缀匹配,匹配越多的距离越近
目前Geohash使用的精度说明如下:
根据Geohash的编码规则将经纬度分解到二进制,结合地理常识,中心网格在南北(上下)方向上体现为纬度的变化,往北则维度的二进制加1,往南则维度的二进制减1,在东西(左右)方向上体现为经度的变化,往东则经度的二进制加1,往西则减1,可以计算出上下左右四个网格经纬度的二进制编码,再将加减得出的经纬度两两组合,计算出左上、左下、右上和右下四个网格的经纬度二进制编码,从而就可以根据Geohash的编码规则计算出周围八个网格的字符串
有效的经度介于-180度至180度之间
有效的纬度介于-85.05112878度至85.05112878度之间
# 当给定的经纬度超出上述合法范围时,会返回error
127.0.0.1:6379> geoadd city 89.0 99 nanji
(error) ERR invalid longitude,latitude pair 89.000000,99.000000
127.0.0.1:6379> geoadd city 116.41667 39.91667 beijing 121.43333 34.50000 shanghai 117.20000 39.13333 tianjin
(integer) 3
127.0.0.1:6379> geoadd city 116.41667 39.91667 beijing 121.43333 34.50000 shanghai 117.20000 39.13333 tianjin
(integer) 3
127.0.0.1:6379> geopos city beijing nanjing
1) 1) "116.41667157411575317"
2) "39.91667095273589183"
2) (nil)
127.0.0.1:6379> geoadd city 116.41667 39.91667 beijing 121.43333 34.50000 shanghai 117.20000 39.13333 tianjin
(integer) 3
127.0.0.1:6379> geodist city beijing shanghai
"748346.9287"
127.0.0.1:6379> geodist city beijing shanghai km
"748.3469"
127.0.0.1:6379> geohash city beijing
1) "wx4g14s53n0"
WITHDIST: 在返回位置元素的同时,将位置元素与中心之间的距离也一并返回。距离的单位和用户给定的范围单位保持一致
WITHCOORD: 将位置元素的经度和维度也一并返回
WITHHASH: 以52位有符号整数的形式,返回位置元素经过原始geohash编码的有序集合分值这个选项主要用于底层应用或者调试,实际中的作用并不大
COUNT限定返回的记录数
GEORADIUS city 116.418017 39.914402 10 km withdist withcoord count 10 withhash desc
127.0.0.1:6379> GEORADIUS city 116.418017 39.914402 10 km withdist withcoord count 10 withhash desc
1) 1) "beijing"
2) "0.2772"
3) (integer) 4069885649163649
4) 1) "116.41667157411575317"
2) "39.91667095273589183"
127.0.0.1:6379> geoadd city 116.408 39.904 beijing 116.298 39.959 haidian 116.443 39.922 chaoyang 121.445 31.213 shanghai 121.23 31.07 minhang 117.246 39.117 tianjin
(integer) 6
127.0.0.1:6379> GEORADIUSBYMEMBER city beijing 500 km withdist asc
1) 1) "beijing"
2) "0.0000"
2) 1) "chaoyang"
2) "3.5948"
3) 1) "haidian"
2) "11.2004"
4) 1) "tianjin"
2) "113.2837"
@RestController
public class GeoController {
public static final String CITY ="city";
@Autowired
private RedisTemplate redisTemplate;
@ApiOperation("新增天安门故宫长城经纬度")
@RequestMapping(value = "/geoadd",method = RequestMethod.POST)
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();
}
@ApiOperation("获取地理位置的坐标")
@RequestMapping(value = "/geopos",method = RequestMethod.GET)
public Point position(String member) {
//获取经纬度坐标
List<Point> list= this.redisTemplate.opsForGeo().position(CITY,member);
return list.get(0);
}
@ApiOperation("geohash算法生成的base32编码值")
@RequestMapping(value = "/geohash",method = RequestMethod.GET)
public String hash(String member) {
//geohash算法生成的base32编码值
List<String> list= this.redisTemplate.opsForGeo().hash(CITY,member);
return list.get(0);
}
@ApiOperation("计算两个位置之间的距离")
@RequestMapping(value = "/geodist",method = RequestMethod.GET)
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,这里为了方便讲课,故意写死
*/
@ApiOperation("通过经度,纬度查找附近的")
@RequestMapping(value = "/georadius",method = RequestMethod.GET)
public GeoResults radiusByxy() {
//这个坐标是北京王府井位置
Circle circle = new Circle(116.418017, 39.914402, Metrics.MILES.getMultiplier());
//返回50条
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(10);
GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults= this.redisTemplate.opsForGeo().radius(CITY,circle, args);
return geoResults;
}
/**
* 通过地方查找附近
*/
@ApiOperation("通过地方查找附近")
@RequestMapping(value = "/georadiusByMember",method = RequestMethod.GET)
public GeoResults radiusByMember() {
String member="天安门";
//返回50条
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(10);
//RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortDescending().limit(10);
//半径10公里内
Distance distance=new Distance(10, Metrics.KILOMETERS);
GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults= this.redisTemplate.opsForGeo().radius(CITY,member, distance,args);
return geoResults;
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。