Redis GEO
概述
Redis的GEO操作是一種基于地理位置信息進(jìn)行操作的功能。它使用經(jīng)度和緯度坐標(biāo)來表示地理位置,支持存儲(chǔ)地理位置信息用來實(shí)現(xiàn)諸如附近位置、搖一搖這類依賴于地理位置信息的功能。
應(yīng)用場(chǎng)景
地理圍欄:通過設(shè)置地理位置的經(jīng)緯度信息,可以將用戶或者車輛等實(shí)體綁定在地理圍欄內(nèi),當(dāng)實(shí)體進(jìn)出圍欄時(shí),可以觸發(fā)相應(yīng)的事件。
附近的人:在類似于約會(huì)、社交、旅游等場(chǎng)景下,可以通過Redis GEO快速查詢周圍的人員或景點(diǎn)。
配送服務(wù):通過獲取配送地址的經(jīng)緯度信息,可以找到距離最近的配送員或倉(cāng)庫(kù),并對(duì)訂單進(jìn)行分配。
地址查找:在地圖應(yīng)用中,可以通過Redis GEO快速查詢某個(gè)地址周圍的商家或服務(wù)設(shè)施。
動(dòng)態(tài)信息:在滴滴、Uber等打車應(yīng)用中,可以實(shí)時(shí)更新車輛的位置信息,以提供更加準(zhǔn)確的車輛推薦和路線規(guī)劃服務(wù)
Redis GEO命令
1.GEOADD添加位置信息
將一個(gè)或多個(gè)指定的地理位置(經(jīng)度、緯度、名稱)添加到指定的鍵中。
GEOADD key longitude latitude member [longitude latitude member ...]
添加一個(gè)名為cities的鍵,并將北京、上海、廣州三個(gè)城市的經(jīng)緯度和名稱添加到該鍵中
GEOADD cities 116.4074 39.9042 Beijing 121.4737 31.2304 Shanghai 113.2644 23.1291 Guangzhou
GEOADD命令對(duì)于經(jīng)緯度是有要求的:
有效的經(jīng)度從-180度到180度
有效的緯度從-85.05112878度到85.05112878度
2.GEODIST查詢距離
返回兩個(gè)位置之間的距離??梢赃x擇以米或千米為單位。
GEODIST key member1 member2 [unit]
可選參數(shù)unit用于指定計(jì)算距離時(shí)的單位:
m 表示單位為米
km 表示單位為千米
mi 表示單位為英里
ft 表示單位為英尺
查詢北京和上海之間的距離,單位為千米
GEODIST cities Beijing Shanghai km
3.GEOHASH獲取指定位置的Geohash值
返回一個(gè)或多個(gè)位置的Geohash值,該值用于對(duì)地理位置進(jìn)行更快速的范圍查找。
GEOHASH key member [member ...]
獲取北京的Geohash值
GEOHASH cities Beijing
4.GEOPOS查詢地理位置坐標(biāo)
返回一個(gè)或多個(gè)位置的經(jīng)度和緯度。
GEOPOS key member [member ...]
查詢廣州的經(jīng)緯度
GEOPOS cities Guangzhou
5.GEORADIUS查找指定范圍內(nèi)的元素
查詢給定坐標(biāo)范圍內(nèi)的所有元素??梢酝ㄟ^設(shè)置排序選項(xiàng)來獲取按距離排序的結(jié)果。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]
查找距離北京1000公里以內(nèi)的城市,并按距離升序排列
GEORADIUS cities 116.4074 39.9042 1000 km ASC
6.GEORADIUSBYMEMBER查詢給定成員周圍的所有元素
查詢給定成員周圍的所有元素??梢酝ㄟ^設(shè)置排序選項(xiàng)來獲取按距離排序的結(jié)果。
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]
查找距離上海最近的城市,并返回它們的名稱和距離
GEORADIUSBYMEMBER cities Shanghai 100 km WITHDIST
7.ZREM刪除成員
從指定鍵中刪除一個(gè)或多個(gè)成員。
ZREM key member [member ...]
從cities鍵中刪除廣州
ZREM cities Guangzhou
GEO命令演示
# 添加位置信息
本機(jī):0>geoadd user:location 121.48941 31.40527 'shagnhai'
"1"
# 添加多個(gè)位置信息
本機(jī):0>geoadd user:location 121.47941 31.41527 'shanghai1' 121.47941 31.43527 'shagnhai2' 121.47941 31.40527 'shagnhai3'
"3"
# 計(jì)算距離,單位有m km ft(英尺) mi(英里)
# 計(jì)算兩點(diǎn)間的距離,單位m
本機(jī):0>geodist user:location shanghai shanghai1 m
"1462.1834"
# 千米:km
本機(jī):0>geodist user:location shanghai shanghai1 km
"1.4622"
# geohash 返回一個(gè)或多個(gè)位置元素的geohash,保存Redis中是用geohash位置52點(diǎn)整數(shù)編碼
# geohash 將二維經(jīng)緯度轉(zhuǎn)換成字符串,每個(gè)字符串代表一個(gè)矩形區(qū)域,該矩形區(qū)域內(nèi)的經(jīng)緯度點(diǎn)都共享一個(gè)相同的geohash字符串。
本機(jī):0>geohash user:location shanghai shanghai1
1) "wtw6st1uuq0"
2) "wtw6sqfx5q0"
# geopos 從key里返回指定成員的位置信息
本機(jī):0>geopos user:location shanghai shanghai1
1) 1) "121.48941010236740112"
2) "31.40526993848380499"
2) 1) "121.47941082715988159"
2) "31.41526941345740198"
# georadius:給定經(jīng)緯度為中心,返回鍵包含的位置元素中,與中心的距離不超過給定最大距離的所有位置元素
# 范圍單位:m km mi ft
# withcoord:將位置元素的經(jīng)緯度一并返回
本機(jī):0>georadius user:location 121.48941 31.40527 3000 m withcoord
1) 1) "shagnhai3"
2) 1) "121.47941082715988159"
2) "31.40526993848380499"
2) 1) "shanghai1"
2) 1) "121.47941082715988159"
2) "31.41526941345740198"
3) 1) "shanghai"
2) 1) "121.48941010236740112"
2) "31.40526993848380499"
# withdist:返回位置元素的同時(shí),將位置元素與中心點(diǎn)間的距離一并返回
本機(jī):0>georadius user:location 121.48941 31.40527 3000 m withdist
1) 1) "shagnhai3"
2) "949.2411"
2) 1) "shanghai1"
2) "1462.1719"
3) 1) "shanghai"
2) "0.0119"
# asc:根據(jù)中心位置,按照從近到遠(yuǎn)的方式返回位置元素
本機(jī):0>georadius user:location 121.48941 31.40527 3000 m withdist asc
1) 1) "shanghai"
2) "0.0119"
2) 1) "shagnhai3"
2) "949.2411"
3) 1) "shanghai1"
2) "1462.1719"
# desc: 根據(jù)中心位置,按照從遠(yuǎn)到近的方式返回位置元素
本機(jī):0>georadius user:location 121.48941 31.40527 3000 m withdist desc
1) 1) "shanghai1"
2) "1462.1719"
2) 1) "shagnhai3"
2) "949.2411"
3) 1) "shanghai"
2) "0.0119"
# count:獲取指定數(shù)量的元素
本機(jī):0>georadius user:location 121.48941 31.40527 3000 m withdist desc count 2
1) 1) "shanghai1"
2) "1462.1719"
2) 1) "shagnhai3"
2) "949.2411"
# georadiusbymember:和georadius命令類似,都可以找出指定位置范圍內(nèi)的元素,但是georadiusbymember的中心點(diǎn)是由給定位置元素決定的,而不像georadius使用經(jīng)緯度決定中心點(diǎn)
本機(jī):0>georadiusbymember user:location shanghai 3 km
1) "shagnhai3"
2) "shanghai1"
3) "shanghai"
Redis GEO實(shí)現(xiàn)附近人的功能
基礎(chǔ)類
創(chuàng)建NearMeUserVO
視圖對(duì)象,封裝響應(yīng)視圖的基本數(shù)據(jù)信息
@Data
public class NearMeUserVO {
public Integer id;
/**
* 距離
*/
public String distance;
}
創(chuàng)建User
對(duì)象模擬操作用戶
@Data
public class User {
private int id;
}
API接口
創(chuàng)建添加、更新用戶位置信息
與查詢附近用戶信息
的2個(gè)API接口
@RestController
public class NearMeUserController {
@Autowired
private INearMeUserService nearMeUserService;
/**
* 添加、更新坐標(biāo)
*
* @param lon
* @param lat
* @return
*/
@PostMapping("/updateUserLocation")
public String updateUserLocation(@RequestParam Float lon, @RequestParam Float lat) {
nearMeUserService.updateUserLocation(lon, lat);
return "OK";
}
/**
* 獲取附近的人
* 查詢距離指定經(jīng)緯度一定范圍內(nèi)的用戶,并返回結(jié)果列表
*
* @param radius
* @param lon
* @param lat
* @return
*/
@GetMapping("/getNearMe")
public Object nearMe(Integer radius, Float lon, Float lat) {
List<NearMeUserVO> nearMe = nearMeUserService.findNearMe(radius, lon, lat);
return nearMe;
}
}
接口實(shí)現(xiàn)
public interface INearMeUserService {
/**
* 更新坐標(biāo)
*
* @param lon 經(jīng)度
* @param lat 緯度
*/
void updateUserLocation(Float lon, Float lat);
/**
* 獲取附近的人
*
* @param radius 半徑,默認(rèn) 1000m
* @param lon 經(jīng)度
* @param lat 緯度
* @return
*/
List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat);
}
@Service
public class NearMeUserServiceImpl implements INearMeUserService {
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
/**
* 用戶定位信息cKEY
*/
private static final String USER_LOCATION_KEY = "user:location";
public void updateUserLocation(Float lon, Float lat) {
if (lon == null || lat == null) {
throw new RuntimeException("獲取經(jīng)度、維度失敗");
}
// 獲取登錄用戶信息
User user = this.getLoginUser();
// 定義key
String key = "user:location";
// 將用戶地理位置信息存入 Redis
RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands.GeoLocation(user.getId(), new Point(lon, lat));
redisTemplate.opsForGeo().add(key, geoLocation);
}
/**
* 獲取附近的人
*
* @param radius 半徑,默認(rèn) 1000m
* @param lon 經(jīng)度
* @param lat 緯度
* @return
*/
public List<NearMeUserVO> findNearMe(Integer radius, Float lon, Float lat) {
// 獲取登錄用戶信息
User user = this.getLoginUser();
int userId = user.getId();
// 處理半徑,默認(rèn) 1000m
if (radius == null) {
radius = 1000;
}
// 獲取用戶經(jīng)緯度
Point point = null;
if (lon == null || lat == null) {
// 如果經(jīng)緯度沒傳,那么從 Redis 中獲取
List<Point> points = redisTemplate.opsForGeo().position(USER_LOCATION_KEY, userId);
if (points == null || points.isEmpty()) {
throw new RuntimeException("獲取經(jīng)緯度失敗");
}
point = points.get(0);
} else {
point = new Point(lon, lat);
}
// 初始化距離對(duì)象,單位 m
Distance distance = new Distance(radius, RedisGeoCommands.DistanceUnit.METERS);
// 初始化 Geo 命令參數(shù)對(duì)象
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
// 附近的人限制3,包含距離,按由近到遠(yuǎn)排序
args.limit(3).includeDistance().sortAscending();
// 以用戶經(jīng)緯度為圓心,范圍 1000m
Circle circle = new Circle(point, distance);
// 獲取附近的人 GeoLocation 信息
GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResult = redisTemplate.opsForGeo().radius(USER_LOCATION_KEY, circle, args);
// 構(gòu)建有序 Map
Map<Integer, NearMeUserVO> nearMeUserVOMap = Maps.newLinkedHashMap();
// 完善用戶信息
geoResult.forEach(result -> {
RedisGeoCommands.GeoLocation<Object> geoLocation = result.getContent();
// 初始化Vo對(duì)象
NearMeUserVO nearMeUserVO = new NearMeUserVO();
nearMeUserVO.setId((Integer) geoLocation.getName());
// 獲取距離
Double dist = result.getDistance().getValue();
// 四舍五入精確到小數(shù)點(diǎn)后 1 位,方便客戶端顯示
String distanceStr = NumberUtil.round(dist, 1).toString() + "m";
nearMeUserVO.setDistance(distanceStr);
nearMeUserVOMap.put((Integer) geoLocation.getName(), nearMeUserVO);
});
// 附近的人,可進(jìn)一步完善用戶信息
// Integer[] userIds = nearMeUserVOMap.keySet().toArray(new Integer[0]);
ArrayList<NearMeUserVO> nearMeUserVO = Lists.newArrayList(nearMeUserVOMap.values());
return nearMeUserVO;
}
/**
* 模擬獲取真實(shí)登錄用戶信息
*
* @return
*/
public User getLoginUser() {
User user = new User();
user.setId(1);
return user;
}
}
執(zhí)行測(cè)試
訪問執(zhí)行添加用戶地理位置數(shù)據(jù)
接口,得到如下所示數(shù)據(jù):文章來源:http://www.zghlxwxcb.cn/news/detail-481760.html
訪問執(zhí)行獲取附近人
接口,得到如下所示數(shù)據(jù):文章來源地址http://www.zghlxwxcb.cn/news/detail-481760.html
"data": [
{
"id": 1,
"distance": "0.0m"
},
{
"id": 3,
"distance": "949.3m"
},
{
"id": 2,
"distance": "1462.2m"
}
]
到了這里,關(guān)于Redis GEO地理位置信息的應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!