Commit 5eb60d24 by chamberone

feat: 算法添加路网支持

1 parent 27bcdacf
package com.dituhui.pea.dispatch.common; package com.dituhui.pea.dispatch.common;
import com.dituhui.pea.dispatch.pojo.Location; import com.dituhui.pea.dispatch.pojo.Location;
import com.dituhui.pea.dispatch.utils.RoadDistanceUtils;
import com.dituhui.pea.dispatch.utils.RoadDistanceUtils.Distance;
import org.gavaghan.geodesy.Ellipsoid; import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GeodeticCalculator; import org.gavaghan.geodesy.GeodeticCalculator;
import org.gavaghan.geodesy.GeodeticCurve; import org.gavaghan.geodesy.GeodeticCurve;
...@@ -62,9 +65,10 @@ public class GeoDistanceCalculator { ...@@ -62,9 +65,10 @@ public class GeoDistanceCalculator {
from -> toLocations.stream().collect(Collectors.toMap( from -> toLocations.stream().collect(Collectors.toMap(
Function.identity(), Function.identity(),
to -> { to -> {
long distance = calculateDistance(from, to); Distance distance = RoadDistanceUtils.getDistance(from, to);
long duration = Math.round(distance / avgRate); long path = (long) distance.getDis();
return new Pair(distance, duration); long time = distance.getTime();
return new Pair(path, time);
} }
)) ))
)); ));
......
...@@ -12,6 +12,8 @@ import com.dituhui.pea.dispatch.pojo.*; ...@@ -12,6 +12,8 @@ import com.dituhui.pea.dispatch.pojo.*;
import com.dituhui.pea.dispatch.service.ExtractService; import com.dituhui.pea.dispatch.service.ExtractService;
import com.dituhui.pea.dispatch.service.SolveService; import com.dituhui.pea.dispatch.service.SolveService;
import com.dituhui.pea.dispatch.utils.DispatchSolutionUtils; import com.dituhui.pea.dispatch.utils.DispatchSolutionUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.optaplanner.core.api.solver.Solver; import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory; import org.optaplanner.core.api.solver.SolverFactory;
...@@ -123,7 +125,7 @@ public class SolveServiceImpl implements SolveService { ...@@ -123,7 +125,7 @@ public class SolveServiceImpl implements SolveService {
} }
// 40分钟兜低(技能未能正确匹配原因) // 40分钟兜低(技能未能正确匹配原因) FIXME 需要跟客户沟通
if (null == order.getTakeTime()) { if (null == order.getTakeTime()) {
order.setTakeTime(40); order.setTakeTime(40);
} }
...@@ -141,14 +143,12 @@ public class SolveServiceImpl implements SolveService { ...@@ -141,14 +143,12 @@ public class SolveServiceImpl implements SolveService {
log.info("组织问题对象, customer-list, groupId:{}, batchNo:{}, customer-list:{}", groupId, batchNo, customerList.size()); log.info("组织问题对象, customer-list, groupId:{}, batchNo:{}, customer-list:{}", groupId, batchNo, customerList.size());
// depotlist 技术员中收点列表
ArrayList<Depot> depotList = new ArrayList<Depot>();
// technicianList // technicianList
ArrayList<Technician> technicianList = new ArrayList<>(); ArrayList<Technician> technicianList = new ArrayList<>();
dispatchEngineerRepo.findByGroupIdAndBatchNo(groupId, batchNo).forEach(engineer -> { dispatchEngineerRepo.findByGroupIdAndBatchNo(groupId, batchNo).forEach(engineer -> {
Location location = new Location(engineer.getId(), engineer.getEngineerCode(), "中心点", Double.parseDouble(engineer.getX()), Double.parseDouble(engineer.getY())); Location location = new Location(engineer.getId(), engineer.getEngineerCode(), "中心点", Double.parseDouble(engineer.getX()), Double.parseDouble(engineer.getY()));
Depot depot = new Depot(engineer.getId(), engineer.getEngineerCode(), location, 60 * 8, 60 * 18); // Depot depot = new Depot(engineer.getId(), engineer.getEngineerCode(), location, 60 * 8, 60 * 18);
depotList.add(depot); // depotList.add(depot);
// log.debug("组织问题对象, technicianList groupId:{}, batchNo:{}, engineer-code:{}", groupId, batchNo, engineer.getEngineerCode()); // log.debug("组织问题对象, technicianList groupId:{}, batchNo:{}, engineer-code:{}", groupId, batchNo, engineer.getEngineerCode());
List<String> skillList = queryEngineerSkills(engineer.getEngineerCode()); List<String> skillList = queryEngineerSkills(engineer.getEngineerCode());
...@@ -162,20 +162,24 @@ public class SolveServiceImpl implements SolveService { ...@@ -162,20 +162,24 @@ public class SolveServiceImpl implements SolveService {
preferedLoctionDistanceMap.put(customer.getCode(), distance); preferedLoctionDistanceMap.put(customer.getCode(), distance);
}); });
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(), engineer.getMaxMinute(), engineer.getMaxDistance() * 1000, depot, 60 * 8, 60 * 18, Set.copyOf(skillList), preferedLoctionDistanceMap); Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(), engineer.getMaxMinute(), engineer.getMaxDistance() * 1000, oneDepot, 60 * 8, 60 * 18, Set.copyOf(skillList), preferedLoctionDistanceMap);
technicianList.add(vehicle); technicianList.add(vehicle);
}); });
log.info("组织问题对象, depotList-list, groupId:{}, batchNo:{}, depotList-list:{}", groupId, batchNo, depotList.size()); log.info("组织问题对象, depotList-list, groupId:{}, batchNo:{}", groupId, batchNo);
log.info("组织问题对象, technician-list, groupId:{}, batchNo:{}, technician-list:{}", groupId, batchNo, technicianList.size()); log.info("组织问题对象, technician-list, groupId:{}, batchNo:{}, technician-list:{}", groupId, batchNo, technicianList.size());
//locationlist // locationlist 起点+订单地点
List<Location> locationList = Stream.concat(depotList.stream().map(Depot::getLocation), customerList.stream().map(Customer::getLocation)).collect(Collectors.toList()); List<Location> locationList = Stream.concat(Lists.newArrayList(oneDepot).stream().map(Depot::getLocation), customerList.stream().map(Customer::getLocation)).collect(Collectors.toList());
DispatchSolution solution = new DispatchSolution(groupId, batchNo, locationList, oneDepot, technicianList, customerList); DispatchSolution solution = new DispatchSolution(groupId, batchNo, locationList, oneDepot, technicianList, customerList);
// path 路网数据初始化,FIXME 需要专门路网数据缓存库
long time1 = System.currentTimeMillis();
distanceCalculator.initDistanceMaps(locationList); distanceCalculator.initDistanceMaps(locationList);
long time2 = System.currentTimeMillis();
log.info("组织问题对象 done, groupId:{}, batchNo:{}, technician-size:{}, customer-size:{}, location-size:{}", groupId, batchNo, technicianList.size(), customerList.size(), locationList.size()); log.info("组织问题对象 done, groupId:{}, batchNo:{}, technician-size:{}, customer-size:{}, location-size:{}, 路网耗时path:{}ms", groupId, batchNo, technicianList.size(), customerList.size(), locationList.size(), time2-time1);
return solution; return solution;
......
package com.dituhui.pea.dispatch.utils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.dituhui.pea.dispatch.pojo.Location;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import lombok.Data;
/**
* 路网组件<br>
* TODO 需要做成分布式缓存模式,这里会造成内存问题<br>
* TODO 调用方式需要改成批量调用方式
*
* @author gpzhang
*
*/
public class RoadDistanceUtils {
public static String URL = "https://api.map.baidu.com/routematrix/v2/riding?";
public static String AK = "doR30pE7R0I7ivGLwMpkpsTT4bos9Akg";
/**
* 格式 x1,y1;x2,y2
*/
private static Map<String, Distance> distanceCache = Maps.newHashMap();
private static Gson gson = new Gson();
/**
* 获取路网距离和时间<br>
* TODO 需要做成分布式缓存模式,这里会造成内存问题<br>
* TODO 调用方式需要改成批量调用方式
*
* @param from
* @param to
* @return
*/
public static Distance getDistance(Location from, Location to) {
try {
String key = from.getLongitude() + "," + from.getLatitude() + ";" + to.getLongitude() + ","
+ to.getLatitude();
Distance distance = distanceCache.get(key);
if (null == distance) {
distance = getDistance(from.getLatitude() + "," + from.getLongitude(),
to.getLatitude() + "," + to.getLongitude());
if(null == distance) {
Distance dis = new Distance();
return dis;
}else {
distanceCache.put(key, distance);
}
return distance;
} else {
return distance;
}
} catch (Exception e) {
Distance dis = new Distance();
return dis;
}
}
private static Distance getDistance(String yx1, String yx2) throws Exception {
Map<String, String> params = new HashMap<String, String>();
params.put("origins", yx1);
params.put("destinations", yx2);
params.put("ak", AK);
params.put("riding_type", "1");// 电动自行车
params.put("coord_type", "gcj02");
String text = requestGetAK(URL, params);
BDResult webResult = gson.fromJson(text, BDResult.class);
float dis = webResult.getResult().get(0).getDistance().getValue() / 1000F;
int time = webResult.getResult().get(0).getDuration().getValue();
Distance d = new Distance();
d.setDis(dis);
d.setTime(time);
return d;
}
/**
* 默认ak 选择了ak,使用IP白名单校验: 根据您选择的AK已为您生成调用代码 检测到您当前的ak设置了IP白名单校验
* 您的IP白名单中的IP非公网IP,请设置为公网IP,否则将请求失败 请在IP地址为xxxxxxx的计算发起请求,否则将请求失败
*/
public static String requestGetAK(String strUrl, Map<String, String> param) throws Exception {
if (strUrl == null || strUrl.length() <= 0 || param == null || param.size() <= 0) {
return "";
}
StringBuffer queryString = new StringBuffer();
queryString.append(strUrl);
for (Map.Entry<?, ?> pair : param.entrySet()) {
queryString.append(pair.getKey() + "=");
// 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可
queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
java.net.URL url = new URL(queryString.toString());
URLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.connect();
InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
BufferedReader reader = new BufferedReader(isr);
StringBuffer buffer = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
reader.close();
isr.close();
return (buffer.toString());
}
@Data
public static class Distance {
float dis;
int time;
}
@Data
static class BDResult {
List<BDDistance> result;
}
@Data
static class BDDistance {
Dis distance;
Dur duration;
}
@Data
static class Dis {
Float value;
}
@Data
static class Dur {
Integer value;
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!