1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package com.flightfeather.uav.biz.satellite
 
import com.flightfeather.uav.common.utils.MapUtil
import com.flightfeather.uav.domain.entity.GridCell
import kotlin.math.ceil
import kotlin.math.floor
 
/**
 * 卫星网格计算工具
 */
object SatelliteGridUtil {
 
    /**
     * 计算坐标点在哪个卫星网格内
     * @date 2025.2.14
     * @param point 坐标点
     * @param gridCellList 卫星网格
     */
    fun searchGirdIn(point: Pair<Double, Double>, gridCellList: List<GridCell?>): GridCell? {
        for (i in gridCellList.indices) {
            val gridCell = gridCellList[i] ?: continue
            val polygon = listOf(
                gridCell.point1Lon.toDouble() to gridCell.point1Lat.toDouble(),
                gridCell.point2Lon.toDouble() to gridCell.point2Lat.toDouble(),
                gridCell.point3Lon.toDouble() to gridCell.point3Lat.toDouble(),
                gridCell.point4Lon.toDouble() to gridCell.point4Lat.toDouble(),
            )
            if (MapUtil.isPointInPolygon(point, polygon)) {
                return gridCell
            }
        }
 
        return null
    }
 
    /**
     * 计算扩散网格,根据给定网格,计算在其周围固定半径下的所有网格
     * 网格的形式为原始卫星遥测网格以及细分网格
     * 原始卫星遥测网格的编号顺序为从左到右,从上到下编号依次递增
     * 细分网格在原始卫星遥测网格的基础上,每个网格内部按从左到右,从上到下编号依次递增
     * @param gridCellIndex 中心点网格索引值
     * @param gridCellList 所有网格数组
     * @param option 网格组的参数信息
     * @param searchLength 扩散的网格半径数量
     * @return 计算得出的扩散网格对象数组
     */
    fun searchDiffuseGrid(
        gridCellIndex: Int,
        gridCellList: List<GridCell?>,
        option: GridGroupOption,
        searchLength: Int,
    ): List<GridCell> {
        val hOffset = option.eachWidth;
        val wOffset = 1;
 
        val cellIdMin = 1
        val cellIdMax = option.width * option.height
 
        val searchWidth = 0 - searchLength
        val searchHeight = 0 - searchLength
 
        val result = mutableListOf<GridCell>()
 
        val eachRange = getCellWidthRange(gridCellIndex, option.eachWidth, option.eachHeight)?: return emptyList()
        val groupRange = getCellWidthRange(
            ceil(gridCellIndex.toDouble() / (option.eachWidth * option.eachHeight)).toInt(),
            option.width / option.eachWidth,
            option.height / option.eachHeight
        ) ?: return emptyList()
 
        for (w in searchWidth..searchLength) {
            // 先进行横向的坐标变换
            var _cellId = gridCellIndex + w * wOffset;
            if (_cellId < eachRange.first || _cellId > eachRange.second) {
                val cellOffset = if (_cellId < eachRange.first) _cellId - eachRange.first else _cellId - eachRange.second
 
                val groupOffset = if (cellOffset / option.eachWidth > 0) {
                    ceil(cellOffset.toDouble() / option.eachWidth).toInt()
                } else {
                    floor(cellOffset.toDouble() / option.eachWidth).toInt()
                }
 
                val newEachRange =
                    (eachRange.first + groupOffset * option.eachWidth * option.eachHeight) to
                            (eachRange.second + groupOffset * option.eachWidth * option.eachHeight)
 
                _cellId = if (groupOffset > 0) {
                    newEachRange.first + cellOffset - wOffset
                } else {
                    newEachRange.second + cellOffset + wOffset;
                }
 
                val _groupId = ceil(_cellId.toDouble() / (option.eachWidth * option.eachHeight)).toInt()
 
                if (_groupId < groupRange.first || _groupId > groupRange.second) {
                    continue;
                }
            }
 
            for (h in searchHeight..searchLength) {
                if (w == 0 && h == 0) continue;
 
                val _eachRange = getCellWidthRange(_cellId, option.eachWidth, option.eachHeight) ?: return emptyList()
 
                val wOffset = _cellId - _eachRange.first;
                var _resCellId = _cellId + h * hOffset;
                if (_resCellId < cellIdMin || _resCellId > cellIdMax) continue;
 
                val total = option.eachWidth * option.eachHeight;
                val x = ceil(_cellId.toDouble() / total).toInt() - 1;
                val eachCellIdMin = 1 + x * total;
                val eachCellIdMax = total + x * total;
                val topCell = eachCellIdMin + wOffset;
                val bottomCell = eachCellIdMax - option.eachWidth + 1 + wOffset;
                if (_resCellId < eachCellIdMin || _resCellId > eachCellIdMax) {
                    val cellOffset = if (_resCellId < eachCellIdMin) {
                        _resCellId - topCell
                    } else {
                        _resCellId - bottomCell;
                    }
 
                    val newTopCell = if (cellOffset > 0) {
                        topCell + option.width * option.eachHeight
                    } else {
                        topCell - option.width * option.eachHeight;
                    }
 
                    val newBottomCell = if (cellOffset > 0) {
                        bottomCell + option.width * option.eachHeight
                    } else {
                        bottomCell - option.width * option.eachHeight;
                    }
 
                    _resCellId = if (cellOffset > 0) {
                        newTopCell + cellOffset - hOffset
                    } else {
                        newBottomCell + cellOffset + hOffset;
                    }
                }
                gridCellList.find { it?.cellIndex == _resCellId }?.let { result.add(it) }
            }
        }
 
        return result;
    }
 
    /**
     * Fixme 2025.3.14: 周边网格的查找,后续通过将网格用坐标点的表示方式来直接计算(x, y)
     * 根据网格索引,获取其所在东西方向的网格索引范围
     * @param cellIndex 网格索引
     * @param width 网格宽度,指东西方向上的网格数量
     * @param height 网格高度,指南北方向上的网格数量
     * @return 返回网格索引值范围
     */
    fun getCellWidthRange(cellIndex: Int, width: Int, height: Int): Pair<Int, Int>? {
        val total = width * height;
        val x = (ceil(cellIndex.toDouble() / total) - 1).toInt()
        val first = 1 + x * total
        val last = width + x * total;
        var scale = 0;
        while (scale < height) {
            val min = first + scale * width;
            val max = last + scale * width;
            if (cellIndex in min..max) {
                return min to max
            }
            scale++;
        }
        return null
    }
}