feiyu02
2024-08-29 e6cc379fbef57277568ee667ec07a508b3dcc479
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/EPWModelServiceImpl.kt
@@ -3,20 +3,22 @@
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.BaseResponse
import com.flightfeather.uav.lightshare.bean.GridVo
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.model.epw.EPWModel
import com.flightfeather.uav.socket.eunm.UWDeviceType
import org.springframework.stereotype.Service
import kotlin.math.PI
import kotlin.math.*
@Service
class EPWModelServiceImpl(
    private val realTimeDataService: RealTimeDataService,
) : EPWModelService {
    companion object {
        private const val LEN = 3// 根据风向网格向外拓展圈数
    }
    val epwModel = EPWGridModel()
@@ -34,6 +36,60 @@
        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') {
@@ -42,26 +98,51 @@
        // 定点监测
        else if (gridType == '1') {
            // FIXME: 2021/12/6 此处为了测试暂时将站点经纬度写死,后续通过数据库配置获取
            val center = Pair(121.235813, 30.835898)
            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 // 网格范围,边长为20千米的正方形
            val totalLen = 2000 // FIXME: 2021/12/16 网格范围,边长为20千米的正方形
            val gridNum = ((totalLen / 2 / len).toInt() - 1) * 2 + 1
            // c.确定左上角网格左下和右上的两个对角点坐标
            var len1 = gridNum//水平方向网格数
            var width = gridNum//垂直方向网格数
            //中心点坐标
            val g1CenterLng = center.first - (gridNum - 1) / 2 * dx//经度减小
            val g1CenterLat = center.second + (gridNum - 1) / 2 * dy//纬度增加
            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 gridNum) {
                for (y in 0 until gridNum) {
            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)
@@ -72,27 +153,7 @@
            }
        }
        // 计算各中心点污染风险权重结果并赋予对应影响等级
        var page = 1
        var totalPage = -1
        while (totalPage == -1 || page <= totalPage) {
            realTimeDataService.getSecondData(deviceCode, startTime, endTime, 0, page, 5000).apply {
                if (totalPage == -1) {
                    totalPage = head?.totalPage ?: 0
                }
                val dataList = data ?: emptyList()
                // FIXME: 2021/7/13 此处为了测试暂时将站点经纬度写死,后续通过数据库配置获取
                dataList.forEach {
                    if (it.lng == 0.0 && it.lat == 0.0) {
                        it.lng = GridLng
                        it.lat = GridLat
                    }
                }
                epwModel.execute(dataList, points, true)
                page++
            }
        }
        epwModel.execute(dataList, points, true)
        val r = epwModel.outputResult()
        val max = mutableMapOf<String, Double>()//记录每种监测因子的最大值
@@ -138,4 +199,62 @@
        }
        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)
    }
}