From dc8c7a05c53c8d5258f8f9a33963f7e91cd683b0 Mon Sep 17 00:00:00 2001 From: xiepufeng <1072271977@qq.com> Date: Tue, 9 Apr 2024 11:48:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=8E=B7=E5=8F=96=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E6=8B=A5=E5=A0=B5=E8=B7=AF=E6=AE=B5=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/constant/StakeMarkConstant.java | 2 +- .../DcTrafficStatisticsController.java | 16 ++ .../domain/DcCongestedSectionData.java | 31 ++++ .../enums/ChannelCongestionLevelEnum.java | 69 +++++++++ .../service/DcTrafficStatisticsService.java | 9 ++ .../impl/DcTrafficStatisticsServiceImpl.java | 25 ++++ .../statistics/handler/TrafficAnalysis.java | 137 +++++++++++++++++- 7 files changed, 281 insertions(+), 8 deletions(-) create mode 100644 zc-business/src/main/java/com/zc/business/domain/DcCongestedSectionData.java diff --git a/zc-business/src/main/java/com/zc/business/constant/StakeMarkConstant.java b/zc-business/src/main/java/com/zc/business/constant/StakeMarkConstant.java index 45d5e654..d3f65f5c 100644 --- a/zc-business/src/main/java/com/zc/business/constant/StakeMarkConstant.java +++ b/zc-business/src/main/java/com/zc/business/constant/StakeMarkConstant.java @@ -13,7 +13,7 @@ public class StakeMarkConstant { // 定义最小桩号常量 public static final int MIN_STAKE_MARK = 54394; - // 定义毫米波雷达最大测量间隔 + // 定义毫米波雷达最大间隔 public static final int MAX_INTERVAL_MILLIMETER_WAVE_RADAR = 12030; /** diff --git a/zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java b/zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java index eb1224cc..0f799cf9 100644 --- a/zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java +++ b/zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java @@ -1,6 +1,7 @@ package com.zc.business.controller; import com.ruoyi.common.core.domain.AjaxResult; +import com.zc.business.domain.DcCongestedSectionData; import com.zc.business.domain.DcTrafficMetricsData; import com.zc.business.domain.DcTrafficSectionData; import com.zc.business.request.DcTrafficMetricsDataRequest; @@ -88,4 +89,19 @@ public class DcTrafficStatisticsController { return AjaxResult.success(dcTrafficMetricsDataList); } + /** + * 获取当前拥堵路段 + * + * @param direction 交通方向,指定查询哪个方向的拥堵路段。具体方向的定义根据实际业务而定。 + * @return 返回一个AjaxResult对象,其中包含了查询结果。如果查询成功,则结果中封装了当前拥堵路段的数据列表。 + */ + @ApiOperation("获取当前拥堵路段") + @GetMapping("/current/congested") + public AjaxResult currentCongestedSection(Byte direction){ + // 调用服务层方法,获取当前交通指标数据 + List dcTrafficMetricsData = dcTrafficStatisticsService.currentCongestedSection(direction); + // 将获取到的交通指标数据封装为成功的结果并返回 + return AjaxResult.success(dcTrafficMetricsData); + } + } diff --git a/zc-business/src/main/java/com/zc/business/domain/DcCongestedSectionData.java b/zc-business/src/main/java/com/zc/business/domain/DcCongestedSectionData.java new file mode 100644 index 00000000..d89bc720 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/domain/DcCongestedSectionData.java @@ -0,0 +1,31 @@ +package com.zc.business.domain; + +import lombok.Data; + +/** + * 交通拥堵路段数据定义 + * @author xiepufeng + */ +@Data +public class DcCongestedSectionData { + + /** + * 开始桩号 + */ + private Integer startStakeMark; + + /** + * 结束桩号 + */ + private Integer endStakeMark; + + /** + * 道路方向 + */ + private Byte direction; + + /** + * 平均车速 + */ + private Integer averageSpeed; +} diff --git a/zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java b/zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java index 770b96ad..67e3e584 100644 --- a/zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java +++ b/zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java @@ -76,6 +76,27 @@ public enum ChannelCongestionLevelEnum { return SEVERE_CONGESTION; // 若速度小于等于所有等级阈值,则视为严重拥堵 } + /** + * 根据给定速度,返回对应的通道拥挤度等级。 + * + * @param speed 速度(单位:km/h) + * @param trafficVolume 车流量 + * @return 对应的通道拥挤度等级 + */ + public static ChannelCongestionLevelEnum fromSpeed(int speed, int trafficVolume) { + + if (trafficVolume == 0) { + return FLOWING; + } + + for (ChannelCongestionLevelEnum level : values()) { + if (speed >= level.speedThreshold) { + return level; + } + } + return SEVERE_CONGESTION; // 若速度小于等于所有等级阈值,则视为严重拥堵 + } + /** * 判断速度是否是是中度拥堵或者严重拥堵 */ @@ -84,6 +105,53 @@ public enum ChannelCongestionLevelEnum { return level == MEDIUM_CONGESTION || level == SEVERE_CONGESTION; } + + /** + * 判断当前交通状况是否为中度或严重拥堵。 + * + * @param speed 当前道路的车速,单位为任意测量单位,取决于具体应用。 + * @param trafficVolume 当前道路的交通流量,单位为任意测量单位,取决于具体应用。 + * @return boolean 返回 true 如果当前交通状况被判断为中度或严重拥堵,否则返回 false。 + */ + public static boolean isMediumOrSevereCongestion(int speed, int trafficVolume) { + + // 当交通流量为0时,不考虑拥堵问题,直接返回false + if (trafficVolume == 0) { + return false; + } + + // 根据车速判断拥堵级别 + ChannelCongestionLevelEnum level = fromSpeed(speed); + // 判断拥堵级别是否为中度或严重拥堵 + return level == MEDIUM_CONGESTION || level == SEVERE_CONGESTION; + } + + /** + * 判断给定速度是否属于拥堵状态。 + * + * @param speed 速度(单位:km/h) + * @param trafficVolume 车流量 + * @return 如果速度小于等于基本畅通的阈值,则返回true;否则返回false。 + */ + public static boolean isCongestion(int speed, int trafficVolume) { + + if (trafficVolume == 0) { + return false; + } + + return speed < BASIC_FLOWING.getSpeedThreshold(); + } + + /** + * 判断给定速度是否属于拥堵状态。 + * + * @param speed 速度(单位:km/h) + * @return 如果速度小于等于基本畅通的阈值,则返回true;否则返回false。 + */ + public static boolean isCongestion(int speed) { + return speed < BASIC_FLOWING.getSpeedThreshold(); + } + /** * 判断给定速度是否属于指定的拥挤度等级。 * @@ -92,6 +160,7 @@ public enum ChannelCongestionLevelEnum { * @return 如果速度属于该等级,返回true;否则返回false。 */ public static boolean isWithinLevel(int speed, ChannelCongestionLevelEnum level) { + if (level == null) return false; ChannelCongestionLevelEnum currentLevel = fromSpeed(speed); return currentLevel == level; } diff --git a/zc-business/src/main/java/com/zc/business/service/DcTrafficStatisticsService.java b/zc-business/src/main/java/com/zc/business/service/DcTrafficStatisticsService.java index 45611fec..1fddc4ba 100644 --- a/zc-business/src/main/java/com/zc/business/service/DcTrafficStatisticsService.java +++ b/zc-business/src/main/java/com/zc/business/service/DcTrafficStatisticsService.java @@ -2,6 +2,7 @@ package com.zc.business.service; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.extension.service.IService; +import com.zc.business.domain.DcCongestedSectionData; import com.zc.business.domain.DcTrafficMetricsData; import com.zc.business.domain.DcTrafficSectionData; import com.zc.business.request.DcTrafficMetricsDataRequest; @@ -50,4 +51,12 @@ public interface DcTrafficStatisticsService extends IService historyTrafficMetrics(DcTrafficMetricsDataRequest request); + + /** + * 获取当前拥堵路段列表。 + * + * @param direction 交通方向,指定查询哪个方向的拥堵路段。具体方向的定义根据实际业务而定。 + * @return 返回当前拥堵路段列表。 + */ + List currentCongestedSection(Byte direction); } diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java index 1caca7d6..de1c91fc 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java @@ -10,6 +10,7 @@ import com.ruoyi.common.exception.ServiceException; import com.zc.business.constant.DeviceTypeConstants; import com.zc.business.constant.RedisKeyConstants; import com.zc.business.controller.DcDeviceController; +import com.zc.business.domain.DcCongestedSectionData; import com.zc.business.domain.DcRoadSection; import com.zc.business.domain.DcTrafficMetricsData; import com.zc.business.domain.DcTrafficSectionData; @@ -228,6 +229,30 @@ public class DcTrafficStatisticsServiceImpl } + /** + * 获取当前拥堵的路段数据列表。 + * + * @param direction 指定的方向,用于筛选交通数据。 + * @return 返回一个包含当前拥堵路段数据的列表。 + */ + @Override + public List currentCongestedSection(Byte direction) { + + // 从Redis缓存中获取指定方向的交通路段数据列表 + List dcTrafficSectionDataCaches = getDcTrafficSectionDataRedisCache(direction); + + // 过滤掉时间错误的交通数据 + processStaleTrafficSectionData(dcTrafficSectionDataCaches); + + // 移除非毫米波雷达设备类型的交通数据 + dcTrafficSectionDataCaches.removeIf(data -> !Objects.equals(data.getDeviceType(), DeviceTypeConstants.MILLIMETER_WAVE_RADAR)); + + // 分析交通数据,计算出当前拥堵的路段列表并返回 + return trafficAnalysis.calculateCongestedSection(dcTrafficSectionDataCaches); + } + + + /** * 恢复每日缓存的函数。 * 该方法尝试从物联平台获取所有设备信息,并对这些信息进行处理。 diff --git a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java index 7ce8ddc1..41e441b8 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java +++ b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java @@ -5,10 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.redis.RedisCache; import com.zc.business.constant.RedisKeyConstants; import com.zc.business.constant.StakeMarkConstant; -import com.zc.business.domain.DcRoadSection; -import com.zc.business.domain.DcTrafficMetricsData; -import com.zc.business.domain.DcTrafficSectionData; -import com.zc.business.domain.DcTrafficVolumeForecast; +import com.zc.business.domain.*; import com.zc.business.enums.*; import com.zc.business.request.DcTrafficMetricsDataRequest; import com.zc.business.service.DcTrafficVolumeForecastService; @@ -425,7 +422,7 @@ public class TrafficAnalysis { int stakeMark = dcTrafficSectionData.getStakeMark(); // 对于不拥堵的路段,累加之前计算的默认拥堵距离 - if (!ChannelCongestionLevelEnum.isMediumOrSevereCongestion(averageSpeed) || dcTrafficSectionData.getTrafficVolume() == 0) { + if (!ChannelCongestionLevelEnum.isMediumOrSevereCongestion(averageSpeed, dcTrafficSectionData.getTrafficVolume())) { totalCongestionDistance.addAndGet(defaultCongestionDistance); previousStakeMark = stakeMark; defaultCongestionDistance = 0; @@ -441,6 +438,7 @@ public class TrafficAnalysis { int congestionDistance = Math.abs(stakeMark - previousStakeMark); if (congestionDistance > StakeMarkConstant.MAX_INTERVAL_MILLIMETER_WAVE_RADAR) { totalCongestionDistance.addAndGet(defaultCongestionDistance); + defaultCongestionDistance = 0; } else { totalCongestionDistance.addAndGet(congestionDistance); // 如果当前路段被计算过,则累加拥堵路段数量 @@ -458,6 +456,10 @@ public class TrafficAnalysis { previousAverageSpeed = averageSpeed; } + if (defaultCongestionDistance != 0) { + totalCongestionDistance.addAndGet(defaultCongestionDistance); + } + }); // 计算并返回路网整体的拥堵指数 @@ -470,8 +472,6 @@ public class TrafficAnalysis { return metricsData; } - - /** * 计算路段交通量 * @@ -638,4 +638,127 @@ public class TrafficAnalysis { } + /** + * 计算拥堵路段数据。 + * + * @param trafficSectionDataList 交通路段数据列表,包含各个路段的平均速度、交通量和行驶方向等信息。 + * @return 拥堵路段数据列表,详细记录了每个拥堵路段的起始桩号、结束桩号、平均速度和行驶方向等信息。 + */ + public List calculateCongestedSection(List trafficSectionDataList) { + + if (trafficSectionDataList == null || trafficSectionDataList.isEmpty()) { + return Collections.emptyList(); + } + + // 根据行驶方向对交通数据进行分组 + Map> groupedByDirection = trafficSectionDataList.stream() + .collect(Collectors.groupingBy(DcTrafficSectionData::getDirection)); + + List dcCongestedSectionDataList = new ArrayList<>(); + + groupedByDirection.forEach((directionData, trafficSectionList) -> { + + List sortedList; + + // 根据行驶方向,对交通路段数据进行排序,上行方向逆序,下行方向正序 + if (directionData.equals(LaneDirectionEnum.UPWARD.getValue())) { + sortedList = trafficSectionList.stream() + .sorted(Comparator.comparing(DcTrafficSectionData::getStakeMark).reversed()) + .collect(Collectors.toList()); + } else { + sortedList = trafficSectionList.stream() + .sorted(Comparator.comparing(DcTrafficSectionData::getStakeMark)) + .collect(Collectors.toList()); + } + + int previousStakeMark = 0; + int previousAverageSpeed = 0; + int defaultCongestionDistance = 0; + int trafficVolume = 0; + ChannelCongestionLevelEnum previousCongestionLevel = null; + DcCongestedSectionData congestedSectionData = null; + + // 遍历排序后的路段数据,计算每个路段的拥堵里程,并累加到总拥堵里程中 + for (DcTrafficSectionData dcTrafficSectionData : sortedList) { + + int averageSpeed = dcTrafficSectionData.getAverageSpeed(); + int stakeMark = dcTrafficSectionData.getStakeMark(); + trafficVolume = dcTrafficSectionData.getTrafficVolume(); + + boolean isCongestion = ChannelCongestionLevelEnum.isCongestion(averageSpeed, trafficVolume); + + // 判断当前路段是否拥堵,如果不是拥堵路段且前一个路段是拥堵的,则更新拥堵里程并创建新的拥堵路段数据 + if (!isCongestion && previousAverageSpeed != 0) { + int congestionDistance = Math.abs(stakeMark - previousStakeMark); + if (congestionDistance > defaultCongestionDistance) { + if (directionData.equals(LaneDirectionEnum.UPWARD.getValue())) { + previousStakeMark -= defaultCongestionDistance; + } else { + previousStakeMark += defaultCongestionDistance; + } + congestedSectionData.setEndStakeMark(previousStakeMark); + } else { + previousStakeMark = stakeMark; + congestedSectionData.setEndStakeMark(stakeMark); + } + + congestedSectionData.setEndStakeMark(previousStakeMark); + previousCongestionLevel = ChannelCongestionLevelEnum.fromSpeed(averageSpeed, trafficVolume); + previousAverageSpeed = 0; + defaultCongestionDistance = 0; + continue; + } + + if (!isCongestion) { + continue; + } + + // 处理同等级别拥堵的路段,计算拥堵距离,更新拥堵路段的结束桩号 + if (ChannelCongestionLevelEnum.isWithinLevel(averageSpeed, previousCongestionLevel)) { + int congestionDistance = Math.abs(stakeMark - previousStakeMark); + // 对于超出最大间隔的拥堵路段,更新拥堵里程 + if (congestionDistance > StakeMarkConstant.MAX_INTERVAL_MILLIMETER_WAVE_RADAR) { + if (directionData.equals(LaneDirectionEnum.UPWARD.getValue())) { + previousStakeMark -= defaultCongestionDistance; + } else { + previousStakeMark += defaultCongestionDistance; + } + congestedSectionData.setEndStakeMark(previousStakeMark); + } else { + previousStakeMark = stakeMark; + congestedSectionData.setEndStakeMark(stakeMark); + } + } else { + // 如果当前路段拥堵级别改变,创建新的拥堵路段数据,并更新起始桩号和默认拥堵距离 + congestedSectionData = new DcCongestedSectionData(); + congestedSectionData.setAverageSpeed(averageSpeed); + congestedSectionData.setDirection(directionData); + congestedSectionData.setStartStakeMark(stakeMark); + + dcCongestedSectionDataList.add(congestedSectionData); + + previousStakeMark = stakeMark; + defaultCongestionDistance = ChannelCongestionLevelEnum.fromSpeed(averageSpeed).getDefaultCongestionDistance(); + } + + previousAverageSpeed = averageSpeed; + previousCongestionLevel = ChannelCongestionLevelEnum.fromSpeed(averageSpeed); + } + + // 处理遍历完所有路段后的拥堵状态,确保最后一个拥堵路段也被记录 + boolean isCongestion = ChannelCongestionLevelEnum.isCongestion(previousAverageSpeed, trafficVolume); + + if (isCongestion && congestedSectionData != null) { + // 根据行驶方向更新拥堵里程 + if (directionData.equals(LaneDirectionEnum.UPWARD.getValue())) { + previousStakeMark -= defaultCongestionDistance; + } else { + previousStakeMark += defaultCongestionDistance; + } + congestedSectionData.setEndStakeMark(previousStakeMark); + } + }); + + return dcCongestedSectionDataList; + } }