| | |
| | | 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( |
| | | 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 |
| | | } |
| | | } |