feiyu02
2025-09-12 61871594dfa0a5ac2c4d895d9ec4034feba57094
2025.9.5
1. 新增走航任务统计功能
已修改5个文件
249 ■■■■ 文件已修改
src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt 151 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt
@@ -23,7 +23,7 @@
    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()
    }
@@ -70,7 +70,7 @@
        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 {
@@ -85,7 +85,7 @@
                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) {
src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt
@@ -26,13 +26,21 @@
        // 溯源问题场景数
        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
    }
    /**
@@ -74,6 +82,87 @@
        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
    }
    /**
     * 生成走航任务详细信息
     * 整合走航任务基本信息、关键场景、数据统计和异常数量,生成完整的任务详情报告
@@ -93,43 +182,51 @@
        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
    }
src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt
@@ -2,11 +2,9 @@
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
@@ -16,6 +14,10 @@
 * @author feiyu02
 */
class MissionSummary() {
    companion object {
        private const val FOCUS_AREA_COUNT = 2
    }
    data class Summary(
        // 汇总周期开始时间
@@ -37,7 +39,9 @@
        // 高风险场景总数
        val highRiskSceneCount:Int,
        // 问题按监测因子类型分布情况, <因子类型,次数,占比>
        val probByFactor:List<Triple<String, Int, Double>>
        val probByFactor: List<Triple<String, Int, Double>>,
        // 聚焦区域或场景
        val focusRegion: List<String>,
    )
    /**
@@ -61,7 +65,8 @@
                countByDegree = emptyList(),
                probCount = 0,
                highRiskSceneCount = 0,
                probByFactor = emptyList()
                probByFactor = emptyList(),
                focusRegion = emptyList()
            )
        }
@@ -100,7 +105,10 @@
        val highRiskSceneCount = clueRes.second // 需关联场景表统计
        val probByFactor = clueRes.third
        // 7. 构建并返回统计结果
        // 7. 从异常所在地区和溯源的场景中统计聚焦区域
        val focusRegion = calFocusRegion(clues)
        // 8. 构建并返回统计结果
        return Summary(
            startTime = startTime,
            endTime = endTime,
@@ -111,7 +119,8 @@
            countByDegree = countByDegree,
            probCount = probCount,
            highRiskSceneCount = highRiskSceneCount,
            probByFactor = probByFactor
            probByFactor = probByFactor,
            focusRegion = focusRegion
        )
    }
@@ -137,4 +146,32 @@
        }
        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)
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
@@ -70,6 +70,11 @@
     */
    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>
    /**
     * 获取走航任务详情(按时间和区域筛选)
     * 根据时间范围和区域查询并生成详细的任务报告,包含关键场景和数据统计
@@ -100,6 +105,6 @@
        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>
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt
@@ -119,10 +119,25 @@
     * @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)
    }
    /**
@@ -134,6 +149,15 @@
     */
    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)
        }
    }
    /**
@@ -256,9 +280,9 @@
                    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)
@@ -268,7 +292,7 @@
        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)
    }