feiyu02
2025-08-28 ddaa44400aa478058ffe9349d59904a130b7ce9c
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
package com.flightfeather.uav.lightshare.service.impl
 
import com.flightfeather.uav.common.GridLat
import com.flightfeather.uav.common.GridLng
import com.flightfeather.uav.common.utils.MapUtil
import com.flightfeather.uav.lightshare.bean.*
import com.flightfeather.uav.lightshare.service.EPWModelService
import com.flightfeather.uav.lightshare.service.RealTimeDataService
import com.flightfeather.uav.model.epw.EPWGridModel
import com.flightfeather.uav.socket.eunm.UWDeviceType
import org.springframework.stereotype.Service
import kotlin.math.*
 
@Service
class EPWModelServiceImpl(
    private val realTimeDataService: RealTimeDataService,
) : EPWModelService {
 
    companion object {
        private const val LEN = 3// 根据风向网格向外拓展圈数
    }
 
    val epwModel = EPWGridModel()
 
    override fun getEpwModelResult(deviceCode: String, startTime: String, endTime: String, len: Double): BaseResponse<List<GridVo>> {
        if (deviceCode.length < 2) return BaseResponse(false, "设备编号格式错误")
        val type = deviceCode.substring(0, 2)
        // 确定数据源类型,区分为‘定点监测数据’和‘移动监测数据两种’
        val gridType = when (type) {
            UWDeviceType.UAV.value -> '0'
            UWDeviceType.VEHICLE.value -> '0'
            UWDeviceType.GRID.value -> '1'
            UWDeviceType.BOAT.value -> 'f'
            else -> 'f'
        }
        if (gridType == 'f') return BaseResponse(false)
 
        val points = mutableListOf<GridVo>()
 
        // 获取全部数据计算平均风向
        val dataList = mutableListOf<DataVo>()
        var page = 1
        var totalPage = -1
        //风向采用单位矢量法求取均值
        var u = .0//东西方位分量总和
        var v = .0//南北方位分量总和
        var c = 0//风向数据计数
        var windDirection = .0 // 平均风向角度
        while (totalPage == -1 || page <= totalPage) {
            realTimeDataService.getSecondData(null, deviceCode, startTime, endTime, 0, page, 5000).apply {
                if (totalPage == -1) {
                    totalPage = head?.totalPage ?: 0
                }
                val list = data ?: emptyList()
 
                // FIXME: 2021/7/13 此处为了测试暂时将站点经纬度写死,后续通过数据库配置获取
                list.forEach {
                    if (it.lng == 0.0 && it.lat == 0.0) {
                        it.lng = GridLng
                        it.lat = GridLat
                    }
 
                    val r = Math.toRadians(it.values?.get(16)?.factorData ?: .0)
                    u += sin(r)
                    v += cos(r)
                    c++
                }
 
                dataList.addAll(list)
                page++
            }
        }
 
        if (c != 0) {
            val avgU = u / c
            val avgV = v / c
            var a = atan(avgU / avgV)
            a = Math.toDegrees(a)
            /**
             * avgU>0;avgV>0: 真实角度处于第一象限,修正值为+0°
             * avgU>0;avgV<0: 真实角度处于第二象限,修正值为+180°
             * avgU<0;avgV<0: 真实角度处于第三象限,修正值为+180°
             * avgU<0;avgV>0: 真实角度处于第四象限,修正值为+360°
             */
            a += if (avgV > 0) {
                if (avgU > 0) 0 else 360
            } else {
                180
            }
            windDirection = a
        }
 
        // 根据不同类型,确定不同的网格生成方式,得出网格中心点集合(网格默认采用正方形)
        // 走航监测
        if (gridType == '0') {
            // TODO: 2021/12/6 走航监测网格点生成
        }
        // 定点监测
        else if (gridType == '1') {
            // FIXME: 2021/12/6 此处为了测试暂时将站点经纬度写死,后续通过数据库配置获取
            val center = Pair(GridLng, GridLat)
            // a.确定网格长度对应的坐标差值
            // FIXME: 2021/12/16 此处网格坐标的计算比较简单,近似将其当作平面坐标来做,后续可改进
            val p1 = MapUtil.getPointByLen(center, len, PI / 2)//正东方向(90°)的坐标点
            val p2 = MapUtil.getPointByLen(center, len, PI)//正南方向(180°)的坐标点
            val dx = p1.first - center.first
            val dy = center.second - p2.second
            // b.确定单边有多少个网格(规定监测点在中心网格的中点上,因此单边网格数一定为奇数)
            val totalLen = 2000 // FIXME: 2021/12/16 网格范围,边长为20千米的正方形
            val gridNum = ((totalLen / 2 / len).toInt() - 1) * 2 + 1
            // c.确定左上角网格左下和右上的两个对角点坐标
            var len1 = gridNum//水平方向网格数
            var width = gridNum//垂直方向网格数
            //中心点坐标
            var g1CenterLng = center.first - (gridNum - 1) / 2 * dx//经度减小
            var g1CenterLat = center.second + (gridNum - 1) / 2 * dy//纬度增加
            when (windDirection) {
                in .0..90.0 -> {
                    g1CenterLat += LEN * dy
                    width += LEN
                    len1 += LEN
                }
                in 90.0..180.0 -> {
                    len1 += LEN
                    width += LEN
                }
                in 180.0..270.0 -> {
                    g1CenterLng -= LEN * dx
                    len1 += LEN
                    width += LEN
                }
                in 270.0..360.0 -> {
                    g1CenterLng -= LEN * dx
                    g1CenterLat += LEN * dy
                    len1 += LEN
                    width += LEN
                }
            }
            //左下坐标
            val g1LB = Pair(g1CenterLng - dx / 2, g1CenterLat - dy / 2)
            //右上坐标
            val g1RT = Pair(g1CenterLng + dx / 2, g1CenterLat + dy / 2)
            // d.得出所有网格的两个对角点坐标
            for (x in 0 until len1) {
                for (y in 0 until width) {
                    points.add(GridVo("$x-$y").apply {
                        this.lb = Pair(g1LB.first + dx * x, g1LB.second - dy * y)
                        this.rt = Pair(g1RT.first + dx * x, g1RT.second - dy * y)
                        this.ciLongitude = (lb!!.first + dx / 2).toBigDecimal()
                        this.ciLatitude = (lb!!.second + dy / 2).toBigDecimal()
                    })
                }
            }
        }
        // 计算各中心点污染风险权重结果并赋予对应影响等级
        epwModel.execute(dataList, points, true)
        val r = epwModel.outputResult()
 
        val max = mutableMapOf<String, Double>()//记录每种监测因子的最大值
//        val min = mutableMapOf<String, Double>()//记录每种监测因子的最小值
        //为每个网格赋值权重结果并且筛选各监测因子的最大最小值
        points.forEach {
            val key = "${it.sourceName};${it.index}"
            val d = r[key]
            d?.forEach { (t, u) ->
                it.result[t] = u["综合(${t})"]?.average ?: .0
 
                //筛选最大值
                if (!max.containsKey(t)) {
                    max[t] = it.result[t]!!
                } else {
                    if (max[t]!! < it.result[t]!!) {
                        max[t] = it.result[t]!!
                    }
                }
//                //筛选最小值
//                if (!min.containsKey(t)) {
//                    min[t] = it.result[t]!!
//                } else {
//                    if (min[t]!! > it.result[t]!!) {
//                        min[t] = it.result[t]!!
//                    }
//                }
            }
        }
        // 根据最大最小值,计算每个网格的各监测因子的影响等级(0->2)(影响大->小)
        points.forEach {
            it.result.forEach{ (k, v) ->
                max[k]?.let {m ->
                    val level = when (v / m) {
                        in 0.6666..1.0 -> 0
                        in 0.3333..0.6665 -> 1
                        in .0..0.3332 -> 2
                        else -> 2
                    }
                    it.level[k] = level
                }
            }
        }
        return BaseResponse(true, data = points)
    }
 
    /**
     * 以监测点为网格线上的中心,向外按照正方形进行扩散。
     * 首先默认展示监测点周围一圈16个网格,然后再以外圈12个网格为基础向外扩散,每次最多扩散一圈,判断权重结果,保留所有的高风险和中风险网格,并保留两圈低风险网格
     * 其中的高、中、低风险判断依据为网格权重值对应当前最大值的比例(分界为66.66%和33.33%)
     */
    override fun getEpwModelResultDynamic(deviceCode: String, startTime: String, endTime: String, len: Double): BaseResponse<List<GridVo>> {
//        if (deviceCode.length < 2) return BaseResponse(false, "设备编号格式错误")
//        // 确定数据源类型,区分为‘定点监测数据’和‘移动监测数据两种’
//        val gridType = when (deviceCode.substring(0, 2)) {
//            UWDeviceType.UAV.value -> '0'
//            UWDeviceType.VEHICLE.value -> '0'
//            UWDeviceType.GRID.value -> '1'
//            UWDeviceType.BOAT.value -> 'f'
//            else -> 'f'
//        }
//        if (gridType == 'f' || gridType == '0') return BaseResponse(false, "该设备类型不支持动态网格风险计算,只有定点网格化设备可行")
//        val points = mutableListOf<GridVo>()
//        // FIXME: 2021/12/6 此处为了测试暂时将站点经纬度写死,后续通过数据库配置获取
//        val center = Pair(GridLng, GridLat)
//
//        // a.确定网格长度对应的坐标差值
//        // FIXME: 2021/12/16 此处网格坐标的计算比较简单,近似将其当作平面坐标来做,后续可改进
//        val p1 = MapUtil.getPointByLen(center, len, PI / 2)//正东方向(90°)的坐标点
//        val p2 = MapUtil.getPointByLen(center, len, PI)//正南方向(180°)的坐标点
//        val dx = p1.first - center.first
//        val dy = center.second - p2.second
//
//        val grids = mutableListOf<QuadrantInfo>()// 所有网格
//        val outerMost = mutableMapOf<Quadrant, QuadrantInfo>()// 当前最外圈的网格
//        // b.先计算内部两圈的结果
//        grids.addAll(listOf(
//            // 第一圈
//            QuadrantInfo(Quadrant.First, 0),
//            QuadrantInfo(Quadrant.Second, 0),
//            QuadrantInfo(Quadrant.Third, 0),
//            QuadrantInfo(Quadrant.Fourth, 0),
//            // 第二圈
//            QuadrantInfo(Quadrant.First, 1),
//            QuadrantInfo(Quadrant.Second, 1),
//            QuadrantInfo(Quadrant.Third, 1),
//            QuadrantInfo(Quadrant.Fourth, 1),
//        ))
//        //当前最外圈(第二圈)
//        outerMost[Quadrant.First] = grids[4]
//        outerMost[Quadrant.Second] = grids[5]
//        outerMost[Quadrant.Third] = grids[6]
//        outerMost[Quadrant.Fourth] = grids[7]
//        //中心点坐标
//        val g1CenterLng = center.first - (gridNum - 1) / 2 * dx//经度减小
//        val g1CenterLat = center.second + (gridNum - 1) / 2 * dy//纬度增加
//        //左下坐标
//        val g1LB = Pair(g1CenterLng - dx / 2, g1CenterLat - dy / 2)
//        //右上坐标
//        val g1RT = Pair(g1CenterLng + dx / 2, g1CenterLat + dy / 2)
 
        return BaseResponse(true)
    }
}