feiyu02
2025-09-04 707b00a0ca6604c249a110b376ac1e44e408e624
2025.9.4
1. 新增走航任务统计功能(待完成)
已修改7个文件
已添加2个文件
347 ■■■■■ 文件已修改
src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/repository/SceneInfoRep.kt 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/eunm/PollutionDegree.kt 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
package com.flightfeather.uav.biz.report
import com.flightfeather.uav.common.net.AMapService
import com.flightfeather.uav.common.utils.MapUtil
import com.flightfeather.uav.domain.entity.GridCell
import com.flightfeather.uav.domain.entity.SceneInfo
import com.flightfeather.uav.domain.entity.getByFactorType
import com.flightfeather.uav.domain.repository.SceneInfoRep
import com.flightfeather.uav.lightshare.bean.GridDataDetailMixVo
import com.flightfeather.uav.lightshare.eunm.PollutionDegree
import com.flightfeather.uav.socket.eunm.FactorType
/**
 * èµ°èˆªç½‘格叠加
 * @date 2025/9/4
 * @author feiyu02
 */
class MissionGridFusion(private val sceneInfoRep: SceneInfoRep) {
    /**
     * æŒ‰AQI等级叠加网格
     */
    class GridFusionByAQI {
        var pollutionDegree: String? = null
        var gridLen:Int? = null
        var missionList: MutableList<MissionInventory.MissionDetail> = mutableListOf()
        var gridFusionList: MutableList<FusionGrid> = mutableListOf()
        var highRiskGridList: MutableList<HighRiskGridByFactor> = mutableListOf()
    }
    /**
     * å åŠ ç½‘æ ¼
     */
    class FusionGrid(
        val cell: GridCell,
        val data: GridDataDetailMixVo,
    )
    /**
     * ç›‘测因子的高风险叠加网格
     */
    class HighRiskGridByFactor{
        // å› å­ç±»åž‹
        var factorType: FactorType? = null
        // é«˜é£Žé™©ç½‘格信息
        var highRiskGrid: FusionGrid? = null
        // å› å­å€¼
        var factorValue: Float? = null
        // æ¶‰åŠçš„街镇
        var town: String? = null
        // å››è‡³èŒƒå›´ï¼Œé¡ºåºä¸ºæœ€å°ç»åº¦ï¼Œæœ€å¤§ç»åº¦, æœ€å°çº¬åº¦ï¼Œæœ€å¤§çº¬åº¦
        var bounds: List<Double>? = null
        // é«˜é£Žé™©åœºæ™¯åˆ—表
        var highRiskScenes:List<SceneInfo?>? = null
    }
    /**
     * ç”Ÿæˆç½‘格融合数据,按AQI等级分组并计算各监测因子的高风险区域
     *
     * @param factorTypes éœ€è¦åˆ†æžçš„监测因子类型列表
     * @param gridLen ç½‘格边长(ç±³)
     * @param gridCells ç½‘格单元列表,包含网格的地理信息和索引
     * @param dataList ä¸‰å…ƒç»„列表,每个元素包含:
     *                 - æ±¡æŸ“等级(PollutionDegree)
     *                 - èµ°èˆªä»»åŠ¡è¯¦æƒ…åˆ—è¡¨(MissionDetail)
     *                 - ç½‘格数据详情列表(GridDataDetailMixVo)
     * @return æŒ‰AQI等级分组的网格融合结果列表,每个元素包含该等级下的所有网格数据和高风险分析
     */
    fun generateGridFusion(
        factorTypes: List<FactorType>,
        gridLen: Int,
        gridCells: List<GridCell>,
        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>,
    ): List<GridFusionByAQI> {
        return dataList.map {
            GridFusionByAQI().apply {
                pollutionDegree = it.first.des
                this.gridLen = gridLen
                missionList.addAll(it.second)
                gridFusionList.addAll(it.third.map { gdm ->
                    val grid = gridCells.find { it.cellIndex == gdm.cellId }
                        ?: throw IllegalArgumentException("网格组${gdm.groupId}中,单元ID: ${gdm.cellId} ä¸å­˜åœ¨")
                    FusionGrid(grid, gdm)
                })
                highRiskGridList.addAll(factorTypes.map { f->
                    HighRiskGridByFactor().apply {
                        factorType = f
                        highRiskGrid = gridFusionList.sortedBy { gf->gf.data.rank }.firstOrNull()
                        if (highRiskGrid != null) {
                            factorValue = highRiskGrid!!.data.getByFactorType(f)
                            if (highRiskGrid!!.cell.longitude != null && highRiskGrid!!.cell.latitude != null) {
                                Thread.sleep(50)
                                val address = AMapService.reGeo(MapUtil.wgs84ToGcj02(
                                    highRiskGrid!!.cell.longitude.toDouble()
                                            to highRiskGrid!!.cell.latitude.toDouble()
                                ))
                                town = address.township + address.street
                            }
                            val polygon = listOf(
                                highRiskGrid!!.cell.point1Lon.toDouble() to highRiskGrid!!.cell.point1Lat.toDouble(),
                                highRiskGrid!!.cell.point2Lon.toDouble() to highRiskGrid!!.cell.point2Lat.toDouble(),
                                highRiskGrid!!.cell.point3Lon.toDouble() to highRiskGrid!!.cell.point3Lat.toDouble(),
                                highRiskGrid!!.cell.point4Lon.toDouble() to highRiskGrid!!.cell.point4Lat.toDouble(),
                            )
                            bounds = MapUtil.calFourBoundaries(polygon)
                            highRiskScenes = sceneInfoRep.findByPolygon(polygon)
                        }
                    }
                })
            }
        }
    }
}
src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt
@@ -94,7 +94,7 @@
            Triple(degree, count, count.toDouble() / totalCount)
        }
        // 6. é—®é¢˜ç›¸å…³ç»Ÿè®¡ï¼ˆç¤ºä¾‹ï¼šæ­¤å¤„假设需关联其他表,暂返回0,实际需根据业务补充)
        // 6. é—®é¢˜ç›¸å…³ç»Ÿè®¡
        val clueRes = calClue(clues)
        val probCount = clueRes.first // éœ€å…³è”问题表统计
        val highRiskSceneCount = clueRes.second // éœ€å…³è”场景表统计
@@ -116,9 +116,9 @@
    }
    private fun calClue(clues: List<PollutedClue?>): Triple<Int, Int, List<Triple<String, Int, Double>>> {
        var probCount = 0 // éœ€å…³è”问题表统计
        var highRiskSceneCount = 0 // éœ€å…³è”场景表统计
        val probByFactorMap = mutableMapOf<FactorType, Int>() // éœ€å…³è”因子表统计
        var probCount = 0
        var highRiskSceneCount = 0
        val probByFactorMap = mutableMapOf<FactorType, Int>()
        clues.forEach { c ->
            if (c?.msgType == MsgType.PolClue.value) {
                c.pollutedSource?.sceneList?.size?.let { s -> highRiskSceneCount += s }
src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt
@@ -143,6 +143,26 @@
    }
}
fun GridDataDetail.getByFactorType(type: FactorType?): Float? {
    return when (type) {
        NO2 -> no2
        CO -> co
        H2S -> h2s
        SO2 -> so2
        O3 -> o3
        PM25 -> pm25
        PM10 -> pm10
        TEMPERATURE -> temperature
        HUMIDITY -> humidity
        VOC -> voc
        NOI -> noi
        WIND_SPEED -> windSpeed
        WIND_DIRECTION -> windDirection
        NO -> no
        else -> null
    }
}
fun List<GridDataDetail>.avg(): GridDataDetail {
    //风向采用单位矢量法求取均值
    var u = .0//东西方位分量总和
src/main/kotlin/com/flightfeather/uav/domain/repository/SceneInfoRep.kt
@@ -1,5 +1,6 @@
package com.flightfeather.uav.domain.repository
import com.flightfeather.uav.common.utils.MapUtil
import com.flightfeather.uav.domain.entity.SceneInfo
import com.flightfeather.uav.domain.mapper.SceneInfoMapper
import com.flightfeather.uav.lightshare.bean.AreaVo
@@ -42,6 +43,23 @@
        })
    }
    /**
     * æ ¹æ®å¤šè¾¹å½¢ç­›é€‰åœºæ™¯
     * @param polygon å¤šè¾¹å½¢åæ ‡åˆ—表,顺序为顺时针或逆时针(要求使用火星坐标系)
     * @return å¤šè¾¹å½¢å†…的场景列表
     */
    fun findByPolygon(polygon: List<Pair<Double, Double>>): List<SceneInfo?> {
        // è®¡ç®—多边形四至范围
        val bounds = MapUtil.calFourBoundaries(polygon)
        val sceneList = findByCoordinateRange(bounds)
        // ç­›é€‰æ˜¯å¦åœ¨åå‘溯源区域多边形内部
        return sceneList.filter { scene ->
            scene ?: false
            val point = scene!!.longitude.toDouble() to scene.latitude.toDouble()
            MapUtil.isPointInPolygon(point, polygon)
        }
    }
    fun findBySceneTypes(sceneTypes: List<Int>): List<SceneInfo?> {
        if (sceneTypes.isEmpty()){
            return emptyList()
src/main/kotlin/com/flightfeather/uav/lightshare/eunm/PollutionDegree.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.flightfeather.uav.lightshare.eunm
/**
 * ç©ºæ°”质量等级
 * @date 2025/9/4
 * @author feiyu02
 */
enum class PollutionDegree(val des: String, val value: Int, val minAqi: Int) {
    AQI_1("优", 1, 0),
    AQI_2("良", 2, 51),
    AQI_3("轻度污染", 3, 101),
    AQI_4("中度污染", 4, 151),
    AQI_5("重度污染", 5, 201),
    AQI_6("严重污染", 6, 301);
    companion object {
        fun getByValue(value: Int): PollutionDegree {
            return values().firstOrNull { it.value == value } ?: AQI_1
        }
        fun getByAqi(aqi: Int): PollutionDegree {
            return values().reversed().firstOrNull { it.minAqi <= aqi } ?: AQI_1
        }
        fun getByDes(des: String): PollutionDegree {
            return values().firstOrNull { it.des == des } ?: AQI_1
        }
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt
@@ -1,5 +1,7 @@
package com.flightfeather.uav.lightshare.eunm
import com.flightfeather.uav.socket.eunm.FactorType
/**
 * åœºæ™¯ç±»åž‹
 * @date 2025/6/2
@@ -26,5 +28,45 @@
    TYPE18(18, "商业体"),
    TYPE19(19, "国控点"),
    TYPE20(20, "市控点"),
    TYPE21(21, "小微站"),
    TYPE21(21, "小微站");
    companion object {
        fun getByFactorType(factorType: FactorType): List<SceneType> {
            return when (factorType) {
                // æ°®æ°§åŒ–合物,一般由于机动车尾气,同步计算CO
                FactorType.NO,
                FactorType.NO2 -> {
                    listOf(TYPE6, TYPE10, TYPE17)
                }
                FactorType.CO -> listOf(TYPE6, TYPE10, TYPE17)
                FactorType.H2S -> emptyList()
                FactorType.SO2 -> emptyList()
                FactorType.O3 -> emptyList()
                // a) pm2.5、pm10特别高,两者在各情况下同步展示,pm2.5占pm10的比重变化,比重越高,越有可能是餐饮
                // b) pm10特别高、pm2.5较高,大颗粒扬尘污染,只展示pm10,pm2.5占pm10的比重变化,工地为主
                FactorType.PM25,
                FactorType.PM10,
                    -> {
                    listOf(
                        TYPE1,
                        TYPE2,
                        TYPE3,
                        TYPE14,
                        TYPE5
                    )
                }
                // c) VOC较高,同比计算pm2.5的量级,可能存在同步偏高(汽修、加油站), åŒæ­¥è®¡ç®—O3是否有高值
                // d) VOC较高,处于加油站(车辆拥堵情况),CO一般较高, åŒæ­¥è®¡ç®—O3是否有高值
                FactorType.VOC -> {
                    listOf(TYPE6, TYPE17, TYPE12)
                }
                else -> emptyList()
            }
        }
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
@@ -1,15 +1,20 @@
package com.flightfeather.uav.lightshare.service
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
import com.flightfeather.uav.biz.report.MissionGridFusion
import com.flightfeather.uav.biz.report.MissionInventory
import com.flightfeather.uav.biz.report.MissionInventory.MissionDetail
import com.flightfeather.uav.biz.report.MissionRiskArea
import com.flightfeather.uav.biz.report.MissionSummary
import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.domain.entity.GridCell
import com.flightfeather.uav.domain.entity.Mission
import com.flightfeather.uav.domain.entity.SceneInfo
import com.flightfeather.uav.lightshare.bean.AreaVo
import com.flightfeather.uav.lightshare.bean.GridDataDetailMixVo
import com.flightfeather.uav.lightshare.eunm.PollutionDegree
import com.flightfeather.uav.socket.eunm.FactorType
import java.util.*
/**
@@ -87,4 +92,14 @@
    fun generateClueByRiskArea(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionRiskArea.ClueByArea>
    fun generateClueByRiskArea(keyScenes: List<SceneInfo?>, pollutedClues: List<PollutedClue?>): List<MissionRiskArea.ClueByArea>
    fun generateGridFusion(factorTypes: List<FactorType>, startTime: Date, endTime: Date, areaVo: AreaVo):
            List<MissionGridFusion.GridFusionByAQI>
    fun generateGridFusion(
        factorTypes: List<FactorType>,
        gridLen: Int,
        gridCells: List<GridCell>,
        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>,
    ): List<MissionGridFusion.GridFusionByAQI>
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt
@@ -3,6 +3,7 @@
import com.flightfeather.uav.biz.FactorFilter
import com.flightfeather.uav.biz.dataanalysis.ExceptionAnalysisController
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
import com.flightfeather.uav.biz.report.MissionGridFusion
import com.flightfeather.uav.biz.report.MissionInventory
import com.flightfeather.uav.biz.report.MissionRiskArea
import com.flightfeather.uav.biz.report.MissionSummary
@@ -10,14 +11,15 @@
import com.flightfeather.uav.common.exception.BizException
import com.flightfeather.uav.common.location.LocationRoadNearby
import com.flightfeather.uav.common.utils.GsonUtils
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.domain.entity.Mission
import com.flightfeather.uav.domain.entity.SceneInfo
import com.flightfeather.uav.domain.entity.*
import com.flightfeather.uav.domain.mapper.MissionMapper
import com.flightfeather.uav.domain.repository.*
import com.flightfeather.uav.lightshare.bean.AreaVo
import com.flightfeather.uav.lightshare.bean.GridDataDetailMixVo
import com.flightfeather.uav.lightshare.eunm.PollutionDegree
import com.flightfeather.uav.lightshare.eunm.SceneType
import com.flightfeather.uav.lightshare.service.DataAnalysisService
import com.flightfeather.uav.lightshare.service.SatelliteDataCalculateService
import com.flightfeather.uav.socket.eunm.FactorType
import com.flightfeather.uav.socket.sender.MsgType
import org.springframework.stereotype.Service
@@ -39,7 +41,9 @@
    private val locationRoadNearby: LocationRoadNearby,
    private val segmentInfoRep: SegmentInfoRep,
    private val sourceTraceRep: SourceTraceRep,
    private val sceneInfoRep: SceneInfoRep
    private val sceneInfoRep: SceneInfoRep,
    private val satelliteGridRep: SatelliteGridRep,
    private val satelliteDataCalculateService: SatelliteDataCalculateService
) : DataAnalysisService {
    /**
@@ -204,4 +208,68 @@
    ): List<MissionRiskArea.ClueByArea> {
        return MissionRiskArea().generateClueByRiskArea(keyScenes, pollutedClues)
    }
    override fun generateGridFusion(
        factorTypes: List<FactorType>,
        startTime: Date,
        endTime: Date,
        areaVo: AreaVo,
    ): List<MissionGridFusion.GridFusionByAQI> {
        val gridLen = 100
        // æŸ¥è¯¢100米网格的具体网格数据
        val gridGroup = satelliteGridRep.fetchGridGroup(GridGroup().apply {
            type = "sub"
            length = gridLen.toDouble()
            provinceCode = areaVo.provinceCode
            cityCode = areaVo.cityCode
            districtCode = areaVo.districtCode
        }).firstOrNull() ?: throw BizException("未查询到100米网格")
        val gridCells = satelliteGridRep.fetchGridCell(gridGroup.id).filterNotNull()
        // æŸ¥è¯¢èŒƒå›´å†…的所有走航任务
        val missions = missionRep.findByAreaAndTime(areaVo, startTime, endTime)
        // æ ¹æ®ç©ºæ°”质量等级分类
        val missionGroups = missions.groupBy { PollutionDegree.getByDes(it?.pollutionDegree ?: "") }
        // æŸ¥è¯¢æ¯ä¸ªç­‰çº§ä¸‹çš„走航任务对应的网格数据(如果没有数据则剔除该任务)
        val gridDataDetailList = missionGroups.mapNotNull { (degree, missionList) ->
            // ç­›é€‰å‡ºæœ‰ç½‘格融合数据的走航任务(同时获取对应的融合数据id列表)
            val gridDataIds = mutableListOf<Int>()
            val validMissions = missionList.filter {mission ->
                val gridData = satelliteGridRep.fetchGridData(GridData().apply { missionCode = mission?.missionCode }).firstOrNull()
                val res = gridData != null
                if (res) gridDataIds.add(gridData?.id ?: 0)
                res
            }
            // åˆå¹¶æ¯ä¸ªç­‰çº§ä¸‹çš„网格数据
            val gridDataDetailMixVos = satelliteDataCalculateService.mixUnderwayGridData(gridGroup.id, gridDataIds)
            // ç»Ÿè®¡æ¯ä¸ªèµ°èˆªä»»åŠ¡çš„èµ°èˆªè¯¦æƒ…ä¿¡æ¯
            val missionCluesData = validMissions.filterNotNull().map {
                Triple(
                    it,
                    sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?>,
                    realTimeDataRep.fetchData(it)
                )
            }
            val keyScenes = sceneInfoRep.findBySceneTypes(
                listOf(
                    SceneType.TYPE19.value,
                    SceneType.TYPE20.value,
                    SceneType.TYPE21.value
                )
            )
            val missionDetails = generateMissionDetail(keyScenes, missionCluesData)
            return@mapNotNull Triple(degree, missionDetails, gridDataDetailMixVos)
        }.filter { it.second.isNotEmpty() }
        return generateGridFusion(factorTypes, gridLen, gridCells, gridDataDetailList)
    }
    override fun generateGridFusion(
        factorTypes: List<FactorType>,
        gridLen: Int,
        gridCells: List<GridCell>,
        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>,
    ): List<MissionGridFusion.GridFusionByAQI> {
        return MissionGridFusion(sceneInfoRep).generateGridFusion(factorTypes, gridLen, gridCells, dataList)
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt
@@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonFormat
import com.flightfeather.uav.lightshare.bean.AreaVo
import com.flightfeather.uav.lightshare.service.DataAnalysisService
import com.flightfeather.uav.socket.eunm.FactorType
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import io.swagger.annotations.ApiParam
@@ -115,4 +116,27 @@
            areaVo
        )
    }
    @ApiOperation(value = "叠加融合分析")
    @PostMapping("/report/gridFusion")
    fun generateGridFusion(
        @ApiParam("开始时间") @RequestParam
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        startTime: LocalDateTime,
        @ApiParam("结束时间") @RequestParam
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        endTime: LocalDateTime,
        @ApiParam("需要统计的监测因子", example = "NO2, CO") @RequestParam
        factorTypes: String,
        @ApiParam("区域") @RequestBody areaVo: AreaVo,
    ) = resPack {
        dataAnalysisService.generateGridFusion(
            factorTypes.split(",").map { FactorType.valueOf(it) },
            Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()),
            Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()),
            areaVo
        )
    }
}