From bde043c8fd1a076f44c402dd56c62d401afbfb16 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期四, 27 三月 2025 17:29:48 +0800
Subject: [PATCH] 1. 新增卫星遥测网格热力图计算逻辑

---
 src/main/kotlin/com/flightfeather/uav/biz/satellite/SatelliteGridManage.kt |  299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 282 insertions(+), 17 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/biz/satellite/SatelliteGridManage.kt b/src/main/kotlin/com/flightfeather/uav/biz/satellite/SatelliteGridManage.kt
index 524dbc4..c201a99 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/satellite/SatelliteGridManage.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/satellite/SatelliteGridManage.kt
@@ -1,7 +1,12 @@
 package com.flightfeather.uav.biz.satellite
 
 import com.flightfeather.uav.common.utils.MapUtil
-import com.flightfeather.uav.domain.entity.GridCell
+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
 
@@ -16,27 +21,34 @@
 
     /**
      * 鏍规嵁姝f柟褰㈢綉鏍间腑蹇冪偣鍧愭爣锛岃绠�4涓《鐐瑰潗鏍�
+     * 缃戞牸涓績鐐瑰潗鏍囨寜鐓т粠宸﹀埌鍙炽�佷粠涓婂埌涓嬬殑椤哄簭鎺掑垪
+     * @date 2025.1.8
      * @param points 缃戞牸涓績鍧愭爣鐐规暟缁�
+     * @return 缃戞牸4涓《鐐圭粡绾害鍧愭爣
      */
     fun calGridVertex(points: List<Pair<Double, Double>>): List<GridVertex> {
+        // 缃戞牸灏戜簬2涓紝鍒欐棤娉曠粯鍒�
         if (points.size < 2) return emptyList()
-        val p1 = points[0];
-        val p2 = points[1];
+        // 鑾峰彇鍓嶄袱涓綉鏍�
+        // Fixme 2025.01.10: 鐩墠鍏堢畝鍖栭�昏緫锛屾寜鐓у墠涓や釜鐐规槸宸﹀彸鎺掑竷锛屼笖鐐筽2鍦ㄧ偣p1鐨勪笢杈�
+        val p1 = points[0]
+        val p2 = points[1]
         // 涓や腑蹇冪偣闂寸殑瑙掑害
-        val angle = MapUtil.getAngle(p1.first, p1.second, p2.first, p2.second);
+        val angle = MapUtil.getAngle(p1.first, p1.second, p2.first, p2.second)
         // 涓や腑蹇冪偣闂寸殑璺濈
-        val dis = MapUtil.getDistance(p1.first, p1.second, p2.first, p2.second);
+        val dis = MapUtil.getDistance(p1.first, p1.second, p2.first, p2.second)
         // 缃戞牸姝f柟褰㈠瑙掔嚎鐨勪竴鍗婇暱搴�
-        val halfDiagonal = sqrt((dis / 2) * (dis / 2) * 2);
+        val halfDiagonal = sqrt((dis / 2) * (dis / 2) * 2)
         // 璁$畻棣栦釜姝f柟褰㈠悇椤剁偣鐩稿浜庝腑蹇冪偣鐨勮搴︼紝寰楀埌姝f柟褰㈠悇椤剁偣鐨勫潗鏍�
-        val angle1 = MapUtil.plusAngle(angle, 45.0);
-        val gp1 = MapUtil.getPointByLen(p1, halfDiagonal, angle1 * PI / 180);
-        val angle2 = MapUtil.plusAngle(angle1, 90.0);
-        val gp2 = MapUtil.getPointByLen(p1, halfDiagonal, angle2 * PI / 180);
-        val angle3 = MapUtil.plusAngle(angle2, 90.0);
-        val gp3 = MapUtil.getPointByLen(p1, halfDiagonal, angle3 * PI / 180);
-        val angle4 = MapUtil.plusAngle(angle3, 90.0);
-        val gp4 = MapUtil.getPointByLen(p1, halfDiagonal, angle4 * PI / 180);
+        // 4涓《鐐规寜鐓т粠宸︿笂瑙掑紑濮嬶紝椤烘椂閽堟柟鍚戠殑椤哄簭杩涜鎺掑垪
+        val angle1 = MapUtil.plusAngle(angle, 45.0 + 180.0)
+        val gp1 = MapUtil.getPointByLen(p1, halfDiagonal, angle1 * PI / 180)
+        val angle2 = MapUtil.plusAngle(angle1, 90.0)
+        val gp2 = MapUtil.getPointByLen(p1, halfDiagonal, angle2 * PI / 180)
+        val angle3 = MapUtil.plusAngle(angle2, 90.0)
+        val gp3 = MapUtil.getPointByLen(p1, halfDiagonal, angle3 * PI / 180)
+        val angle4 = MapUtil.plusAngle(angle3, 90.0)
+        val gp4 = MapUtil.getPointByLen(p1, halfDiagonal, angle4 * PI / 180)
         // 璁$畻4涓《鐐瑰垎鍒笌涓績鐐圭殑缁忕含搴﹀樊鍊�
         val dx1 = gp1.first - p1.first
         val dy1 = gp1.second - p1.second
@@ -59,10 +71,263 @@
     }
 
     /**
-     * 鎷嗗垎缃戞牸
+     * 鎷嗗垎缃戞牸涓虹粏鍒嗙綉鏍硷紝鎵�鏈夌綉鏍煎簲璇ユ槸鐩稿悓杈归暱鐨勬鏂瑰舰
+     * 鏍规嵁鐩镐技鐭╁舰鐨勫師鐞嗭紝鍙互鍒嗗埆鎸夋瘮渚嬪緱鍒版瘡涓粏鍒嗙綉鏍肩殑缁忕含搴�
+     * @date 2025.1.17
+     * @param gridCellList 鍘熷缃戞牸鏁扮粍
+     * @param scale 鎷嗗垎鐨勭郴鏁帮紝渚嬪 2锛岃〃绀哄皢鍘熸湁缃戞牸鎸夎竟闀跨殑 1/2 鎷嗗垎鎴� 2 * 2 鐨�4涓綉鏍�
+     * @param groupId 缁嗗垎鍚庣殑缃戞牸鎵�灞炵殑缃戞牸缁刬d
+     * @return 缁嗗垎缃戞牸
      */
-    fun splitGrid(gridCellList: List<GridCell?>): List<GridCell> {
-        TODO()
+    fun splitGrid(gridCellList: List<GridCell?>, scale: Int, groupId:Int): List<GridCell?> {
+        if (scale <= 0) throw IllegalArgumentException("缃戞牸鎷嗗垎鐨勬暟閲忎笉鑳藉皬浜�1")
+        // 鎷嗗垎绯绘暟涓�1锛屽垯琛ㄧず涓嶆媶鍒�
+        if (scale == 1) return gridCellList
+        if (gridCellList.isEmpty()) return emptyList()
+
+        val newGridCellList = mutableListOf<GridCell>()
+
+        // 鏍规嵁鍑芥暟[calGridVertex]鐢熸垚鐨勭綉鏍�4涓《鐐瑰潗鏍囷紝浠ヤ笂鍖椾笅鍗楀乏瑗垮彸涓滄柟鍚戜负鏍囧噯
+        // 璁$畻棣栦釜缃戞牸涓績鍧愭爣鐐瑰垎鍒拰4涓《鐐圭殑缁忕含搴﹀樊鍊�
+        val p = gridCellList.find { it != null }!!
+        // 锛堥�氳繃杩戜技骞抽潰鍧愭爣绯荤殑鏂瑰紡锛夋牴鎹崟涓師濮嬬綉鏍肩殑4涓《鐐瑰潗鏍囷紝鍒嗗埆纭畾浠ヤ笅涓夌粍鍧愭爣鐐逛箣闂寸殑缁忕含搴﹀崟浣嶅亸绉婚噺
+        val p1 = p.point1Lon to p.point1Lat
+        val p2 = p.point2Lon to p.point2Lat
+        val p3 = p.point3Lon to p.point3Lat
+        val p4 = p.point4Lon to p.point4Lat
+        // p1銆乸4鐨勭粡绾害鍗曚綅宸��
+        val dx1 = (p4.first - p1.first) / scale.toBigDecimal()
+        val dy1 = (p4.second - p1.second) / scale.toBigDecimal()
+        // p1銆乸2鐨勭粡绾害鍗曚綅宸��
+        val dx2 = (p2.first - p1.first) / scale.toBigDecimal()
+        val dy2 = (p2.second - p1.second) / scale.toBigDecimal()
+        // p4銆乸3鐨勭粡绾害鍗曚綅宸��
+        val dx3 = (p3.first - p4.first) / scale.toBigDecimal()
+        val dy3 = (p3.second - p4.second) / scale.toBigDecimal()
+        // 涓績鐐瑰拰p1鐨勭粡绾害鍗曚綅宸��
+        val dxC = (p.longitude - p1.first) / scale.toBigDecimal()
+        val dyC = (p.latitude - p1.second) / scale.toBigDecimal()
+
+        // 缃戞牸绱㈠紩
+        var cellIndex = 0
+
+        // 瀵圭綉鏍肩粍鍐呯殑鎵�鏈夌綉鏍艰繘琛岀綉鏍肩粏鍒�
+        gridCellList.forEach { g ->
+            if (g == null) return@forEach
+
+            // 缃戞牸琛屽惊鐜�
+            for (row in 0 until scale) {
+                val newGridCell1 = GridCell()
+
+                // 纭畾姣忎竴琛岄涓粏鍒嗙綉鏍肩殑涓績鍧愭爣鍜�4涓《鐐瑰潗鏍�
+                // 宸︿笂瑙掗《鐐规牴鎹墍鍦ㄨ鏁板湪鍘熷缃戞牸椤剁偣鍩虹涓婂鍔犲亸绉婚噺
+                newGridCell1.point1Lon = g.point1Lon + dx1 * row.toBigDecimal()
+                newGridCell1.point1Lat = g.point1Lat + dy1 * row.toBigDecimal()
+                // 宸︿笅瑙掗《鐐规牴鎹墍鍦ㄨ鏁板湪鍘熷缃戞牸椤剁偣鍩虹涓婂鍔犲亸绉婚噺锛堟瘮宸︿笂瑙掗《鐐瑰涓�涓亸绉伙級
+                newGridCell1.point4Lon = g.point1Lon + dx1 * (row + 1).toBigDecimal()
+                newGridCell1.point4Lat = g.point1Lat + dy1 * (row + 1).toBigDecimal()
+                // 鍙充笂瑙掗《鐐瑰湪缁嗗垎缃戞牸宸︿笂瑙掔殑鍩虹涓婂鍔犵浉搴旂殑鍋忕Щ閲�
+                newGridCell1.point2Lon = newGridCell1.point1Lon + dx2
+                newGridCell1.point2Lat = newGridCell1.point1Lat + dy2
+                // 鍙充笅瑙掗《鐐瑰湪缁嗗垎缃戞牸宸︿笅瑙掔殑鍩虹涓婂鍔犵浉搴旂殑鍋忕Щ閲�
+                newGridCell1.point3Lon = newGridCell1.point4Lon + dx3
+                newGridCell1.point3Lat = newGridCell1.point4Lat + dy3
+                // 涓績鐐瑰湪缁嗗垎缃戞牸宸︿笂瑙掔殑鍩虹涓婂鍔犲浐瀹氬亸绉婚噺
+                newGridCell1.longitude = newGridCell1.point1Lon + dxC
+                newGridCell1.latitude = newGridCell1.point1Lat + dyC
+
+                newGridCell1.groupId = groupId
+                newGridCell1.cellIndex = ++cellIndex
+                newGridCell1.fatherCellIndex = g.cellIndex
+
+                // 鍔犲叆缁撴灉闆嗗悎
+                newGridCellList.add(newGridCell1)
+
+                // 缃戞牸鍒楀惊鐜�(浠庣2鍒楀紑濮�)
+                for (col in 1 until scale) {
+                    val newGridCell = GridCell()
+                    newGridCell.point1Lon = newGridCell1.point1Lon + dx2 * col.toBigDecimal()
+                    newGridCell.point1Lat = newGridCell1.point1Lat + dy2 * col.toBigDecimal()
+                    newGridCell.point2Lon = newGridCell1.point2Lon + dx2 * col.toBigDecimal()
+                    newGridCell.point2Lat = newGridCell1.point2Lat + dy2 * col.toBigDecimal()
+                    newGridCell.point3Lon = newGridCell1.point3Lon + dx3 * col.toBigDecimal()
+                    newGridCell.point3Lat = newGridCell1.point3Lat + dy3 * col.toBigDecimal()
+                    newGridCell.point4Lon = newGridCell1.point4Lon + dx3 * col.toBigDecimal()
+                    newGridCell.point4Lat = newGridCell1.point4Lat + dy3 * col.toBigDecimal()
+                    newGridCell.longitude = newGridCell.point1Lon + dxC
+                    newGridCell.latitude = newGridCell.point1Lat + dyC
+
+                    newGridCell.groupId = groupId
+                    newGridCell.cellIndex = ++cellIndex
+                    newGridCell.fatherCellIndex = g.cellIndex
+
+                    newGridCellList.add(newGridCell)
+                }
+            }
+        }
+        return newGridCellList
+    }
+
+    /**
+     * 鎷嗗垎鏁版嵁锛屽皢鍘熷鍗槦缃戞牸閬ユ祴鏁版嵁鏄犲皠鍒板搴旂粏鍒嗙綉鏍间笂
+     * @date 2025.2.7
+     * @param subGridCellList 缁嗗垎缃戞牸
+     * @param subGridData 缁嗗垎缃戞牸瀵瑰簲鐨勬暟鎹储寮�
+     * @param originGridDataDetailList 缁嗗垎缃戞牸鎵�灞炵綉鏍肩殑鍘熷缃戞牸鏁版嵁
+     * @return 鏄犲皠鍚庣殑缁嗗垎缃戞牸閬ユ祴鏁版嵁
+     */
+    fun splitData(
+        subGridCellList: List<GridCell?>, subGridData: GridData, originGridDataDetailList: List<GridDataDetail?>
+    ): List<GridDataDetail> {
+        if (subGridCellList.isEmpty() || originGridDataDetailList.isEmpty()) return emptyList()
+
+        val result = mutableListOf<GridDataDetail>()
+
+        // 灏嗙粏鍒嗙綉鏍兼寜鐓х埗缃戞牸id鍜岃嚜韬綉鏍糹d杩涜鍗囧簭鎺掑垪
+        val _subGridCellList = subGridCellList.sortedWith(Comparator { o1, o2 ->
+            if (o1 == null && o2 == null) {
+                return@Comparator 0
+            } else if (o1 == null) {
+                return@Comparator -1
+            } else if (o2 == null) {
+                return@Comparator 1
+            } else {
+                if (o1.fatherCellIndex == o2.fatherCellIndex) {
+                    return@Comparator o1.cellIndex - o2.cellIndex
+                } else {
+                    return@Comparator o1.fatherCellIndex - o2.fatherCellIndex
+                }
+            }
+        })
+
+        // 灏嗗師濮嬬綉鏍兼暟鎹寜鐓х綉鏍糹d鍗囧簭鎺掑垪
+        val _originGridDataDetailIterator = originGridDataDetailList.sortedBy { it?.cellId }.iterator()
+        var fatherGridData = _originGridDataDetailIterator.next()
+
+        // 閬嶅巻缁嗗垎缃戞牸锛屼负姣忎釜缁嗗垎缃戞牸鐢熸垚涓�鏉$綉鏍兼暟鎹�
+        _subGridCellList.forEach {
+            while (fatherGridData?.cellId != it?.fatherCellIndex && _originGridDataDetailIterator.hasNext()) {
+                fatherGridData = _originGridDataDetailIterator.next()
+            }
+            val subGridDataDetail = GridDataDetail().apply {
+                dataId = subGridData.id
+                groupId = it?.groupId
+                cellId = it?.cellIndex
+                pm25 = fatherGridData?.pm25
+                rank = fatherGridData?.rank
+            }
+
+            result.add(subGridDataDetail)
+        }
+
+        return result
+    }
+
+    /**
+     * 璧拌埅鏁版嵁鍜屽崼鏄熺綉鏍艰瀺鍚�
+     * 鏁版嵁铻嶅悎閲囩敤鍧囧�兼柟寮忕粺璁★紙鏆傛椂锛�
+     * @date 2025.2.7
+     * @param realTimeDataList 寰呰瀺鍚堢殑璧拌埅鐩戞祴鏁版嵁
+     * @param gridData 铻嶅悎鍚庣殑鏁版嵁缁勭储寮�
+     * @param gridCellList 寰呰瀺鍚堢殑鍗槦缃戞牸
+     * @return 铻嶅悎鍚庣殑缃戞牸鐩戞祴鏁版嵁
+     */
+    fun dataFusion(
+        realTimeDataList: List<BaseRealTimeData>,
+        gridData: GridData?,
+        gridCellList: List<GridCell?>,
+    ): List<GridDataDetail> {
+        // 閬嶅巻璧拌埅鐩戞祴鏁版嵁锛岃绠楁瘡涓偣鎵�鍦ㄧ綉鏍�
+        val dataMap = mutableMapOf<GridCell, MutableList<BaseRealTimeData>>()
+        realTimeDataList.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
+    }
+
+    /**
+     * 璁$畻鐑姏鍥剧綉鏍硷紝鍗崇綉鏍煎懆杈规墿鏁e奖鍝嶆潈閲嶈绠�
+     * @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
     }
 
 }
\ No newline at end of file

--
Gitblit v1.9.3