Java 实现 Redis中的GEO数据结构
LBS (基于位置信息服务(Location-Based Service,LBS))应用访问的数据是和人
或物关联的一组经纬度信息,而且要能查询相邻的经纬度范围,GEO 就非常适合应用在
LBS 服务的场景中
java
import java.util.ArrayList;
import java.util.List;
// 定义一个表示地理位置的类,用于存储地理位置的相关信息
public class GeoLocation {
// 成员名称,用于标识这个地理位置,比如某个地点的名称
private String member;
// 地理位置的经度
private double longitude;
// 地理位置的纬度
private double latitude;
// 构造函数,用于初始化 GeoLocation 对象
// 参数 member 为成员名称,longitude 为经度,latitude 为纬度
public GeoLocation(String member, double longitude, double latitude) {
this.member = member;
this.longitude = longitude;
this.latitude = latitude;
}
// 获取成员名称的方法
public String getMember() {
return member;
}
// 获取经度的方法
public double getLongitude() {
return longitude;
}
// 获取纬度的方法
public double getLatitude() {
return latitude;
}
}
// 定义一个用于计算两个地理位置之间距离的工具类
class GeoDistanceCalculator {
// 地球的平均半径,单位为千米,在计算距离时会用到
private static final int EARTH_RADIUS = 6371;
// 静态方法,使用 Haversine 公式计算两个经纬度之间的距离
// 参数 lat1 和 lon1 是第一个地点的纬度和经度
// 参数 lat2 和 lon2 是第二个地点的纬度和经度
public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
// 计算两个纬度之间差值的弧度
double dLat = Math.toRadians(lat2 - lat1);
// 计算两个经度之间差值的弧度
double dLon = Math.toRadians(lon2 - lon1);
// 将第一个地点的纬度转换为弧度
lat1 = Math.toRadians(lat1);
// 将第二个地点的纬度转换为弧度
lat2 = Math.toRadians(lat2);
// Haversine 公式的一部分,用于计算球面距离
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
// 计算反三角函数,得到球面距离的弧度值
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
// 最终距离等于地球半径乘以弧度值
return EARTH_RADIUS * c;
}
}
// 定义一个模拟 Redis Geo 数据结构的类,用于管理地理位置信息
class GeoDataStructure {
// 用于存储所有地理位置信息的列表
private List<GeoLocation> locations;
// 构造函数,初始化存储地理位置信息的列表
public GeoDataStructure() {
this.locations = new ArrayList<>();
}
// 向数据结构中添加一个新的地理位置信息
// 参数 member 为成员名称,longitude 为经度,latitude 为纬度
public void addLocation(String member, double longitude, double latitude) {
// 创建一个新的 GeoLocation 对象
GeoLocation location = new GeoLocation(member, longitude, latitude);
// 将新的地理位置信息添加到列表中
locations.add(location);
}
// 根据给定的经纬度和距离范围,查找附近的成员
// 参数 longitude 和 latitude 是查询的中心点的经度和纬度
// 参数 distance 是查询的距离范围,单位为千米
public List<String> findNearbyMembers(double longitude, double latitude, double distance) {
// 用于存储附近成员名称的列表
List<String> nearbyMembers = new ArrayList<>();
// 遍历所有存储的地理位置信息
for (GeoLocation location : locations) {
// 计算当前地理位置与查询中心点之间的距离
double dist = GeoDistanceCalculator.calculateDistance(latitude, longitude,
location.getLatitude(), location.getLongitude());
// 如果计算出的距离小于等于查询的距离范围
if (dist <= distance) {
// 将该地理位置的成员名称添加到附近成员列表中
nearbyMembers.add(location.getMember());
}
}
// 返回附近成员列表
return nearbyMembers;
}
}
// 测试 GeoDataStructure 类功能的测试类
class GeoDataStructureTest {
public static void main(String[] args) {
// 创建一个 GeoDataStructure 对象,用于管理地理位置信息
GeoDataStructure geoData = new GeoDataStructure();
// 向 GeoDataStructure 对象中添加一些地理位置信息
// 这里添加了三个地点,分别是 place1、place2 和 place3
geoData.addLocation("place1", 116.4074, 39.9042);
geoData.addLocation("place2", 121.4737, 31.2304);
geoData.addLocation("place3", 113.2644, 23.1291);
// 定义查询的中心点的经度
double targetLongitude = 116.4074;
// 定义查询的中心点的纬度
double targetLatitude = 39.9042;
// 定义查询的距离范围,单位为千米
double searchDistance = 10000;
// 调用 findNearbyMembers 方法,查找附近的成员
List<String> nearbyMembers = geoData.findNearbyMembers(targetLongitude, targetLatitude, searchDistance);
// 输出附近的成员名称列表
System.out.println("附近的地点: " + nearbyMembers);
}
}