| | |
| | | package com.flightfeather.uav.biz.satellite |
| | | |
| | | import com.flightfeather.uav.common.utils.MapUtil |
| | | import com.flightfeather.uav.domain.entity.BaseRealTimeData |
| | | import com.flightfeather.uav.domain.entity.GridCell |
| | | import com.flightfeather.uav.domain.entity.GridData |
| | | import com.flightfeather.uav.domain.entity.GridDataDetail |
| | | import com.flightfeather.uav.domain.entity.* |
| | | import com.flightfeather.uav.model.underwaygrid.GridCellAndData |
| | | import com.flightfeather.uav.model.underwaygrid.GridCellSop |
| | | import com.flightfeather.uav.model.underwaygrid.UnderwayGridModel |
| | | import com.flightfeather.uav.socket.eunm.FactorType |
| | | import org.springframework.beans.BeanUtils |
| | | import kotlin.math.PI |
| | | import kotlin.math.sqrt |
| | | |
| | |
| | | /** |
| | | * 根据正方形网格中心点坐标,计算4个顶点坐标 |
| | | * 网格中心点坐标按照从左到右、从上到下的顺序排列 |
| | | * @date 2025.1.8 |
| | | * @param points 网格中心坐标点数组 |
| | | * @return 网格4个顶点经纬度坐标 |
| | | */ |
| | | fun calGridVertex(points: List<Pair<Double, Double>>): List<GridVertex> { |
| | | // 网格少于2个,则无法绘制 |
| | |
| | | /** |
| | | * 拆分网格为细分网格,所有网格应该是相同边长的正方形 |
| | | * 根据相似矩形的原理,可以分别按比例得到每个细分网格的经纬度 |
| | | * @date 2025.1.17 |
| | | * @param gridCellList 原始网格数组 |
| | | * @param scale 拆分的系数,例如 2,表示将原有网格按边长的 1/2 拆分成 2 * 2 的4个网格 |
| | | * @param groupId 细分后的网格所属的网格组id |
| | | * @return |
| | | * @return 细分网格 |
| | | */ |
| | | fun splitGrid(gridCellList: List<GridCell?>, scale: Int, groupId:Int): List<GridCell?> { |
| | | if (scale <= 0) throw IllegalArgumentException("网格拆分的数量不能小于1") |
| | |
| | | |
| | | /** |
| | | * 拆分数据,将原始卫星网格遥测数据映射到对应细分网格上 |
| | | * @param subGridCellList 细分网格, 按照 |
| | | * @date 2025.2.7 |
| | | * @param subGridCellList 细分网格 |
| | | * @param subGridData 细分网格对应的数据索引 |
| | | * @param originGridDataDetailList 细分网格所属网格的原始网格数据 |
| | | * @return 映射后的细分网格遥测数据 |
| | | */ |
| | | fun splitData( |
| | | subGridCellList: List<GridCell?>, subGridData: GridData, originGridDataDetailList: List<GridDataDetail?> |
| | |
| | | } |
| | | |
| | | /** |
| | | * 数据融合 |
| | | * 走航数据和卫星网格融合 |
| | | * 数据融合采用均值方式统计(暂时) |
| | | * @date 2025.2.7 |
| | | * @param realTimeDataList 待融合的走航监测数据 |
| | | * @param gridData 融合后的数据组索引 |
| | | * @param gridCellList 待融合的卫星网格 |
| | | * @return 融合后的网格监测数据 |
| | | */ |
| | | fun dataFusion(realTimeDataList:List<BaseRealTimeData>, gridData: GridData, gridCellList: List<GridCell?>) { |
| | | // 遍历走航监测数据,计算每个点所在网格,并形成网格值 |
| | | fun dataFusion( |
| | | realTimeDataList: List<BaseRealTimeData>, |
| | | gridData: GridData?, |
| | | gridCellList: List<GridCell?>, |
| | | ): List<GridDataDetail> { |
| | | // 遍历走航监测数据,计算每个点所在网格 |
| | | val dataMap = mutableMapOf<GridCell, MutableList<BaseRealTimeData>>() |
| | | realTimeDataList.forEach { |
| | | it.longitude |
| | | it.latitude |
| | | gridCellList.forEach { |
| | | if (it.longitude == null || it.latitude == null) return@forEach |
| | | |
| | | } |
| | | SatelliteGridUtil.searchGirdIn(it.longitude!!.toDouble() to it.latitude!!.toDouble(), gridCellList) |
| | | ?.let { cell -> |
| | | if (!dataMap.containsKey(cell)) { |
| | | dataMap[cell] = mutableListOf() |
| | | } |
| | | dataMap[cell]?.add(it) |
| | | } |
| | | } |
| | | |
| | | // 统计每个网格中的均值 |
| | | // Fixme 2025.2.20 暂时默认以均值方式统计,后续调整为多种方式并支持用户选择 |
| | | val gridDataDetailList = mutableListOf<GridDataDetail>() |
| | | dataMap.forEach { (k, v) -> |
| | | val avgData = v.avg() |
| | | val dataDetail = GridDataDetail() |
| | | BeanUtils.copyProperties(avgData, dataDetail) |
| | | dataDetail.apply { |
| | | dataId = gridData?.id |
| | | groupId = k.groupId |
| | | cellId = k.cellIndex |
| | | rank |
| | | } |
| | | gridDataDetailList.add(dataDetail) |
| | | } |
| | | |
| | | gridDataDetailList.sortBy { it.pm25 } |
| | | gridDataDetailList.forEachIndexed { index, d -> |
| | | d.rank = index + 1 |
| | | } |
| | | gridDataDetailList.sortBy { it.cellId } |
| | | |
| | | return gridDataDetailList |
| | | } |
| | | |
| | | /** |
| | | * 计算热力图网格,即网格周边扩散影响权重计算 |
| | | * @param gridDataDetail 网格监测数据 |
| | | * @param gridCellList 区域网格数组 |
| | | * @param option 区域网格参数信息 |
| | | * @param searchLength 计算周边八方向(上下左右及四个对角)网格的长度 |
| | | * @return 周边网格及对应的监测数据结果 |
| | | */ |
| | | fun heatMap( |
| | | gridDataDetail: GridDataDetail, gridCellList: List<GridCell?>, |
| | | option: GridGroupOption, searchLength: Int, |
| | | ): List<GridDataDetail> { |
| | | // 找到网格数据对应的网格信息 |
| | | val gridCell = gridCellList.find { it?.cellIndex == gridDataDetail.cellId } |
| | | ?: throw IllegalArgumentException("网格数据和给定的区域网格不匹配") |
| | | |
| | | // 获取周边网格 |
| | | val surroundGridCellList = |
| | | SatelliteGridUtil.searchDiffuseGrid(gridDataDetail.cellId, gridCellList, option, searchLength) |
| | | |
| | | // 使用走航网格权重模型,计算周边网格的监测数据值 |
| | | val underwayGridModel = UnderwayGridModel() |
| | | val dataList = listOf(GridCellAndData(gridCell, gridDataDetail)) |
| | | val gridCellSopList = surroundGridCellList.map { |
| | | GridCellSop( |
| | | it, |
| | | it.id.toString(), |
| | | it.cellIndex.toString(), |
| | | it.cellIndex.toString() |
| | | ) } |
| | | underwayGridModel.execute(dataList, gridCellSopList) |
| | | val resMap = underwayGridModel.outputResult() |
| | | |
| | | // 格式化结果并返回 |
| | | val result = mutableListOf<GridDataDetail>() |
| | | gridCellSopList.forEach { |
| | | val resGridDataDetail = GridDataDetail().apply { |
| | | dataId = gridDataDetail.dataId |
| | | groupId = gridDataDetail.groupId |
| | | cellId = it.gridCell.cellIndex |
| | | } |
| | | |
| | | val key = "${it.sourceName};${it.index}" |
| | | val d = resMap[key] |
| | | d?.forEach { (t, u) -> |
| | | val avg = u["综合(${t})"]?.average ?: .0 |
| | | resGridDataDetail.setFactorValue(FactorType.getByName(t), avg.toFloat()) |
| | | } |
| | | result.add(resGridDataDetail) |
| | | } |
| | | |
| | | return result |
| | | } |
| | | |
| | | } |