2025.12.19
1. 动态溯源相关分析逻辑调整;
2. 走航报告接口参数调整;
| | |
| | | import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue |
| | | import com.flightfeather.uav.common.utils.MapUtil |
| | | import com.flightfeather.uav.domain.entity.SceneInfo |
| | | import com.flightfeather.uav.domain.entity.avg |
| | | import com.flightfeather.uav.socket.eunm.AggregatedFactorType |
| | | import com.flightfeather.uav.socket.eunm.FactorType |
| | | |
| | |
| | | pollutedClues.forEach { pollutedClue -> |
| | | if (pollutedClue == null) return@forEach |
| | | // 计算单个PollutedClue的均值经纬度 |
| | | val wgs84Center = pollutedClue.pollutedData?.getExceptionCenter() ?: return@forEach |
| | | val wgs84Center = pollutedClue.pollutedData?.exceptionCenter() ?: return@forEach |
| | | |
| | | // 坐标转换 |
| | | val gcj02Point = MapUtil.wgs84ToGcj02(wgs84Center) |
| | |
| | | package com.flightfeather.uav.biz.sourcetrace.model |
| | | |
| | | import com.flightfeather.uav.biz.FactorFilter |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag |
| | | import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig |
| | | import com.flightfeather.uav.common.net.AMapService |
| | | import com.flightfeather.uav.common.utils.MapUtil |
| | | import com.flightfeather.uav.domain.entity.BaseRealTimeData |
| | | import com.flightfeather.uav.domain.entity.avg |
| | | import kotlin.math.PI |
| | | |
| | | /** |
| | |
| | | windLevelCondition: RTExcWindLevelConfig.WindLevelCondition, |
| | | ) { |
| | | |
| | | val avgData = pollutedData.getExceptionAvgData() |
| | | val avgData = pollutedData.exceptionAvgData() |
| | | val pair = avgData.longitude!!.toDouble() to avgData.latitude!!.toDouble() |
| | | polygon = calSector( |
| | | avgData.windDirection?.toDouble() ?: .0, |
| | |
| | | import com.flightfeather.uav.biz.FactorFilter |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType |
| | | import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig |
| | | import com.flightfeather.uav.common.utils.DateUtil |
| | | import com.flightfeather.uav.domain.entity.BaseRealTimeData |
| | | import com.flightfeather.uav.domain.entity.avg |
| | |
| | | constructor(dataIndexList: List<Int>, factorType: FactorType){ |
| | | this.dataIndexList = dataIndexList |
| | | this.factorType = factorType |
| | | val first = getFirstDataValue()?.toDouble() |
| | | val last = getLastDataValue()?.toDouble() |
| | | val first = firstDataValue()?.toDouble() |
| | | val last = lastDataValue()?.toDouble() |
| | | if (first != null && last != null) { |
| | | per = round((last - first) / first * 100) / 100 |
| | | rate = round((last - first) / DEFAULT_PERIOD * 100) / 100 |
| | |
| | | * 获取异常数据的第一个数据 |
| | | * !!!!第一个数据其实是首个异常数据的前一个数据值!!!! |
| | | */ |
| | | fun getFirstData(): BaseRealTimeData? { |
| | | fun firstData(): BaseRealTimeData? { |
| | | return dataIndexList?.firstOrNull()?.let { |
| | | val i = if (it > 0) it - 1 else it |
| | | historyDataList[i].toBaseRealTimeData(BaseRealTimeData::class.java) |
| | | } |
| | | } |
| | | fun getFirstDataValue(): Float? { |
| | | return getFirstData()?.getByFactorType(factorType) |
| | | fun firstDataValue(): Float? { |
| | | return firstData()?.getByFactorType(factorType) |
| | | } |
| | | |
| | | /** |
| | | * 获取异常数据的最后一个数据 |
| | | */ |
| | | fun getLastData(): BaseRealTimeData? { |
| | | fun lastData(): BaseRealTimeData? { |
| | | return dataIndexList?.lastOrNull()?.let { |
| | | historyDataList[it].toBaseRealTimeData(BaseRealTimeData::class.java) |
| | | } |
| | | } |
| | | fun getLastDataValue(): Float? { |
| | | return getLastData()?.getByFactorType(factorType) |
| | | fun lastDataValue(): Float? { |
| | | return lastData()?.getByFactorType(factorType) |
| | | } |
| | | } |
| | | |
| | |
| | | /** |
| | | * 获取异常数据 |
| | | */ |
| | | fun getExceptionData(): List<BaseRealTimeData>? { |
| | | fun exceptionData(): List<BaseRealTimeData>? { |
| | | return dataIndexList?.map { historyDataList[it].toBaseRealTimeData(BaseRealTimeData::class.java) } |
| | | } |
| | | |
| | |
| | | * 获取异常数据分段情况 |
| | | * 将连续的异常数据分为一组 |
| | | */ |
| | | fun getExceptionDataGroup(): List<List<Int>> { |
| | | fun exceptionDataGroup(): List<List<Int>> { |
| | | val res = mutableListOf<MutableList<Int>>() |
| | | var curGroup = mutableListOf<Int>() |
| | | var lastIndex = -2 |
| | |
| | | min = s.second |
| | | max = s.third |
| | | |
| | | excGroup = getExceptionDataGroup().map { ExcGroup(it, e.first.main) } |
| | | excGroup = exceptionDataGroup().map { ExcGroup(it, e.first.main) } |
| | | avgPer = excGroup?.mapNotNull { it.per }?.average() |
| | | avgRate = excGroup?.mapNotNull { it.rate }?.average() |
| | | } |
| | |
| | | return factors |
| | | } |
| | | |
| | | fun getExceptionAvgData(): BaseRealTimeData { |
| | | val exceptionDataList = statisticMap.flatMap { it.value.getExceptionData() ?: emptyList() } |
| | | fun exceptionAvgData(): BaseRealTimeData { |
| | | val exceptionDataList = statisticMap.flatMap { it.value.exceptionData() ?: emptyList() } |
| | | val avgData = exceptionDataList.avg() |
| | | return avgData |
| | | } |
| | | /** |
| | | * 获取异常数据中心坐标(异常数据中经度纬度的平均值) |
| | | */ |
| | | fun getExceptionCenter(): Pair<Double, Double>? { |
| | | val avgData = getExceptionAvgData() |
| | | fun exceptionCenter(): Pair<Double, Double>? { |
| | | val avgData = exceptionAvgData() |
| | | val wgs84Lng = avgData.longitude?.toDouble() |
| | | val wgs84Lat = avgData.latitude?.toDouble() |
| | | return if (wgs84Lng == null || wgs84Lat == null) null else Pair(wgs84Lng, wgs84Lat) |
| | |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType |
| | | import com.flightfeather.uav.common.utils.DateUtil |
| | | import com.flightfeather.uav.common.utils.MapUtil |
| | | import com.flightfeather.uav.domain.entity.BaseRealTimeData |
| | | import com.flightfeather.uav.domain.entity.SceneInfo |
| | | import com.flightfeather.uav.domain.repository.SceneInfoRep |
| | | import com.flightfeather.uav.lightshare.bean.AreaVo |
| | |
| | | import com.flightfeather.uav.socket.eunm.FactorType |
| | | import org.springframework.beans.BeanUtils |
| | | import org.springframework.web.context.ContextLoader |
| | | import kotlin.math.round |
| | | |
| | | /** |
| | | * 污染来源 |
| | |
| | | pollutedData.statisticMap.entries.forEach { s -> |
| | | val txtArr = mutableListOf<String>() |
| | | s.value.excGroup?.forEach exception@{ p -> |
| | | val preValue = p.getFirstDataValue() |
| | | val curValue = p.getLastDataValue() |
| | | val per = p.per?.times(100) |
| | | val preValue = p.firstDataValue() |
| | | val curValue = p.lastDataValue() |
| | | val per = round(p.per?.times(100) ?: .0) |
| | | val rate = p.rate |
| | | if (preValue == null || curValue == null || per == null) return@exception |
| | | when (pollutedData.exceptionType) { |
| | |
| | | return if (res.isEmpty()) null else res[0] |
| | | } |
| | | |
| | | val stMsgCache = mutableMapOf<String, List<BaseExceptionResult?>>() |
| | | fun fetchList( |
| | | deviceCode: String, |
| | | startTime: Date, |
| | | endTime: Date, |
| | | msgType: MsgType? = null, |
| | | minPer: Double? = 0.5, |
| | | ): List<BaseExceptionResult?> { |
| | | var stMsgList = listOf<BaseExceptionResult?>() |
| | | val key = "${deviceCode}_${startTime.time}_${endTime.time}_${msgType?.value}" |
| | | if (stMsgCache.containsKey(key)) { |
| | | stMsgList = stMsgCache[key]!! |
| | | } |
| | | if (stMsgList.isEmpty()) { |
| | | var res = sourceTraceMsgBlobMapper.selectWithBlob(deviceCode, startTime, endTime) |
| | | if (msgType !== null) { |
| | | res = res.filter { it?.msgType == msgType.value } |
| | | } |
| | | |
| | | return res.map { stm -> |
| | | stMsgList = res.map { stm -> |
| | | when (stm?.msgType) { |
| | | MsgType.PolClue.value, |
| | | MsgType.DataChange.value, |
| | |
| | | else -> null |
| | | } |
| | | } |
| | | stMsgCache[key] = stMsgList |
| | | } |
| | | // 筛选出异常数据PollutedClue中异常百分比大于minPer的 |
| | | return stMsgList.filter { |
| | | if (it is PollutedClue) { |
| | | var valid = false |
| | | (it as PollutedClue?)?.pollutedData?.statisticMap?.entries?.forEach {sta-> |
| | | if (!valid) valid = (sta.value.avgPer?:.0) > minPer!! |
| | | } |
| | | valid |
| | | } else { |
| | | true |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Transactional |
| | |
| | | |
| | | @ApiParam("需要统计的监测因子", example = "NO2, CO") |
| | | var factorTypes: List<String>? = null |
| | | |
| | | /** 最小污染百分比,用于筛选异常数据点(可选) */ |
| | | @ApiModelProperty(value = "最小污染百分比,用于筛选异常数据点(可选)") |
| | | var minPer: Double? = 0.5 |
| | | } |
| | |
| | | * 获取历史污染溯源结果 |
| | | * 根据任务编号查询并返回历史污染溯源结果 |
| | | * @param missionCode 走航任务编号 |
| | | * @param minPer 最小污染百分比,用于筛选异常数据点(可选) |
| | | * @return 历史污染溯源结果的字符串表示(具体格式需参考实现类) |
| | | */ |
| | | fun fetchHistory(missionCode: String): String |
| | | fun fetchHistory(missionCode: String, minPer: Double?): String |
| | | |
| | | /** |
| | | * 生成走航任务汇总统计 |
| | |
| | | * @param areaVo 区域参数,包含经纬度范围等地理信息 |
| | | * @return 汇总统计对象,包含任务总数、异常率、平均数据等指标 |
| | | */ |
| | | fun generateMissionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary |
| | | fun generateMissionSummary( |
| | | startTime: Date, endTime: Date, areaVo: AreaVo, removeOtherDistrict: Boolean = false, |
| | | removeNoPollutedSource: Boolean = false, minPer: Double? = 0.5, |
| | | ): MissionSummary.Summary |
| | | |
| | | fun generateMissionSummary(missionCode: String): MissionSummary.Summary |
| | | fun generateMissionSummary( |
| | | missionCode: String, minPer: Double? = 0.5, |
| | | ): MissionSummary.Summary |
| | | |
| | | /** |
| | | * 生成走航任务清单(按时间和区域筛选) |
| | |
| | | * @param areaVo 区域参数 |
| | | * @return 走航任务信息列表,每个元素包含任务基本信息和统计数据 |
| | | */ |
| | | fun generateMissionList(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionInventory.MissionInfo> |
| | | fun generateMissionList( |
| | | startTime: Date, endTime: Date, areaVo: AreaVo, removeOtherDistrict: Boolean = false, |
| | | removeNoPollutedSource: Boolean = false, minPer: Double? = 0.5, |
| | | ): List<MissionInventory.MissionInfo> |
| | | |
| | | /** |
| | | * 生成走航任务清单(直接处理任务线索) |
| | |
| | | * @param areaVo 区域参数 |
| | | * @return 任务详情列表,每个元素包含任务完整信息、场景数据和统计结果 |
| | | */ |
| | | fun generateMissionDetail(startTime: Date, endTime: Date, areaVo: AreaVo,granularity: String?): List<MissionDetail> |
| | | fun generateMissionDetail( |
| | | startTime: Date, endTime: Date, areaVo: AreaVo, granularity: String?, |
| | | removeOtherDistrict: Boolean = false, |
| | | removeNoPollutedSource: Boolean = false, |
| | | minPer: Double? = 0.5, |
| | | ): List<MissionDetail> |
| | | |
| | | /** |
| | | * 生成走航任务详情(按任务编号筛选) |
| | |
| | | * @param granularity 数据颗粒度,可选值为SECOND, MINUTE, HOUR, 默认MINUTE |
| | | * @return 任务详情对象,包含任务完整信息、场景数据和统计结果 |
| | | */ |
| | | fun generateMissionDetail(missionCode: String, granularity: String?): MissionDetail |
| | | fun generateMissionDetail( |
| | | missionCode: String, granularity: String?, minPer: Double? = 0.5, |
| | | ): MissionDetail |
| | | |
| | | /** |
| | | * 获取走航任务详情(直接处理任务数据) |
| | |
| | | startTime: Date, |
| | | endTime: Date, |
| | | areaVo: AreaVo, |
| | | removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | removeOtherDistrict: Boolean = false, |
| | | removeNoPollutedSource: Boolean = false, |
| | | minPer: Double? = 0.5, |
| | | ): List<MissionRiskArea.ClassifyClue> |
| | | |
| | | fun generateClueByRiskArea(missionCode: String): List<MissionRiskArea.ClueByArea> |
| | | fun generateClueByRiskArea(missionCode: String, minPer: Double? = 0.5): 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>, |
| | | startTime: Date, |
| | | endTime: Date, |
| | | areaVo: AreaVo, |
| | | removeOtherDistrict: Boolean = false, |
| | | removeNoPollutedSource: Boolean = false, |
| | | minPer: Double? = 0.5, |
| | | ): List<MissionGridFusion.GridFusionByAQI> |
| | | |
| | | fun generateGridFusion( |
| | | factorTypes: List<FactorType>, |
| | |
| | | * 获取历史污染溯源结果 |
| | | * 查询指定任务的历史污染溯源结果并序列化为JSON字符串 |
| | | * @param missionCode 走航任务编码 |
| | | * @param minPer 最小污染百分比,用于筛选异常数据点(可选) |
| | | * @return 历史污染溯源结果的JSON字符串,具体格式由sourceTraceRep实现决定 |
| | | * @throws BizException 当走航任务不存在时抛出 |
| | | */ |
| | | override fun fetchHistory(missionCode: String): String { |
| | | override fun fetchHistory(missionCode: String, minPer: Double?): String { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("走航任务不存在") |
| | | |
| | | val res = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime) |
| | | val res = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime, minPer = minPer ?: 0.5) |
| | | return GsonUtils.gson.toJson(res) |
| | | } |
| | | |
| | |
| | | * @param endTime 统计结束时间(包含) |
| | | * @param areaVo 区域参数,包含省、市、区三级行政区划编码 |
| | | * @return 汇总统计对象,包含任务总数、异常点数量、平均里程等核心指标 |
| | | * @see MissionSummary 汇总统计处理器,封装具体的统计逻辑实现 |
| | | * @see MissionSummary 汇总统计处理器,封装具体统计逻辑的实现 |
| | | */ |
| | | override fun generateMissionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary { |
| | | override fun generateMissionSummary( |
| | | startTime: Date, endTime: Date, areaVo: AreaVo, removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, minPer: Double?, |
| | | ): MissionSummary.Summary { |
| | | val clues = mutableListOf<PollutedClue?>() |
| | | val missions = missionRep.findByAreaAndTime(areaVo, startTime, endTime).onEach { |
| | | it ?: return@onEach |
| | | val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?> |
| | | val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue, minPer ?: 0.5) as List<PollutedClue?> |
| | | clues.addAll(clue) |
| | | } |
| | | filterClue(areaVo, clues, removeOtherDistrict, removeNoPollutedSource) |
| | | val summary = MissionSummary().execute(startTime, endTime, missions, clues) |
| | | return summary |
| | | } |
| | | |
| | | override fun generateMissionSummary(missionCode: String): MissionSummary.Summary { |
| | | override fun generateMissionSummary( |
| | | missionCode: String, minPer: Double?, |
| | | ): MissionSummary.Summary { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("走航任务不存在") |
| | | val clues = sourceTraceRep.fetchList( |
| | | mission.deviceCode, |
| | | mission.startTime, |
| | | mission.endTime, |
| | | MsgType.PolClue |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5, |
| | | ) as List<PollutedClue?> |
| | | val summary = MissionSummary().execute(mission.startTime, mission.endTime, listOf(mission), clues) |
| | | return summary |
| | |
| | | * @see MissionRep.findByAreaAndTime 区域时间筛选数据源 |
| | | * @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?> |
| | | // } |
| | | // return generateMissionList(missionClues) |
| | | override fun generateMissionList( |
| | | startTime: Date, endTime: Date, areaVo: AreaVo, removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, minPer: Double?, |
| | | ): List<MissionInventory.MissionInfo> { |
| | | 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) |
| | | ) |
| | | val clues = sourceTraceRep.fetchList( |
| | | it.deviceCode, |
| | | it.startTime, |
| | | it.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | filterClue(areaVo, clues.toMutableList(), removeOtherDistrict, removeNoPollutedSource) |
| | | Triple(it, clues, realTimeDataRep.fetchData(it)) |
| | | } |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | |
| | | endTime: Date, |
| | | areaVo: AreaVo, |
| | | granularity: String?, |
| | | removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | minPer: Double?, |
| | | ): List<MissionInventory.MissionDetail> { |
| | | 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) |
| | | ) |
| | | val clues = sourceTraceRep.fetchList( |
| | | it.deviceCode, |
| | | it.startTime, |
| | | it.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | filterClue(areaVo, clues.toMutableList(), removeOtherDistrict, removeNoPollutedSource) |
| | | Triple(it, clues, realTimeDataRep.fetchData(it)) |
| | | } |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | |
| | | return generateMissionDetail(keyScenes, missionCluesData, granularity ?: "MINUTE") |
| | | } |
| | | |
| | | override fun generateMissionDetail(missionCode: String, granularity: String?): MissionInventory.MissionDetail { |
| | | override fun generateMissionDetail( |
| | | missionCode: String, |
| | | granularity: String?, |
| | | minPer: Double?, |
| | | ): MissionInventory.MissionDetail { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("任务不存在") |
| | | val missionClues = sourceTraceRep.fetchList( |
| | | mission.deviceCode, |
| | | mission.startTime, |
| | | mission.endTime, |
| | | MsgType.PolClue |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | val realTimeData = realTimeDataRep.fetchData(mission) |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | |
| | | areaVo: AreaVo, |
| | | removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | minPer: Double?, |
| | | ): List<MissionRiskArea.ClassifyClue> { |
| | | val clues = mutableListOf<PollutedClue?>() |
| | | missionRep.findByAreaAndTime(areaVo, startTime, endTime).onEach { |
| | | it ?: return@onEach |
| | | val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?> |
| | | val clue = sourceTraceRep.fetchList( |
| | | it.deviceCode, |
| | | it.startTime, |
| | | it.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | clues.addAll(clue) |
| | | } |
| | | if (removeOtherDistrict) { |
| | | clues.removeIf { |
| | | !areaVo.districtName.isNullOrBlank() && |
| | | (it?.pollutedArea?.address.isNullOrBlank() |
| | | || !it!!.pollutedArea!!.address!!.contains(areaVo.districtName!!)) |
| | | } |
| | | } |
| | | if (removeNoPollutedSource) { |
| | | clues.removeIf { it?.pollutedSource?.sceneList.isNullOrEmpty() } |
| | | } |
| | | // if (removeOtherDistrict) { |
| | | // clues.removeIf { |
| | | // !areaVo.districtName.isNullOrBlank() && |
| | | // (it?.pollutedArea?.address.isNullOrBlank() |
| | | // || !it!!.pollutedArea!!.address!!.contains(areaVo.districtName!!)) |
| | | // } |
| | | // } |
| | | // if (removeNoPollutedSource) { |
| | | // clues.removeIf { it?.pollutedSource?.sceneList.isNullOrEmpty() } |
| | | // } |
| | | filterClue(areaVo, clues, removeOtherDistrict, removeNoPollutedSource) |
| | | return MissionRiskArea().generateClueByRiskArea(clues) |
| | | } |
| | | |
| | | override fun generateClueByRiskArea(missionCode: String): List<MissionRiskArea.ClueByArea> { |
| | | override fun generateClueByRiskArea(missionCode: String, minPer: Double?): List<MissionRiskArea.ClueByArea> { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("任务不存在") |
| | | val pollutedClues = sourceTraceRep.fetchList( |
| | | mission.deviceCode, |
| | | mission.startTime, |
| | | mission.endTime, |
| | | MsgType.PolClue |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | |
| | | startTime: Date, |
| | | endTime: Date, |
| | | areaVo: AreaVo, |
| | | removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | minPer: Double?, |
| | | ): List<MissionGridFusion.GridFusionByAQI> { |
| | | val gridLen = 100 |
| | | // 查询100米网格的具体网格数据 |
| | |
| | | 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 clues = sourceTraceRep.fetchList( |
| | | it.deviceCode, |
| | | it.startTime, |
| | | it.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | filterClue(areaVo, clues.toMutableList(), removeOtherDistrict, removeNoPollutedSource) |
| | | Triple(it, clues, realTimeDataRep.fetchData(it)) |
| | | } |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | |
| | | ): List<MissionGridFusion.GridFusionByAQI> { |
| | | return MissionGridFusion(sceneInfoRep).generateGridFusion(factorTypes, gridLen, gridCells, dataList) |
| | | } |
| | | |
| | | private fun filterClue( |
| | | areaVo: AreaVo, clues: MutableList<PollutedClue?>, removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | ) { |
| | | if (removeOtherDistrict) { |
| | | clues.removeIf { |
| | | !areaVo.districtName.isNullOrBlank() && |
| | | (it?.pollutedArea?.address.isNullOrBlank() |
| | | || !it!!.pollutedArea!!.address!!.contains(areaVo.districtName!!)) |
| | | } |
| | | clues.forEach { |
| | | it?.pollutedSource?.sceneList = it?.pollutedSource?.sceneList?.filter { s-> |
| | | s.districtCode == areaVo.districtCode |
| | | } |
| | | } |
| | | } |
| | | if (removeNoPollutedSource) { |
| | | clues.removeIf { it?.pollutedSource?.sceneList.isNullOrEmpty() } |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | package com.flightfeather.uav.lightshare.web |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat |
| | | import com.flightfeather.uav.common.exception.BizException |
| | | import com.flightfeather.uav.lightshare.bean.AnalysisOption |
| | | 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 |
| | | import org.springframework.format.annotation.DateTimeFormat |
| | | import org.springframework.web.bind.annotation.* |
| | | import java.time.LocalDateTime |
| | | import java.time.ZoneId |
| | | import java.util.* |
| | | |
| | |
| | | @GetMapping("/pollution/trace/history") |
| | | fun fetchHistory( |
| | | @ApiParam("走航任务编号") @RequestParam missionCode: String, |
| | | ) = resPack { dataAnalysisService.fetchHistory(missionCode) } |
| | | @ApiParam("最小污染百分比,用于筛选异常数据点(可选)") @RequestParam(required = false) minPer: Double? = null, |
| | | ) = resPack { dataAnalysisService.fetchHistory(missionCode, minPer) } |
| | | |
| | | @ApiOperation(value = "生成走航任务汇总统计") |
| | | @PostMapping("/report/missionSummary") |
| | |
| | | dataAnalysisService.generateMissionSummary( |
| | | Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | areaVo |
| | | areaVo, |
| | | analysisOption.removeOtherDistrict, |
| | | analysisOption.removeNoPollutedSource, |
| | | analysisOption.minPer, |
| | | ) |
| | | } |
| | | |
| | |
| | | @GetMapping("/report/missionSummary/one") |
| | | fun generateOneMissionSummary( |
| | | @ApiParam("任务编号") @RequestParam missionCode: String, |
| | | ) = resPack { dataAnalysisService.generateMissionSummary(missionCode) } |
| | | @ApiParam("最小污染百分比,用于筛选异常数据点(可选)") @RequestParam(required = false) minPer: Double? = null, |
| | | ) = resPack { dataAnalysisService.generateMissionSummary(missionCode, minPer) } |
| | | |
| | | @ApiOperation(value = "生成走航任务清单") |
| | | @PostMapping("/report/missionList") |
| | |
| | | dataAnalysisService.generateMissionList( |
| | | Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | areaVo |
| | | areaVo, |
| | | analysisOption.removeOtherDistrict, |
| | | analysisOption.removeNoPollutedSource, |
| | | analysisOption.minPer, |
| | | ) |
| | | } |
| | | |
| | |
| | | dataAnalysisService.generateMissionDetail( |
| | | Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | areaVo,granularity |
| | | areaVo,granularity, |
| | | analysisOption.removeOtherDistrict, |
| | | analysisOption.removeNoPollutedSource, |
| | | analysisOption.minPer, |
| | | ) |
| | | } |
| | | |
| | |
| | | @ApiParam("任务编号") @RequestParam missionCode: String, |
| | | @ApiParam("数据颗粒度", allowableValues = "SECOND, MINUTE, HOUR") @RequestParam(required = false) |
| | | granularity: String?, |
| | | ) = resPack { dataAnalysisService.generateMissionDetail(missionCode, granularity) } |
| | | @ApiParam("最小污染百分比,用于筛选异常数据点(可选)") @RequestParam(required = false) minPer: Double? = null, |
| | | ) = resPack { dataAnalysisService.generateMissionDetail(missionCode, granularity, minPer) } |
| | | |
| | | @ApiOperation(value = "走航典型隐患区域统计") |
| | | @PostMapping("/report/clueByRiskArea") |
| | |
| | | Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | areaVo, |
| | | analysisOption.removeOtherDistrict, |
| | | analysisOption.removeNoPollutedSource |
| | | analysisOption.removeNoPollutedSource, |
| | | analysisOption.minPer, |
| | | ) |
| | | } |
| | | |
| | |
| | | @GetMapping("/report/clueByRiskArea/one") |
| | | fun generateOneClueByRiskArea( |
| | | @ApiParam("任务编号") @RequestParam missionCode: String, |
| | | ) = resPack { dataAnalysisService.generateClueByRiskArea(missionCode) } |
| | | @ApiParam("最小污染百分比,用于筛选异常数据点(可选)") @RequestParam(required = false) minPer: Double? = null, |
| | | ) = resPack { dataAnalysisService.generateClueByRiskArea(missionCode, minPer) } |
| | | |
| | | @ApiOperation(value = "叠加融合分析") |
| | | @PostMapping("/report/gridFusion") |
| | |
| | | factorTypes, |
| | | Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()), |
| | | areaVo |
| | | areaVo, |
| | | analysisOption.removeOtherDistrict, |
| | | analysisOption.removeNoPollutedSource, |
| | | analysisOption.minPer, |
| | | ) |
| | | } |
| | | } |
| | |
| | | import java.io.OutputStreamWriter |
| | | import java.text.SimpleDateFormat |
| | | import java.util.* |
| | | import kotlin.math.round |
| | | |
| | | /** |
| | | * @author riku |
| | |
| | | |
| | | @Test |
| | | fun reGeo() { |
| | | val a = AMapService.reGeo(MapUtil.wgs84ToGcj02(121.45017 to 31.274426)) |
| | | println(a) |
| | | val a = 0.7000000000000001 |
| | | println(round(a * 100)) |
| | | } |
| | | |
| | | @Test |
| | |
| | | @Test |
| | | fun autoSourceTrace() { |
| | | val sourceTraceController = SourceTraceController(sceneInfoRep, sourceTraceRep, true) |
| | | val startTime = LocalDateTime.of(2025, 12, 11, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant() |
| | | val endTime = LocalDateTime.of(2025, 12, 11, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant() |
| | | val startTime = LocalDateTime.of(2025, 11, 19, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant() |
| | | // val endTime = LocalDateTime.of(2025, 11, 19, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant() |
| | | // val startTime = LocalDateTime.of(2025, 11, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant() |
| | | val endTime = LocalDateTime.of(2025, 12, 31, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant() |
| | | val missions = missionMapper.selectByExample(Example(Mission::class.java).apply { |
| | | createCriteria().andBetween("startTime", startTime, endTime) |
| | | }) |
| | |
| | | |
| | | @Test |
| | | fun deleteSourceTrace() { |
| | | val startTime = LocalDateTime.of(2025, 12, 11, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant() |
| | | val endTime = LocalDateTime.of(2025, 12, 11, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant() |
| | | val startTime = LocalDateTime.of(2025, 11, 19, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant() |
| | | // val endTime = LocalDateTime.of(2025, 11, 19, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant() |
| | | // val startTime = LocalDateTime.of(2025, 11, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant() |
| | | val endTime = LocalDateTime.of(2025, 12, 31, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant() |
| | | val missions = missionMapper.selectByExample(Example(Mission::class.java).apply { |
| | | createCriteria().andBetween("startTime", startTime, endTime) |
| | | }) |
| | |
| | | |
| | | @Test |
| | | fun fetchHistory() { |
| | | dataAnalysisService.fetchHistory("SH-CN-20250723(01)") |
| | | dataAnalysisService.fetchHistory("SH-CN-20250723(01)", .0) |
| | | } |
| | | |
| | | @Test |
| | |
| | | @Test |
| | | fun calMissionInfo() { |
| | | missionMapper.selectByExample(Example(Mission::class.java).apply { |
| | | createCriteria().andBetween("startTime", "2025-12-05 00:00:00", "2025-12-31 23:59:59") |
| | | createCriteria().andBetween("startTime", "2025-12-11 00:00:00", "2025-12-11 23:59:59") |
| | | }).forEach {mission -> |
| | | mission?.let { missionService.calMissionInfo(it.missionCode) } |
| | | Thread.sleep(1000) |