| | |
| | | class GridFusionByAQI { |
| | | var pollutionDegree: String? = null |
| | | var gridLen:Int? = null |
| | | var missionList: MutableList<MissionInventory.MissionDetail> = mutableListOf() |
| | | var missionList: MutableList<MissionInventory.MissionInfo> = mutableListOf() |
| | | var gridFusionList: MutableList<FusionGrid> = mutableListOf() |
| | | var highRiskGridList: MutableList<HighRiskGridByFactor> = mutableListOf() |
| | | } |
| | |
| | | factorTypes: List<FactorType>, |
| | | gridLen: Int, |
| | | gridCells: List<GridCell>, |
| | | dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>, |
| | | dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionInfo>, List<GridDataDetailMixVo>>>, |
| | | ): List<GridFusionByAQI> { |
| | | return dataList.map { |
| | | GridFusionByAQI().apply { |
| | |
| | | highRiskGridList.addAll(factorTypes.map { f-> |
| | | HighRiskGridByFactor().apply { |
| | | factorType = f |
| | | highRiskGrid = gridFusionList.sortedBy { gf->gf.data.rank }.firstOrNull() |
| | | highRiskGrid = gridFusionList.maxByOrNull { gf->gf.data.getByFactorType(f) ?: 0f } |
| | | if (highRiskGrid != null) { |
| | | factorValue = highRiskGrid!!.data.getByFactorType(f) |
| | | if (highRiskGrid!!.cell.longitude != null && highRiskGrid!!.cell.latitude != null) { |
| | |
| | | |
| | | // 溯源问题场景数 |
| | | var sceneCount: Int = 0 |
| | | |
| | | // 溯源问题场景 |
| | | var scenes: List<SceneInfo>? = null |
| | | |
| | | // 走航涉及区域 |
| | | var keyScene: List<SceneInfo>? = null |
| | | |
| | | var exceptionCount: Int = 0 |
| | | } |
| | | |
| | | // 走航详情信息 |
| | | class MissionDetail : Mission() { |
| | | var keyScene: List<SceneInfo>? = null |
| | | // var keyScene: List<SceneInfo>? = null |
| | | var dataStatistics: List<FactorStatistics>? = null |
| | | var exceptionCount: Int = 0 |
| | | // var exceptionCount: Int = 0 |
| | | } |
| | | |
| | | /** |
| | |
| | | return result |
| | | } |
| | | |
| | | fun generateMissionInfo( |
| | | keyScenes: List<SceneInfo?>, |
| | | mission: Mission, |
| | | pollutedClues: List<PollutedClue?>, |
| | | data: List<BaseRealTimeData>, |
| | | minDis: Double = 100.0, |
| | | ): MissionInfo { |
| | | val factorMap = mutableMapOf<FactorType, Int>() |
| | | val abnormalFactors = mutableListOf<FactorType>() |
| | | var sceneCount = 0 |
| | | val scenes = mutableListOf<SceneInfo>() |
| | | // 提取途径关键场景信息(计算走航路线是否与关键场景距离较近) |
| | | val relatedScenes = mutableListOf<SceneInfo>() |
| | | data.forEach { d -> |
| | | // 跳过缺少经纬度的数据点 |
| | | if (d.longitude == null || d.latitude == null) { |
| | | return@forEach |
| | | } |
| | | // 转换为GCJ02坐标系 |
| | | val point = MapUtil.wgs84ToGcj02(d.longitude!!.toDouble() to d.latitude!!.toDouble()) |
| | | keyScenes.forEach ks@{ k -> |
| | | // 跳过缺少经纬度的场景 |
| | | if (k?.longitude == null || k.latitude == null) { |
| | | return@ks |
| | | } |
| | | // 检查是否未添加过 |
| | | if (!relatedScenes.contains(k)) { |
| | | // 计算距离 |
| | | val distance = MapUtil.getDistance( |
| | | k.longitude!!.toDouble(), |
| | | k.latitude!!.toDouble(), |
| | | point.first, |
| | | point.second |
| | | ) |
| | | // 检查是否距离小于阈值 |
| | | if (distance < minDis) { |
| | | relatedScenes.add(k) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | pollutedClues.forEach { |
| | | if (it?.msgType == MsgType.PolClue.value) { |
| | | it.pollutedData?.statisticMap?.keys?.forEach { k -> |
| | | // 计算每个走航任务的所有异常因子 |
| | | if (!abnormalFactors.contains(k)) { |
| | | abnormalFactors.add(k) |
| | | } |
| | | // 计算每个走航任务的首要污染物 |
| | | if (!factorMap.containsKey(k)) { |
| | | factorMap[k] = 0 |
| | | } |
| | | factorMap[k] = factorMap[k]!! + 1 |
| | | } |
| | | // 计算每个走航任务的溯源场景数量 |
| | | sceneCount += it.pollutedSource?.sceneList?.size ?: 0 |
| | | it.pollutedSource?.sceneList?.forEach { s-> |
| | | if (scenes.find { s1 -> s1.guid == s.guid } == null) { |
| | | scenes.add(s) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 异常数据点数量统计 |
| | | val clues = pollutedClues.filter { it?.msgType == MsgType.PolClue.value } |
| | | |
| | | val missionInfo = MissionInfo() |
| | | BeanUtils.copyProperties(mission, missionInfo) |
| | | missionInfo.apply { |
| | | mainFactor = factorMap.maxByOrNull { it.value }?.key?.name |
| | | this.abnormalFactors = abnormalFactors |
| | | this.sceneCount = sceneCount |
| | | this.scenes = scenes |
| | | keyScene = relatedScenes |
| | | exceptionCount = clues.size |
| | | } |
| | | |
| | | return missionInfo |
| | | } |
| | | |
| | | /** |
| | | * 生成走航任务详细信息 |
| | | * 整合走航任务基本信息、关键场景、数据统计和异常数量,生成完整的任务详情报告 |
| | |
| | | mission: Mission, |
| | | pollutedClues: List<PollutedClue?>, |
| | | data: List<BaseRealTimeData>, |
| | | minDis:Double = 100.0 |
| | | minDis: Double = 100.0, |
| | | ): MissionDetail { |
| | | // 创建任务详情对象并复制基本信息 |
| | | val missionDetail = MissionDetail() |
| | | BeanUtils.copyProperties(mission, missionDetail) |
| | | |
| | | // 提取途径关键场景信息(计算走航路线是否与关键场景距离较近) |
| | | val relatedScenes = mutableListOf<SceneInfo>() |
| | | data.forEach { d-> |
| | | // 跳过缺少经纬度的数据点 |
| | | if (d.longitude == null || d.latitude == null) { |
| | | return@forEach |
| | | } |
| | | // 转换为GCJ02坐标系 |
| | | val point = MapUtil.wgs84ToGcj02(d.longitude!!.toDouble() to d.latitude!!.toDouble()) |
| | | keyScenes.forEach ks@ { k-> |
| | | // 跳过缺少经纬度的场景 |
| | | if (k?.longitude == null || k.latitude == null) { |
| | | return@ks |
| | | } |
| | | // 计算距离 |
| | | val distance = MapUtil.getDistance(k.longitude!!.toDouble(), k.latitude!!.toDouble(), point.first, point.second) |
| | | // 检查是否距离小于阈值且未添加过 |
| | | if (distance < minDis && !relatedScenes.contains(k)) { |
| | | relatedScenes.add(k) |
| | | } |
| | | } |
| | | } |
| | | // val relatedScenes = mutableListOf<SceneInfo>() |
| | | // data.forEach { d -> |
| | | // // 跳过缺少经纬度的数据点 |
| | | // if (d.longitude == null || d.latitude == null) { |
| | | // return@forEach |
| | | // } |
| | | // // 转换为GCJ02坐标系 |
| | | // val point = MapUtil.wgs84ToGcj02(d.longitude!!.toDouble() to d.latitude!!.toDouble()) |
| | | // keyScenes.forEach ks@{ k -> |
| | | // // 跳过缺少经纬度的场景 |
| | | // if (k?.longitude == null || k.latitude == null) { |
| | | // return@ks |
| | | // } |
| | | // // 检查是否未添加过 |
| | | // if (!relatedScenes.contains(k)) { |
| | | // // 计算距离 |
| | | // val distance = MapUtil.getDistance( |
| | | // k.longitude!!.toDouble(), |
| | | // k.latitude!!.toDouble(), |
| | | // point.first, |
| | | // point.second |
| | | // ) |
| | | // // 检查是否距离小于阈值 |
| | | // if (distance < minDis) { |
| | | // relatedScenes.add(k) |
| | | // } |
| | | // } |
| | | // } |
| | | // } |
| | | // 存储与任务相关联的关键场景信息 |
| | | missionDetail.keyScene = relatedScenes |
| | | // missionDetail.keyScene = relatedScenes |
| | | |
| | | // 计算环境因子统计数据(平均值、最小值、最大值) |
| | | missionDetail.dataStatistics = data.calDataStatistics() |
| | | |
| | | // 异常数据点数量统计 |
| | | val clues = pollutedClues.filter { it?.msgType == MsgType.PolClue.value } |
| | | missionDetail.exceptionCount = clues.size |
| | | // val clues = pollutedClues.filter { it?.msgType == MsgType.PolClue.value } |
| | | // missionDetail.exceptionCount = clues.size |
| | | |
| | | return missionDetail |
| | | } |
| | |
| | | |
| | | import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue |
| | | import com.flightfeather.uav.domain.entity.Mission |
| | | import com.flightfeather.uav.domain.repository.MissionRep |
| | | import com.flightfeather.uav.lightshare.bean.AreaVo |
| | | import com.flightfeather.uav.socket.eunm.FactorType |
| | | import com.flightfeather.uav.socket.sender.MsgType |
| | | import org.springframework.stereotype.Component |
| | | import java.util.* |
| | | import kotlin.math.round |
| | | |
| | |
| | | * @author feiyu02 |
| | | */ |
| | | class MissionSummary() { |
| | | |
| | | companion object { |
| | | private const val FOCUS_AREA_COUNT = 2 |
| | | } |
| | | |
| | | data class Summary( |
| | | // 汇总周期开始时间 |
| | |
| | | // 高风险场景总数 |
| | | val highRiskSceneCount:Int, |
| | | // 问题按监测因子类型分布情况, <因子类型,次数,占比> |
| | | val probByFactor:List<Triple<String, Int, Double>> |
| | | val probByFactor: List<Triple<String, Int, Double>>, |
| | | // 聚焦区域或场景 |
| | | val focusRegion: List<String>, |
| | | ) |
| | | |
| | | /** |
| | |
| | | countByDegree = emptyList(), |
| | | probCount = 0, |
| | | highRiskSceneCount = 0, |
| | | probByFactor = emptyList() |
| | | probByFactor = emptyList(), |
| | | focusRegion = emptyList() |
| | | ) |
| | | } |
| | | |
| | |
| | | val highRiskSceneCount = clueRes.second // 需关联场景表统计 |
| | | val probByFactor = clueRes.third |
| | | |
| | | // 7. 构建并返回统计结果 |
| | | // 7. 从异常所在地区和溯源的场景中统计聚焦区域 |
| | | val focusRegion = calFocusRegion(clues) |
| | | |
| | | // 8. 构建并返回统计结果 |
| | | return Summary( |
| | | startTime = startTime, |
| | | endTime = endTime, |
| | |
| | | countByDegree = countByDegree, |
| | | probCount = probCount, |
| | | highRiskSceneCount = highRiskSceneCount, |
| | | probByFactor = probByFactor |
| | | probByFactor = probByFactor, |
| | | focusRegion = focusRegion |
| | | ) |
| | | } |
| | | |
| | |
| | | } |
| | | return Triple(probCount, highRiskSceneCount, probByFactor) |
| | | } |
| | | |
| | | private fun calFocusRegion(clues: List<PollutedClue?>): List<String> { |
| | | // 统计每个区域或场景出现的次数 |
| | | val focusArea = mutableMapOf<String, Int>() |
| | | val focusScene = mutableMapOf<String, Int>() |
| | | clues.forEach { c-> |
| | | if (c?.msgType == MsgType.PolClue.value) { |
| | | if (!c.pollutedArea?.address.isNullOrBlank()) { |
| | | if (focusArea.containsKey(c.pollutedArea?.address)) { |
| | | focusArea[c.pollutedArea?.address!!] = focusArea[c.pollutedArea?.address]!! + 1 |
| | | } else { |
| | | focusArea[c.pollutedArea?.address!!] = 1 |
| | | } |
| | | } |
| | | c.pollutedSource?.sceneList?.forEach { s-> |
| | | if (s.name != null) { |
| | | if (focusScene.containsKey(s.name!!)) { |
| | | focusScene[s.name!!] = focusScene[s.name!!]!! + 1 |
| | | } else { |
| | | focusScene[s.name!!] = 1 |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return focusArea.entries.sortedByDescending { it.value }.map { it.key }.take(FOCUS_AREA_COUNT) + |
| | | focusScene.entries.sortedByDescending { it.value }.map { it.key }.take(FOCUS_AREA_COUNT) |
| | | } |
| | | } |
| | |
| | | */ |
| | | fun generateMissionList(missionClues: List<Pair<Mission, List<PollutedClue?>>>): List<MissionInventory.MissionInfo> |
| | | |
| | | fun generateMissionInfo( |
| | | keyScenes: List<SceneInfo?>, |
| | | missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>, |
| | | ): List<MissionInventory.MissionInfo> |
| | | |
| | | /** |
| | | * 获取走航任务详情(按时间和区域筛选) |
| | | * 根据时间范围和区域查询并生成详细的任务报告,包含关键场景和数据统计 |
| | |
| | | factorTypes: List<FactorType>, |
| | | gridLen: Int, |
| | | gridCells: List<GridCell>, |
| | | dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>, |
| | | dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionInfo>, List<GridDataDetailMixVo>>>, |
| | | ): List<MissionGridFusion.GridFusionByAQI> |
| | | } |
| | |
| | | * @see generateMissionList 重载方法,处理已关联的数据对 |
| | | */ |
| | | override fun generateMissionList(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionInventory.MissionInfo> { |
| | | val missionClues = missionRep.findByAreaAndTime(areaVo, startTime, endTime).filterNotNull().map { |
| | | it to sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?> |
| | | // val missionClues = missionRep.findByAreaAndTime(areaVo, startTime, endTime).filterNotNull().map { |
| | | // it to sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?> |
| | | // } |
| | | // return generateMissionList(missionClues) |
| | | val missionCluesData = missionRep.findByAreaAndTime(areaVo, startTime, endTime).filterNotNull().map { |
| | | Triple( |
| | | it, |
| | | sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?>, |
| | | realTimeDataRep.fetchData(it) |
| | | ) |
| | | } |
| | | return generateMissionList(missionClues) |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | | SceneType.TYPE19.value, |
| | | SceneType.TYPE20.value, |
| | | SceneType.TYPE21.value |
| | | ) |
| | | ) |
| | | return generateMissionInfo(keyScenes, missionCluesData) |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | override fun generateMissionList(missionClues: List<Pair<Mission, List<PollutedClue?>>>): List<MissionInventory.MissionInfo> { |
| | | return MissionInventory().generateMissionList(missionClues) |
| | | } |
| | | |
| | | override fun generateMissionInfo( |
| | | keyScenes: List<SceneInfo?>, |
| | | missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>, |
| | | ): List<MissionInventory.MissionInfo> { |
| | | return missionCluesData.map { |
| | | MissionInventory().generateMissionInfo(keyScenes, it.first, it.second, it.third) |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | SceneType.TYPE21.value |
| | | ) |
| | | ) |
| | | val missionDetails = generateMissionDetail(keyScenes, missionCluesData) |
| | | val missionInfos = generateMissionInfo(keyScenes, missionCluesData) |
| | | |
| | | return@mapNotNull Triple(degree, missionDetails, gridDataDetailMixVos) |
| | | return@mapNotNull Triple(degree, missionInfos, gridDataDetailMixVos) |
| | | }.filter { it.second.isNotEmpty() } |
| | | |
| | | return generateGridFusion(factorTypes, gridLen, gridCells, gridDataDetailList) |
| | |
| | | factorTypes: List<FactorType>, |
| | | gridLen: Int, |
| | | gridCells: List<GridCell>, |
| | | dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>, |
| | | dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionInfo>, List<GridDataDetailMixVo>>>, |
| | | ): List<MissionGridFusion.GridFusionByAQI> { |
| | | return MissionGridFusion(sceneInfoRep).generateGridFusion(factorTypes, gridLen, gridCells, dataList) |
| | | } |