| | |
| | | |
| | | @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; // 所有检查的点都在围栏内 |
| | | } |
| | | |
| | | /** |
| | | * 根据起始点获取驾车距离 和 时间 以及避让区域 |
| | |
| | | |
| | | JSONObject jsonObject1 = JSON.parseObject(data); |
| | | JSONArray path = jsonObject1.getJSONArray("paths"); |
| | | log.info("开始判断导航路径是否在运营区域范围内 {}", fenceId); |
| | | 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(","); |
| | | List<LonLatDto> pointsArray = new ArrayList<>(); |
| | | for (String string : split) { |
| | | String[] split1 = string.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; |
| | | pointsArray.add(lonLatDto); |
| | | } |
| | | } |
| | | } |
| | | ForkJoinPool pool = new ForkJoinPool(); // 创建ForkJoinPool对象(默认使用处理器的核心数作为线程池大小) |
| | | ArrayPointsTask arrayPointsTask = new ArrayPointsTask(pointsArray, fenceId); // 创建求和任务对象并传入数组作为参数(这里也可以传入数组的一部分作为参数来实现更细粒度的拆分) |
| | | Long temp = pool.invoke(arrayPointsTask); // 提交任务并等待处理完成(也可以使用submit方法提交任务并获取一个Future对象来异步获取结果) |
| | | pool.shutdown(); // 关闭ForkJoinPool(释放资源) |
| | | if(temp==0){ |
| | | Integer costTime = jsonObject2.getInteger("costTime"); |
| | | Integer distance = jsonObject2.getInteger("distance"); |
| | |
| | | } |
| | | routeVo.setPolyline(lonLatDtos); |
| | | routeVo.setRouteID(routeID); |
| | | log.info("导航 {} 在 {} 运营区域内", routeVo.getRouteID(), fenceId); |
| | | return routeVo; |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | class ArrayPointsTask extends RecursiveTask<Long>{ |
| | | //最大定位集合阈值 |
| | | private final int THRESHOLD = 10; |
| | | //围栏ID |
| | | String fenceId; |
| | | //定位点集合 |
| | | List<LonLatDto> pointsArray; |
| | | |
| | | |
| | | public ArrayPointsTask(List<LonLatDto> pointsArray, String fenceId) { |
| | | this.pointsArray = pointsArray; |
| | | this.fenceId = fenceId; |
| | | } |
| | | |
| | | @Override |
| | | protected Long compute() { |
| | | if(this.pointsArray.size() > THRESHOLD){ |
| | | int num = this.pointsArray.size() / 2; |
| | | ArrayPointsTask task1 = new ArrayPointsTask(this.pointsArray.subList(0, num), fenceId); |
| | | ArrayPointsTask task2 = new ArrayPointsTask(this.pointsArray.subList(num, this.pointsArray.size()), fenceId); |
| | | task1.fork(); |
| | | task2.fork(); |
| | | return task1.join() + task2.join(); |
| | | }else{ |
| | | long temp = 0; |
| | | for (LonLatDto lonLatDto : this.pointsArray) { |
| | | if (!xpGeoFencingPolygonService.isInFence(lonLatDto.getLongitude(), lonLatDto.getLatitude(), fenceId, "operating_area")) { |
| | | temp++; |
| | | break; |
| | | } |
| | | } |
| | | return temp; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 线路还原接口 |
| | | * 车端使用的数据 |