Riku
2025-10-21 0366feb2fe536a27435685e152c34c75af68b712
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
package com.flightfeather.uav.biz.sourcetrace.model
 
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
 
/**
 * 动态溯源污染区域
 * 通过地图坐标点形成多边形来描述一块污染区域
 * @date 2025/5/27
 * @author feiyu02
 */
class PollutedArea() {
 
    /**
     * 溯源角度可设置
     */
 
    constructor(
        historyData: List<BaseRealTimeData>,
        exceptionData: List<BaseRealTimeData>,
        config: RTExcWindLevelConfig,
        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition?,
    ) : this() {
        distanceType = windLevelCondition?.distanceType
        distanceRange = distanceType?.disRange
        distanceDes = distanceType?.des
        windLevelCondition?.let { sourceTrace(historyData, exceptionData, config, it) }
    }
 
    // 所属街镇
    var township:String? = null
    // 格式化地址
    var address: String? = null
    // 所在道路名称
    var street:String? = null
    // 所在道路上的最近门牌号
    var streetNumber:String? = null
    // 处于该门牌号的方向
    var direction:String? = null
    // 处于该门牌号的方向距离
    var distance: String? = null
    // 所处最近交叉路口的方向和距离
    var roadinter:String? = null
    // 所处最近交叉路口的距离
    var distance2: String? = null
 
    // 污染范围扇形区域(经纬度多边形)
    var polygon: List<Pair<Double, Double>>? = null
 
    // 近距离污染圆形区域
    var closePolygon: List<Pair<Double, Double>>? = null
 
    // 污染可能的发生距离
    var distanceType: DistanceType? = null
 
    var distanceRange: Pair<Double, Double>? = null
    var distanceDes: String? = null
 
    /**
     * 反向溯源
     */
    private fun sourceTrace(
        historyData: List<BaseRealTimeData>,
        exceptionData: List<BaseRealTimeData>,
        config: RTExcWindLevelConfig,
        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
    ) {
        val avgData = if (exceptionData.size == 1) {
            exceptionData.first()
        } else {
            exceptionData.avg()
        }
 
        val pair = avgData.longitude!!.toDouble() to avgData.latitude!!.toDouble()
 
        polygon = calSector(
            avgData.windDirection?.toDouble() ?: .0,
            pair,
            windLevelCondition.distanceType.disRange,
            config.sourceTraceDegOffset
        ).map {
            // 将坐标转换为gcj02(火星坐标系),因为污染源场景信息都为此坐标系
            MapUtil.wgs84ToGcj02(it)
        }
 
        closePolygon = closeSourceTrace(historyData, pair).map {
            // 将坐标转换为gcj02(火星坐标系),因为污染源场景信息都为此坐标系
            MapUtil.wgs84ToGcj02(it)
        }
 
        if (config.isSearchAddress) {
            try {
                val address = AMapService.reGeo(MapUtil.wgs84ToGcj02(pair))
                this.township = address.province + address.district + address.township
                this.address = address.address
                this.street = address.street
                this.streetNumber = address.streetNumber
                this.direction = address.direction
                this.distance = address.distance
                this.roadinter = address.roadinter
                this.distance2 = address.distance2
                Thread.sleep(100)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
 
    }
 
    /**
     * 根据中心点坐标、风向和溯源距离,以及给定的夹角,计算以中心点按照风向向外扩散形成的扇形的点坐标
     * @param windDir 风向,单位:度
     * @param center 中心点坐标经纬度
     * @param distanceRange 溯源距离范围,单位:米
     * @param defaultDegOffset 扩散偏移角度
     * @return 多边形顶点坐标集合
     */
    private fun calSector(
        windDir: Double,
        center: Pair<Double, Double>,
        distanceRange: Pair<Double, Double>,
        defaultDegOffset: Double = 60.0,
    ): List<Pair<Double, Double>> {
 
        val sDeg = windDir - defaultDegOffset / 2
        val eDeg = windDir + defaultDegOffset / 2
 
        val result = mutableListOf<Pair<Double, Double>>()
 
        if (distanceRange.first == .0) {
//            result.add(center)
            var startDeg = 0
            while (startDeg <= 360) {
                val p = MapUtil.getPointByLen(center, distanceRange.second, startDeg * PI / 180)
                result.add(p)
                startDeg++
            }
        } else {
            // 从开始角度循环计算坐标点至结束角度,步长1°
            var startDeg = sDeg
            while (startDeg <= eDeg) {
                val p = MapUtil.getPointByLen(center, distanceRange.first, startDeg * PI / 180)
                result.add(p)
                startDeg++
            }
            if (distanceRange.second > .0) {
                // 此处需要从结束角度开始反向循环计算至开始角度,步长1°,使得两组坐标点按顺序排列,可绘制对应的多边形
                startDeg = eDeg
                while (startDeg >= sDeg) {
                    val p = MapUtil.getPointByLen(center, distanceRange.second, startDeg * PI / 180)
                    result.add(p)
                    startDeg--
                }
            }
        }
 
        return result
    }
 
    private fun closeSourceTrace(
        historyData: List<BaseRealTimeData>,
        center: Pair<Double, Double>,
    ): List<Pair<Double, Double>> {
        val result = mutableListOf<Pair<Double, Double>>()
        var startDeg = 0
        while (startDeg <= 360) {
            val p = MapUtil.getPointByLen(center, DistanceType.TYPE1.disRange.second, startDeg * PI / 180)
            result.add(p)
            startDeg++
        }
 
        return result
    }
}