package com.flightfeather.uav.lightshare.service.impl
|
|
import com.flightfeather.uav.biz.satellite.GridGroupOption
|
import com.flightfeather.uav.biz.satellite.SatelliteGridManage
|
import com.flightfeather.uav.common.exception.BizException
|
import com.flightfeather.uav.common.utils.TimeUtil
|
import com.flightfeather.uav.domain.entity.*
|
import com.flightfeather.uav.domain.repository.MissionRep
|
import com.flightfeather.uav.domain.repository.RealTimeDataRep
|
import com.flightfeather.uav.domain.repository.SatelliteGridRep
|
import com.flightfeather.uav.lightshare.bean.GridDataDetailMixVo
|
import com.flightfeather.uav.lightshare.eunm.GridType
|
import com.flightfeather.uav.lightshare.eunm.SatelliteDataType
|
import com.flightfeather.uav.lightshare.service.SatelliteDataCalculateService
|
import org.springframework.beans.BeanUtils
|
import org.springframework.stereotype.Service
|
import org.springframework.transaction.annotation.Transactional
|
import java.util.Date
|
import kotlin.math.round
|
|
/**
|
* 卫星网格坐标及监测数据二次计算
|
* @date 2025/1/15
|
* @author feiyu02
|
*/
|
@Service
|
class SatelliteDataCalculateServiceImpl(
|
private val satelliteGridRep: SatelliteGridRep,
|
private val realTimeDataRep: RealTimeDataRep,
|
private val missionRep: MissionRep,
|
) : SatelliteDataCalculateService {
|
|
|
override fun calGridVertex(groupId: Int): List<GridCell?> {
|
val cellList = satelliteGridRep.fetchGridCell(groupId)
|
val vertexList = SatelliteGridManage.calGridVertex(cellList.map {
|
if (it?.longitude == null || it.latitude == null) {
|
throw BizException("卫星遥测网格计算顶点坐标点失败,存在中心点坐标为空的情况")
|
}
|
it.longitude?.toDouble()!! to it.latitude?.toDouble()!!
|
})
|
|
cellList.forEachIndexed { i, c ->
|
val v = vertexList[i]
|
c?.point1Lon = v.point1Lon.toBigDecimal()
|
c?.point1Lat = v.point1Lat.toBigDecimal()
|
|
c?.point2Lon = v.point2Lon.toBigDecimal()
|
c?.point2Lat = v.point2Lat.toBigDecimal()
|
|
c?.point3Lon = v.point3Lon.toBigDecimal()
|
c?.point3Lat = v.point3Lat.toBigDecimal()
|
|
c?.point4Lon = v.point4Lon.toBigDecimal()
|
c?.point4Lat = v.point4Lat.toBigDecimal()
|
}
|
|
satelliteGridRep.updateGridCellBatch(cellList)
|
|
return cellList
|
}
|
|
override fun splitGrid(groupId: Int, scale: Int): List<GridCell?> {
|
// 检查该网格属性是否合规
|
val gridGroup =
|
satelliteGridRep.fetchGridGroup(groupId) ?: throw BizException("该网格组不存在,无法进行网格细分")
|
if (gridGroup.length == null) throw BizException("该网格组没有设定网格边长,无法进行网格细分")
|
|
// 检查该网格下该种类的细分网格是否存在,若不存在,则新建
|
val searchGridGroup = GridGroup().apply {
|
type = GridType.Sub.name.lowercase()
|
fatherGroupId = gridGroup.id
|
length = round(gridGroup.length / scale)
|
}
|
val subGridGroupList = satelliteGridRep.fetchGridGroup(searchGridGroup)
|
// 若细分网格记录超过1个,说明业务逻辑存在问题,相同边长的细分网格应该只有1个
|
if (subGridGroupList.size > 1) {
|
throw BizException("该网格组下${searchGridGroup.length}米边长的网格记录超过1个,无法再进行网格细分,并且请检查生成逻辑是否问题")
|
}
|
// 若细分网格记录有且只有1个,则无需再次细分,直接返回已有结果
|
else if (subGridGroupList.size == 1) {
|
val g = subGridGroupList.first()
|
?: throw BizException("该网格组下的细分网格记录已损坏,无法使用,请检查数据库记录")
|
return satelliteGridRep.fetchGridCell(g.id)
|
}
|
// 当没有记录时,执行生成逻辑
|
|
// 生成新的细分网格组记录
|
val newGridGroup = GridGroup()
|
BeanUtils.copyProperties(gridGroup, newGridGroup)
|
newGridGroup.apply {
|
id = null
|
name += "${searchGridGroup.length.toInt()}米细分"
|
createTime = Date()
|
length = searchGridGroup.length
|
type = GridType.Sub.name.lowercase()
|
fatherGroupId = groupId
|
}
|
satelliteGridRep.insertGridGroup(newGridGroup)
|
|
// 获取具体网格信息
|
val cellList = satelliteGridRep.fetchGridCell(groupId)
|
// 按照给定的拆分系数进行拆分
|
val subCellList = SatelliteGridManage.splitGrid(cellList, scale, newGridGroup.id)
|
satelliteGridRep.insertGridCell(subCellList)
|
|
return subCellList
|
}
|
|
@Transactional
|
override fun splitData(groupId: Int, dataId: Int): List<GridDataDetail?> {
|
// 检查是否是细分网格类型
|
val gridGroup =
|
satelliteGridRep.fetchGridGroup(groupId) ?: throw BizException("该网格组不存在,无法进行细分网格数据映射")
|
if (gridGroup.type != GridType.Sub.name.lowercase()) throw BizException("该网格组不是细分网格类型存在,无法进行细分网格数据映射")
|
|
val subGridCellList = satelliteGridRep.fetchGridCell(groupId)
|
val originGridData = satelliteGridRep.fetchGridData(dataId)
|
val originGridDataDetailList = satelliteGridRep.fetchGridDataDetail(dataId)
|
|
val subGridData = GridData().apply {
|
this.groupId = groupId
|
dataTime = originGridData?.dataTime
|
type = SatelliteDataType.Sub.value.toByte()
|
}
|
satelliteGridRep.insertGridData(subGridData)
|
|
val subGridDataDetailList =
|
SatelliteGridManage.splitData(subGridCellList, subGridData, originGridDataDetailList)
|
|
satelliteGridRep.insertGridDataDetail(subGridDataDetailList)
|
|
return subGridDataDetailList
|
}
|
|
@Transactional
|
override fun dataFusion(missionCode: String, groupId: Int): List<GridDataDetail?> {
|
// 查询走航任务及对应走航监测数据
|
val mission = missionRep.findOne(missionCode) ?: throw BizException("任务不存在")
|
val data = realTimeDataRep.fetchData(mission)
|
|
// 查找是否已有走航融合记录
|
val oldGridDataList = satelliteGridRep.fetchGridData(GridData().apply {
|
this.groupId = groupId
|
mixDataId = missionCode
|
this.type = SatelliteDataType.Monitor.value.toByte()
|
})
|
|
if (oldGridDataList.isEmpty()) {
|
// 创建融合数据索引对象
|
val newGridData = GridData().apply {
|
this.groupId = groupId
|
dataTime = mission.startTime
|
type = SatelliteDataType.Monitor.value.toByte()
|
this.missionCode = mission.missionCode
|
// Fixme 2025.3.27: 行政区划在走航任务添加对应字段后进行赋值
|
provinceCode
|
provinceName
|
cityCode
|
cityName
|
districtCode
|
districtName = mission.districtName
|
// Fixme 2025.3.27: 所属监测点区域目前需要通过用户选择确定
|
zone
|
pollutionDegreeIndex
|
pollutionDegree
|
|
val period = TimeUtil.getDayTimeTag(mission.startTime, mission.endTime)
|
dayTimePeriod = period?.first
|
dayTimePeriodStart = period?.second
|
dayTimePeriodEnd = period?.third
|
}
|
satelliteGridRep.insertGridData(newGridData)
|
|
// 查询网格单元格信息
|
val gridCellList = satelliteGridRep.fetchGridCell(groupId)
|
|
// 将走航数据和卫星网格进行融合计算
|
val gridDataDetailList = SatelliteGridManage.dataFusion(data, newGridData, gridCellList)
|
satelliteGridRep.insertGridDataDetail(gridDataDetailList)
|
|
return gridDataDetailList
|
} else {
|
val oldGridData = oldGridDataList.first()
|
val oldGridDataDetailList = satelliteGridRep.fetchGridDataDetail(oldGridData?.id, oldGridData?.groupId)
|
// 查询网格单元格信息
|
val gridCellList = satelliteGridRep.fetchGridCell(groupId)
|
// 将走航数据和卫星网格进行融合计算
|
val gridDataDetailList = SatelliteGridManage.dataFusion(data, oldGridData, gridCellList)
|
|
// 将已有的数据id赋值给新的融合结果,两组结果均以根据cellId顺序排列,所以直接循环赋值
|
gridDataDetailList.forEachIndexed { index, gridDataDetail ->
|
gridDataDetail.id = oldGridDataDetailList[index]?.id
|
}
|
|
satelliteGridRep.updateGridDataDetail(gridDataDetailList)
|
|
return gridDataDetailList
|
}
|
}
|
|
override fun mixGridData(groupId: Int, dataIdList: List<Int>): List<GridDataDetailMixVo> {
|
// 获取所使用的网格组网格信息
|
val gridCellList = satelliteGridRep.fetchGridCell(groupId)
|
|
val gridDataDetailMap = mutableMapOf<Int?, MutableList<GridDataDetail>>()
|
|
// 归集所有相同单元网格的数据
|
dataIdList.forEach { id ->
|
satelliteGridRep.fetchGridDataDetail(id, groupId).forEach gdd@{ gdd ->
|
if (gdd == null) return@gdd
|
if (!gridDataDetailMap.containsKey(gdd.cellId)) {
|
gridDataDetailMap[gdd.cellId] = mutableListOf()
|
}
|
gridDataDetailMap[gdd.cellId]?.add(gdd)
|
}
|
}
|
|
// 将所有结果格式化,同时重叠网格进行监测数据均值计算
|
val result = mutableListOf<GridDataDetailMixVo>()
|
gridDataDetailMap.forEach { (_, v) ->
|
val target = GridDataDetailMixVo()
|
if (v.size == 1) {
|
BeanUtils.copyProperties(v[0], target)
|
} else {
|
BeanUtils.copyProperties(v.avg(), target)
|
target.mixData = true
|
target.originDataList = v
|
}
|
target.apply {
|
this.dataId = v.first().dataId
|
this.groupId = v.first().groupId
|
this.cellId = v.first().cellId
|
}
|
result.add(target)
|
}
|
result.sortBy { it.pm25 }
|
result.forEachIndexed { index, d ->
|
d.rank = index + 1
|
}
|
result.sortBy { it.cellId }
|
|
return result
|
}
|
|
override fun buildHeatmap(
|
groupId: Int,
|
gridDataDetailList: List<GridDataDetail>,
|
searchLength: Int,
|
): List<GridDataDetail> {
|
val gridCellList = satelliteGridRep.fetchGridCell(groupId)
|
val originCellIdList = gridDataDetailList.map { it.cellId }
|
// Fixme 2025.3.24: 此处根据现有的网格信息设计方式,使用临时的参数,后续将网格通过二维坐标形式表示,此处参数去除
|
val option = GridGroupOption(120, 90, 10, 10)
|
|
val resMap = mutableMapOf<Int, MutableList<GridDataDetail>>()
|
|
// 循环计算每个网格的周边扩散网格结果
|
gridDataDetailList.forEach { gdd ->
|
SatelliteGridManage.heatMap(gdd, gridCellList, option, searchLength).forEach { r ->
|
if (!originCellIdList.contains(r.cellId)) {
|
if (!resMap.containsKey(r.cellId)) {
|
resMap[r.cellId] = mutableListOf()
|
}
|
resMap[r.cellId]?.add(r)
|
}
|
}
|
}
|
|
// 将所有结果格式化,同时重叠网格进行监测数据均值计算
|
val result = mutableListOf<GridDataDetail>()
|
resMap.forEach { (_, v) ->
|
result.add(v.avg().apply {
|
this.dataId = v.first().dataId
|
this.groupId = v.first().groupId
|
this.cellId = v.first().cellId
|
})
|
}
|
result.addAll(gridDataDetailList)
|
|
return result
|
}
|
}
|