司机端的小程序可以实时上传定位坐标,并且Redis中保存了司机的GEO缓存和上线缓存。创建订单的过程中,我们要查找附近适合接单的司机。如果有这样的司机,代驾系统才会创建订单,否则就拒绝创建订单。
既然要计算方圆几公里以内的司机,我们就得用上Redis的GEO计算,要写的代码如下。
@Service
public class DriverLocationServiceImpl implements DriverLocationService {
……
@Override
public ArrayList searchBefittingDriverAboutOrder(double startPlaceLatitude,
double startPlaceLongitude,
double endPlaceLatitude,
double endPlaceLongitude,
double mileage) {
//搜索订单起始点5公里以内的司机
Point point = new Point(startPlaceLongitude, startPlaceLatitude);
//设置GEO距离单位为千米
Metric metric = RedisGeoCommands.DistanceUnit.KILOMETERS;
Distance distance = new Distance(5, metric);
Circle circle = new Circle(point, distance);
//创建GEO参数
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands
.GeoRadiusCommandArgs
.newGeoRadiusArgs()
.includeDistance() //结果中包含距离
.includeCoordinates() //结果中包含坐标
.sortAscending(); //升序排列
//执行GEO计算,获得查询结果
GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo()
.radius("driver_location", circle, args);
ArrayList list = new ArrayList(); //需要通知的司机列表
if (radius != null) {
Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = radius.iterator();
while (iterator.hasNext()) {
GeoResult<RedisGeoCommands.GeoLocation<String>> result = iterator.next();
RedisGeoCommands.GeoLocation<String> content = result.getContent();
String driverId = content.getName();
//Point memberPoint = content.getPoint(); // 对应的经纬度坐标
double dist = result.getDistance().getValue(); // 距离中心点的距离
//排查掉不在线的司机
if (!redisTemplate.hasKey("driver_online#" + driverId)) {
continue;
}
//查找该司机的在线缓存
Object obj = redisTemplate.opsForValue().get("driver_online#" + driverId);
//如果查找的那一刻,缓存超时被置空,那么就忽略该司机
if (obj == null) {
continue;
}
String value = obj.toString();
String[] temp = value.split("#");
int rangeDistance = Integer.parseInt(temp[0]);
int orderDistance = Integer.parseInt(temp[1]);
String orientation = temp[2];
//判断是否符合接单范围
boolean bool_1 = dist <= rangeDistance;
//判断订单里程是否符合
boolean bool_2 = false;
if (orderDistance == 0) {
bool_2 = true;
} else if (orderDistance == 5 && mileage > 0 && mileage <= 5) {
bool_2 = true;
} else if (orderDistance == 10 && mileage > 5 && mileage <= 10) {
bool_2 = true;
} else if (orderDistance == 15 && mileage > 10 && mileage <= 15) {
bool_2 = true;
} else if (orderDistance == 30 && mileage > 15 && mileage <= 30) {
bool_2 = true;
}
//判断定向接单是否符合
boolean bool_3 = false;
if (!orientation.equals("none")) {
double orientationLatitude = Double.parseDouble(orientation.split(",")[0]);
double orientationLongitude = Double.parseDouble(orientation.split(",")[1]);
//把定向点的火星坐标转换成GPS坐标
double[] location = CoordinateTransform.transformGCJ02ToWGS84(orientationLongitude, orientationLatitude);
GlobalCoordinates point_1 = new GlobalCoordinates(location[1], location[0]);
//把订单终点的火星坐标转换成GPS坐标
location = CoordinateTransform.transformGCJ02ToWGS84(endPlaceLongitude, endPlaceLatitude);
GlobalCoordinates point_2 = new GlobalCoordinates(location[1], location[0]);
//这里不需要Redis的GEO计算,直接用封装函数计算两个GPS坐标之间的距离
GeodeticCurve geoCurve = new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.WGS84, point_1, point_2);
//如果定向点距离订单终点距离在3公里以内,说明这个订单和司机定向点是顺路的
if (geoCurve.getEllipsoidalDistance() <= 3000) {
bool_3 = true;
}
} else {
bool_3 = true;
}
//匹配接单条件
if (bool_1 && bool_2 && bool_3) {
HashMap map = new HashMap() {{
put("driverId", driverId);
put("distance", dist);
}};
list.add(map); //把该司机添加到需要通知的列表中
}
}
}
return list;
}
}