package com.flightfeather.uav.lightshare.service.impl
|
|
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.*
|
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.socket.eunm.UWDeviceType
|
import org.springframework.stereotype.Service
|
import kotlin.math.*
|
|
@Service
|
class EPWModelServiceImpl(
|
private val realTimeDataService: RealTimeDataService,
|
) : EPWModelService {
|
|
companion object {
|
private const val LEN = 3// 根据风向网格向外拓展圈数
|
}
|
|
val epwModel = EPWGridModel()
|
|
override fun getEpwModelResult(deviceCode: String, startTime: String, endTime: String, len: Double): BaseResponse<List<GridVo>> {
|
if (deviceCode.length < 2) return BaseResponse(false, "设备编号格式错误")
|
val type = deviceCode.substring(0, 2)
|
// 确定数据源类型,区分为‘定点监测数据’和‘移动监测数据两种’
|
val gridType = when (type) {
|
UWDeviceType.UAV.value -> '0'
|
UWDeviceType.VEHICLE.value -> '0'
|
UWDeviceType.GRID.value -> '1'
|
UWDeviceType.BOAT.value -> 'f'
|
else -> 'f'
|
}
|
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') {
|
// TODO: 2021/12/6 走航监测网格点生成
|
}
|
// 定点监测
|
else if (gridType == '1') {
|
// 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
|
// b.确定单边有多少个网格(规定监测点在中心网格的中点上,因此单边网格数一定为奇数)
|
val totalLen = 2000 // FIXME: 2021/12/16 网格范围,边长为20千米的正方形
|
val gridNum = ((totalLen / 2 / len).toInt() - 1) * 2 + 1
|
// c.确定左上角网格左下和右上的两个对角点坐标
|
var len1 = gridNum//水平方向网格数
|
var width = gridNum//垂直方向网格数
|
//中心点坐标
|
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 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)
|
this.ciLongitude = (lb!!.first + dx / 2).toBigDecimal()
|
this.ciLatitude = (lb!!.second + dy / 2).toBigDecimal()
|
})
|
}
|
}
|
}
|
// 计算各中心点污染风险权重结果并赋予对应影响等级
|
epwModel.execute(dataList, points, true)
|
val r = epwModel.outputResult()
|
|
val max = mutableMapOf<String, Double>()//记录每种监测因子的最大值
|
// val min = mutableMapOf<String, Double>()//记录每种监测因子的最小值
|
//为每个网格赋值权重结果并且筛选各监测因子的最大最小值
|
points.forEach {
|
val key = "${it.sourceName};${it.index}"
|
val d = r[key]
|
d?.forEach { (t, u) ->
|
it.result[t] = u["综合(${t})"]?.average ?: .0
|
|
//筛选最大值
|
if (!max.containsKey(t)) {
|
max[t] = it.result[t]!!
|
} else {
|
if (max[t]!! < it.result[t]!!) {
|
max[t] = it.result[t]!!
|
}
|
}
|
// //筛选最小值
|
// if (!min.containsKey(t)) {
|
// min[t] = it.result[t]!!
|
// } else {
|
// if (min[t]!! > it.result[t]!!) {
|
// min[t] = it.result[t]!!
|
// }
|
// }
|
}
|
}
|
// 根据最大最小值,计算每个网格的各监测因子的影响等级(0->2)(影响大->小)
|
points.forEach {
|
it.result.forEach{ (k, v) ->
|
max[k]?.let {m ->
|
val level = when (v / m) {
|
in 0.6666..1.0 -> 0
|
in 0.3333..0.6665 -> 1
|
in .0..0.3332 -> 2
|
else -> 2
|
}
|
it.level[k] = level
|
}
|
}
|
}
|
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)
|
}
|
}
|