| | |
| | | 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() |
| | | |
| | |
| | | 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(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') { |
| | |
| | | // 定点监测 |
| | | 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) |
| | |
| | | } |
| | | } |
| | | // 计算各中心点污染风险权重结果并赋予对应影响等级 |
| | | 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++ |
| | | } |
| | | } |
| | | val r = epwModel.outputResult() |
| | | |
| | | val max = mutableMapOf<String, Double>()//记录每种监测因子的最大值 |
| | |
| | | } |
| | | 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) |
| | | } |
| | | } |