feiyu02
2025-05-29 4d065a305b997bfb66f41b33a31d59de63b1958d
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt
@@ -1,18 +1,115 @@
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 {
class PollutedArea() {
    var name: String? = null
    /**
     * 溯源角度可设置
     */
    constructor(
        exceptionData: List<BaseRealTimeData>,
        config: RTExcWindLevelConfig,
        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
    ) : this() {
        distanceType = windLevelCondition.mutationRate.second
        sourceTrace(exceptionData, config, windLevelCondition)
    }
    var address: String? = null
    // 污染范围区域(经纬度多边形)
    var polygon: List<Pair<Double, Double>>? = null
    // 污染可能的发生距离
    var distanceType: DistanceType? = null
    /**
     * 反向溯源
     */
    private fun sourceTrace(
        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.windSpeed!!.toDouble(),
            pair,
            windLevelCondition.mutationRate.second.disRange,
            config.sourceTraceDegOffset
        )
        try {
            val address = AMapService.reGeo(pair)
            this.address = 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)
        } 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°,使得两组坐标点按顺序排列,可绘制对应的多边形
            var startDeg = eDeg
            while (startDeg >= sDeg) {
                val p = MapUtil.getPointByLen(center, distanceRange.second, startDeg * PI / 180)
                result.add(p)
                startDeg--
            }
        }
        return result
    }
}