Compare commits

...

2 Commits

  1. 2
      zc-business/src/main/java/com/zc/business/constant/StakeMarkConstant.java
  2. 16
      zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java
  3. 31
      zc-business/src/main/java/com/zc/business/domain/DcCongestedSectionData.java
  4. 69
      zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java
  5. 9
      zc-business/src/main/java/com/zc/business/service/DcTrafficStatisticsService.java
  6. 25
      zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java
  7. 137
      zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java

2
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;
/**

16
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<DcCongestedSectionData> dcTrafficMetricsData = dcTrafficStatisticsService.currentCongestedSection(direction);
// 将获取到的交通指标数据封装为成功的结果并返回
return AjaxResult.success(dcTrafficMetricsData);
}
}

31
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;
}

69
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;
}

9
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<DcTrafficSectionDat
* @return 返回符合查询条件的历史交通指标数据列表
*/
List<DcTrafficMetricsData> historyTrafficMetrics(DcTrafficMetricsDataRequest request);
/**
* 获取当前拥堵路段列表
*
* @param direction 交通方向指定查询哪个方向的拥堵路段具体方向的定义根据实际业务而定
* @return 返回当前拥堵路段列表
*/
List<DcCongestedSectionData> currentCongestedSection(Byte direction);
}

25
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<DcCongestedSectionData> currentCongestedSection(Byte direction) {
// 从Redis缓存中获取指定方向的交通路段数据列表
List<DcTrafficSectionData> dcTrafficSectionDataCaches = getDcTrafficSectionDataRedisCache(direction);
// 过滤掉时间错误的交通数据
processStaleTrafficSectionData(dcTrafficSectionDataCaches);
// 移除非毫米波雷达设备类型的交通数据
dcTrafficSectionDataCaches.removeIf(data -> !Objects.equals(data.getDeviceType(), DeviceTypeConstants.MILLIMETER_WAVE_RADAR));
// 分析交通数据,计算出当前拥堵的路段列表并返回
return trafficAnalysis.calculateCongestedSection(dcTrafficSectionDataCaches);
}
/**
* 恢复每日缓存的函数
* 该方法尝试从物联平台获取所有设备信息并对这些信息进行处理

137
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<DcCongestedSectionData> calculateCongestedSection(List<DcTrafficSectionData> trafficSectionDataList) {
if (trafficSectionDataList == null || trafficSectionDataList.isEmpty()) {
return Collections.emptyList();
}
// 根据行驶方向对交通数据进行分组
Map<Byte, List<DcTrafficSectionData>> groupedByDirection = trafficSectionDataList.stream()
.collect(Collectors.groupingBy(DcTrafficSectionData::getDirection));
List<DcCongestedSectionData> dcCongestedSectionDataList = new ArrayList<>();
groupedByDirection.forEach((directionData, trafficSectionList) -> {
List<DcTrafficSectionData> 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;
}
}

Loading…
Cancel
Save