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 address: 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(),
|
pair,
|
windLevelCondition.distanceType.disRange,
|
config.sourceTraceDegOffset
|
).map {
|
// 将坐标转换为gcj02(火星坐标系),因为污染源场景信息都为此坐标系
|
MapUtil.wgs84ToGcj02(it)
|
}
|
|
closePolygon = closeSourceTrace(historyData, pair)
|
|
try {
|
val address = AMapService.reGeo(pair)
|
this.address = address.district + address.township + address.street
|
} 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
|
}
|
}
|