本文介绍了Redis数据类型GEO的相关概念、命令、推荐使用场景和应用代码举例。
目录
GEO:概念与相关命令
概念
GEO可以理解为是一种特殊的Zset,只是其分数值由经度和纬度共同决定。
GEO常用于地理位置相关的操作,如计算两点之间的距离、查找附近的成员等。
粗略技术原理:geoadd 命令 -- Redis中国用户组(CRUG),更详细的内容可自行了解。
命令
- GEOADD:将一个或多个成员的地理位置添加到指定的GEO数据结构中。
- GEODIST:计算两个成员之间的地理距离。
- GEOPOS:获取一个或多个成员的地理位置坐标。
- GEORADIUS:根据给定的地理位置和半径范围,查找附近的成员。
- GEORADIUSBYMEMBER:根据给定的成员和半径范围,查找附近的成员。
- GEOREMOVE:从GEO数据结构中移除一个或多个成员。
- GEORADIUS_WITHDIST:查找附近的成员,并返回距离信息。
- GEORADIUS_WITHCOORD:查找附近的成员,并返回坐标信息。
(仅列出常用命令,其他命令可自行到官网Commands | Redis查看)
推荐使用场景
地理位置相关的功能主要可归类为这两类:
- 地理位置服务:
- 构建地图:如定位服务、附近的商店和酒店、美团找单车等。实现思路:将所有的商店存入key为商店的geo,将酒店存入key为商店的geo,将单车位置存入key为单车的geo,当需要获得指定半径内的对应物品时,就使用GEORADIUS命令指定自己位置、半径以及需要查询的key,即可获得对应的其他所需物品位置。
- 位置分析:如根据地图进行路线生成、寻找热门商业圈等。实现思路:导航功能一般是直接调用高德地图、百度地图这类api进行导航...导航功能需要坐标点数据(起点、终点坐标、途经点坐标)和道路数据(道路网络连接关系、路段信息、交通信息),通常需要使用图数据结构和路径规划算法(如Dijkstra、A*、或者基于实时交通的算法),往往无法单纯使用GEO实现,GEO仅能提供一些辅助功能如根据位置远近推荐目的地。
- 轨迹追踪:
- 位置跟踪:如外卖员位置和预计到达时间等。实现思路:实时获得更新自己、对方位置,获得之间距离;同时配合道路数据进行剩余时间计算。
- 地理围栏:借助位置跟踪,当某个对象进入或离开指定区域时触发事件。实现思路:实时更新位置,当目标与目的地达到一定距离以内或以外则触发事件。
应用代码举例
建议使用管道输入中国大多数城市的坐标,坐标文件已绑定。
此处仅演示各类命令使用:(建议使用StringRadisTemplate,避免序列化问题)
package com.example.redis_study.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @auther 齿轮
* @create 2023-09-05-20:36
*/
@RestController
public class GEOController {
public static final String key = "china";
@Autowired
StringRedisTemplate redisTemplate;
//GEOADD
@PostMapping("/GEOADD")
public void AddCity(@RequestParam double longitude, @RequestParam double latitude, @RequestParam String city) {
Map<String, Point> map = new HashMap<>();
map.put(city, new Point(longitude, latitude));
System.out.println(map);
redisTemplate.opsForGeo().add(key, map);
}
//GEOOPS
@GetMapping("/GEOOPS")
public String getCity(@RequestParam String city) {
List position = redisTemplate.opsForGeo().position(key, city);
return position.toString();
}
//GEODIST
@GetMapping("/GEODIST")
public String getDist(@RequestParam String city1, @RequestParam String city2) {
Distance distance = redisTemplate.opsForGeo().distance(key, city1, city2, Metrics.KILOMETERS);
return distance.toString();
}
//GEORADIUS
@GetMapping("/GEORADIUS")
public Object getCitiesByRadius(@RequestParam double longitude, @RequestParam double latitude, @RequestParam double radius, @RequestParam int count) {
Circle circle = new Circle(new Point(longitude, latitude), new Distance(radius, Metrics.KILOMETERS));//指定自己坐标
//设置返回条件
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().
includeDistance().
includeCoordinates().
sortAscending().
limit(count);
//获得返回结果
GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(key, circle, args);
return results;
}
//GEORADIUSBYMEMBER
@GetMapping("/GEORADIUSBYMEMBER")
public Object getCitiesByRadiusAndOtherCity(@RequestParam String city, @RequestParam double radius, @RequestParam int count) {
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().
includeDistance().
includeCoordinates().
sortAscending().
limit(count);
GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(key, city, new Distance(radius, Metrics.KILOMETERS), args);
return results;
}
}
运行结果: