feiyu02
2025-03-28 8cf331411ea79c0d83e00657ed1374b29b09f4d7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
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
    }
}