package com.xiaopeng.map.utils;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONObject;
|
import com.xiaopeng.api.dto.operation.LonLatDto;
|
import com.xiaopeng.api.vo.operation.RouteVo;
|
import com.xiaopeng.common.core.utils.HttpUtils;
|
import com.xiaopeng.map.mangodb.service.XpGeoFencingPolygonService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.stereotype.Component;
|
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.concurrent.*;
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
/**
|
* 高德地图的地理编码工具类
|
*/
|
@Slf4j
|
@Component
|
@Lazy
|
public class GDMapGeocodingUtil {
|
|
private String key = "66ae577f63898dae3016d599fda57221";
|
|
|
@Autowired
|
private XpGeoFencingPolygonService xpGeoFencingPolygonService;
|
// 自定义线程池配置
|
private static final int CORE_POOL_SIZE = 5; // 核心线程数
|
private static final int MAX_POOL_SIZE = 10; // 最大线程数
|
private static final long KEEP_ALIVE_TIME = 60L; // 线程空闲存活时间
|
private static final int QUEUE_CAPACITY = 100; // 阻塞队列容量
|
|
// 创建自定义线程池
|
private final ExecutorService customThreadPool = new ThreadPoolExecutor(
|
CORE_POOL_SIZE,
|
MAX_POOL_SIZE,
|
KEEP_ALIVE_TIME,
|
TimeUnit.SECONDS,
|
new LinkedBlockingQueue<>(QUEUE_CAPACITY),
|
new ThreadFactory() {
|
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
@Override
|
public Thread newThread(Runnable r) {
|
Thread t = new Thread(r, "route-check-thread-" + threadNumber.getAndIncrement());
|
t.setDaemon(true); // 设置为守护线程,避免应用关闭时线程未停止
|
return t;
|
}
|
},
|
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:调用线程执行
|
);
|
|
// public RouteVo getDistanceAndTime(LonLatDto start, LonLatDto end, String avoidRoads, String fenceId, Integer routePlanRule) {
|
// RouteVo routeVo = new RouteVo();
|
//
|
// String url = "https://tsapi.amap.com/v1/route/price?key=" + key + "&extensions=all&start=" + start.getLongitude() + "," + start.getLatitude();
|
// HashMap<String, Object> params = new HashMap<>();
|
// params.put("extensions", "all");
|
// params.put("start", start.getLongitude() + "," + start.getLatitude());
|
// params.put("end", end.getLongitude() + "," + end.getLatitude());
|
// params.put("routeUserType", 2);
|
// if (avoidRoads != null && !avoidRoads.isEmpty()) {
|
// params.put("voidPolygons", avoidRoads);
|
// }
|
// if (routePlanRule != null) {
|
// params.put("strategy", routePlanRule);
|
// }
|
//
|
// String post = HttpUtils.post(url, JSON.toJSONString(params));
|
// System.out.println("高德地图返回结果:" + post);
|
//
|
// JSONObject jsonObject = JSON.parseObject(post);
|
// String data = jsonObject.getString("data");
|
// JSONObject jsonObject1 = JSON.parseObject(data);
|
// JSONArray path = jsonObject1.getJSONArray("paths");
|
//
|
// if (path == null || path.isEmpty()) {
|
// return null;
|
// }
|
//
|
// // 使用自定义线程池并行处理每个路径的围栏检查
|
// List<Future<PathCheckResult>> futures = new ArrayList<>();
|
// CountDownLatch latch = new CountDownLatch(path.size()); // 用于等待所有任务完成
|
//
|
// // 提交每个路径的处理任务
|
// for (int j = 0; j < path.size(); j++) {
|
// final int index = j;
|
// JSONObject pathObject = path.getJSONObject(index);
|
//
|
// Future<PathCheckResult> future = customThreadPool.submit(() -> {
|
// try {
|
// PathCheckResult result = new PathCheckResult();
|
// result.setIndex(index);
|
// result.setPathObject(pathObject);
|
//
|
// String points = pathObject.getString("points");
|
// String[] split = points.split(";");
|
//
|
// // 检查点位是否在围栏内
|
// boolean inFence = checkPointsInFence(split, fenceId);
|
// result.setInFence(inFence);
|
//
|
// return result;
|
// } finally {
|
// latch.countDown(); // 任务完成,计数器减1
|
// }
|
// });
|
//
|
// futures.add(future);
|
// }
|
//
|
// try {
|
// // 等待所有任务完成,设置超时时间
|
// if (!latch.await(10, TimeUnit.SECONDS)) {
|
// System.out.println("路径检查任务超时");
|
// // 取消所有未完成的任务
|
// for (Future<PathCheckResult> future : futures) {
|
// if (!future.isDone()) {
|
// future.cancel(true);
|
// }
|
// }
|
// }
|
// } catch (InterruptedException e) {
|
// Thread.currentThread().interrupt();
|
// System.out.println("路径检查任务被中断");
|
// return null;
|
// }
|
//
|
// // 收集处理结果,找到第一个不在围栏内的路径
|
// PathCheckResult validPathResult = null;
|
//
|
// for (Future<PathCheckResult> future : futures) {
|
// try {
|
// if (future.isDone() && !future.isCancelled()) {
|
// PathCheckResult result = future.get();
|
// if (!result.isInFence()) {
|
// validPathResult = result;
|
// break; // 找到有效的就停止
|
// }
|
// }
|
// } catch (InterruptedException | ExecutionException e) {
|
// System.out.println("获取路径检查结果失败: " + e.getMessage());
|
// e.printStackTrace();
|
// }
|
// }
|
//
|
// // 如果找到了有效路径,处理该路径数据
|
// if (validPathResult != null) {
|
// JSONObject validPath = validPathResult.getPathObject();
|
// String routeID = validPath.getString("routeID");
|
// String points = validPath.getString("points");
|
// String[] split = points.split(";");
|
//
|
// Integer costTime = validPath.getInteger("costTime");
|
// Integer distance = validPath.getInteger("distance");
|
//
|
// routeVo.setDistance(distance);
|
// routeVo.setDuration(costTime);
|
//
|
// ArrayList<LonLatDto> lonLatDtos = new ArrayList<>(split.length);
|
// for (String s : split) {
|
// String[] split1 = s.split(",");
|
// LonLatDto lonLatDto = new LonLatDto(Double.parseDouble(split1[0]), Double.parseDouble(split1[1]));
|
// lonLatDtos.add(lonLatDto);
|
// }
|
// routeVo.setPolyline(lonLatDtos);
|
// routeVo.setRouteID(routeID);
|
//
|
// return routeVo;
|
// }
|
//
|
// return null;
|
// }
|
/**
|
* 路径检查结果内部类
|
*/
|
private static class PathCheckResult {
|
private int index;
|
private JSONObject pathObject;
|
private boolean inFence; // true: 所有点都在围栏内, false: 有点不在围栏内
|
|
public int getIndex() {
|
return index;
|
}
|
|
public void setIndex(int index) {
|
this.index = index;
|
}
|
|
public JSONObject getPathObject() {
|
return pathObject;
|
}
|
|
public void setPathObject(JSONObject pathObject) {
|
this.pathObject = pathObject;
|
}
|
|
public boolean isInFence() {
|
return inFence;
|
}
|
|
public void setInFence(boolean inFence) {
|
this.inFence = inFence;
|
}
|
}
|
/**
|
* 检查路径点位是否在围栏内
|
*/
|
private boolean checkPointsInFence(String[] points, String fenceId) {
|
// 取5分之一的点位进行检查
|
for (int i = 0; i < points.length; i++) {
|
if (i % 5 == 0) {
|
String[] split1 = points[i].split(",");
|
LonLatDto lonLatDto = new LonLatDto(Double.parseDouble(split1[0]), Double.parseDouble(split1[1]));
|
if (!xpGeoFencingPolygonService.isInFence(lonLatDto.getLongitude(), lonLatDto.getLatitude(), fenceId, "operating_area")) {
|
return false; // 有点位不在围栏内
|
}
|
}
|
}
|
return true; // 所有检查的点都在围栏内
|
}
|
|
/**
|
* 根据起始点获取驾车距离 和 时间 以及避让区域
|
*
|
* @param start 起点
|
* @param end 终点
|
* @param avoidRoads 避让点
|
* @param fenceId mango id
|
* @param routePlanRule 线路规划规则
|
*
|
* @return
|
* @throws Exception
|
*/
|
public RouteVo getDistanceAndTime(LonLatDto start, LonLatDto end,String avoidRoads,String fenceId,Integer routePlanRule) {
|
RouteVo routeVo = new RouteVo();
|
|
String url = "https://tsapi.amap.com/v1/route/price?key=" + key + "&extensions=all&start=" + start.getLongitude() + "," + start.getLatitude();
|
HashMap<String, Object> params = new HashMap<>();
|
params.put("extensions", "all");
|
params.put("start", start.getLongitude() + "," + start.getLatitude());
|
params.put("end", end.getLongitude() + "," + end.getLatitude());
|
params.put("routeUserType",2);
|
if(avoidRoads!=null && !avoidRoads.isEmpty()){
|
params.put("voidPolygons", avoidRoads);
|
}
|
if(routePlanRule!=null){
|
params.put("strategy", routePlanRule);
|
}
|
|
String post = HttpUtils.post(url, JSON.toJSONString(params));
|
System.out.println("高德地图返回结果:"+ post);
|
// String forObject = restTemplate.getForObject(url, String.class, params);
|
JSONObject jsonObject = JSON.parseObject(post);
|
String data = jsonObject.getString("data");
|
|
JSONObject jsonObject1 = JSON.parseObject(data);
|
JSONArray path = jsonObject1.getJSONArray("paths");
|
for (Object o : path) {
|
JSONObject jsonObject2 = (JSONObject) o;
|
String routeID = jsonObject2.getString("routeID");
|
String points = jsonObject2.getString("points");
|
String[] split = points.split(";");
|
int temp=0;
|
// 取5分之一的点位
|
for (int i = 0; i < split.length; i++) {
|
if (i % 10 == 0) {
|
String[] split1 = split[i].split(",");
|
LonLatDto lonLatDto = new LonLatDto(Double.parseDouble(split1[0]), Double.parseDouble(split1[1]));
|
if (!xpGeoFencingPolygonService.isInFence(lonLatDto.getLongitude(), lonLatDto.getLatitude(), fenceId, "operating_area")) {
|
temp++;
|
break;
|
}
|
}
|
}
|
if(temp==0){
|
Integer costTime = jsonObject2.getInteger("costTime");
|
Integer distance = jsonObject2.getInteger("distance");
|
routeVo.setDistance(distance);
|
routeVo.setDuration(costTime);
|
ArrayList<LonLatDto> lonLatDtos = new ArrayList<>(split.length);
|
for (String s : split) {
|
String[] split1 = s.split(",");
|
LonLatDto lonLatDto = new LonLatDto(Double.parseDouble(split1[0]), Double.parseDouble(split1[1]));
|
lonLatDtos.add(lonLatDto);
|
}
|
routeVo.setPolyline(lonLatDtos);
|
routeVo.setRouteID(routeID);
|
return routeVo;
|
}
|
}
|
|
return null;
|
|
}
|
|
|
/**
|
* 线路还原接口
|
* 车端使用的数据
|
* @param routeID
|
* @return
|
*/
|
public String routed(String routeID){
|
String url = "https://tsapi.amap.com/v1/route/query/routeid?key=" + key;
|
HashMap<String, Object> params = new HashMap<>();
|
params.put("routeID", routeID);
|
params.put("sdkVersion", "9.850.152.3.532");
|
params.put("encoderVersion", "5.1");
|
String post = HttpUtils.post(url, JSON.toJSONString(params));
|
log.info("高德地图返回结果:"+ post);
|
return post;
|
}
|
|
|
|
/**
|
* 根据经纬度获取行政区域信息
|
* @param lon
|
* @param lan
|
* @return
|
* @throws Exception
|
*/
|
public Map<String, String> geocode(String lon, String lan) {
|
String url = "https://restapi.amap.com/v3/geocode/regeo?key=" + key + "&location=" + lon + "," + lan;
|
String forObject = HttpUtils.sendGet(url);
|
JSONObject jsonObject = JSON.parseObject(forObject);
|
Map<String, String> map = new HashMap<>();
|
if(jsonObject.getString("status").equals("1")){
|
JSONObject regeocode = jsonObject.getJSONObject("regeocode");
|
JSONObject addressComponent = regeocode.getJSONObject("addressComponent");
|
String address = regeocode.getString("formatted_address");
|
map.put("address", address);
|
String code = addressComponent.getString("adcode");
|
String province = addressComponent.getString("province");
|
String city = addressComponent.getString("city");
|
String district = addressComponent.getString("district");
|
map.put("province", province);
|
map.put("provinceCode", code.substring(0, 2) + "0000");
|
map.put("city", city);
|
map.put("cityCode", code.substring(0, 4) + "00");
|
map.put("district", district);
|
map.put("districtCode", code);
|
}
|
return map;
|
}
|
|
}
|