// ================================
|
// 3D占用网格系统模块
|
// ================================
|
import * as Cesium from 'cesium'
|
|
class OccupancyGrid {
|
constructor(viewer, config = {}, gridParams) {
|
this.viewer = viewer
|
this.gridEntities = []
|
this.tilesBoundingBoxes = [] // 存储瓦片边界框
|
this.gridParams = gridParams
|
|
// 网格配置参数 - 可自定义
|
this.config = {
|
// 网格单元尺寸配置
|
gridSize: config.gridSize || 50, // 网格单元大小(米)
|
gridWidth: config.gridWidth || 50, // 网格宽度(米)
|
gridHeight: config.gridHeight || 50, // 网格高度(米)
|
gridDepth: config.gridDepth || 50, // 网格深度(米)
|
|
// 区域扩展配置
|
heightExtension: config.heightExtension || 120, // 向上向下各扩展120米,确保4层网格(总高度240米,4×50=200米,留有余量)
|
widthExtension: config.widthExtension || 100, // 水平方向扩展100米
|
|
// 颜色配置
|
occupiedColor: config.occupiedColor || Cesium.Color.RED.withAlpha(0.7), // 占用网格颜色(红色)
|
freeColor: config.freeColor || Cesium.Color.GREEN.withAlpha(0.3), // 空闲网格颜色(绿色)
|
occupiedOutlineColor: config.occupiedOutlineColor || Cesium.Color.DARKRED, // 占用网格边框颜色
|
freeOutlineColor: config.freeOutlineColor || Cesium.Color.DARKGREEN, // 空闲网格边框颜色
|
outlineWidth: config.outlineWidth || 1, // 边框宽度
|
|
// 透明度配置
|
occupiedAlpha: config.occupiedAlpha || 0.7, // 占用网格透明度
|
freeAlpha: config.freeAlpha || 0.3, // 空闲网格透明度
|
|
// 性能优化配置
|
maxGridCount: config.maxGridCount || 10000, // 最大网格数量限制
|
enableBatching: config.enableBatching || true, // 是否启用批处理优化
|
|
// 调试配置
|
showOccupiedOnly: config.showOccupiedOnly || false, // 是否只显示占用的网格
|
showFreeOnly: config.showFreeOnly || false, // 是否只显示空闲的网格
|
enableLogging: config.enableLogging || true // 是否启用详细日志
|
}
|
|
// 向后兼容旧参数
|
this.gridSize = this.config.gridSize
|
this.heightExtension = this.config.heightExtension
|
}
|
|
// ================================
|
// 更新配置
|
// ================================
|
updateConfig (newConfig) {
|
this.config = { ...this.config, ...newConfig }
|
|
// 更新向后兼容参数
|
this.gridSize = this.config.gridSize
|
this.heightExtension = this.config.heightExtension
|
|
if (this.config.enableLogging) {
|
console.log('网格配置已更新:', this.config)
|
}
|
}
|
|
// ================================
|
// 获取当前配置
|
// ================================
|
getConfig () {
|
return { ...this.config }
|
}
|
|
// ================================
|
// 生成占用网格 - 使用配置参数
|
// ================================
|
async generateOccupancyGrid (waypoints) {
|
if (waypoints.length < 2) {
|
console.warn('需要至少2个航点来生成占用网格')
|
return
|
}
|
|
// 清除之前的网格
|
this.clearGrid()
|
|
// 获取起点和终点
|
const startPoint = waypoints[0]
|
const endPoint = waypoints[waypoints.length - 1]
|
|
// 计算边界框
|
const bounds = this.calculateBounds(startPoint, endPoint)
|
|
// 收集3D瓦片的边界框信息
|
await this.collectTilesBoundingBoxes(bounds)
|
|
// 生成网格
|
await this.createGrid(bounds)
|
|
if (this.config.enableLogging) {
|
console.log('3D占用网格生成完成')
|
console.log(`网格尺寸: ${this.config.gridWidth}m x ${this.config.gridHeight}m x ${this.config.gridDepth}m`)
|
console.log(`总网格数量: ${this.gridEntities.length}`)
|
console.log(`瓦片边界框数量: ${this.tilesBoundingBoxes.length}`)
|
}
|
}
|
|
// ================================
|
// 使用航线瓦片数据生成占用网格 - 使用配置参数
|
// ================================
|
async generateOccupancyGridWithTiles (waypoints, flightPathTiles) {
|
if (waypoints.length < 2) {
|
console.warn('需要至少2个航点来生成占用网格')
|
return
|
}
|
|
if (this.config.enableLogging) {
|
console.log(`开始生成占用网格,将分析网格范围内的所有叶子瓦片节点`)
|
}
|
|
// 清除之前的网格
|
this.clearGrid()
|
|
// 获取起点和终点
|
const startPoint = waypoints[0]
|
const endPoint = waypoints[waypoints.length - 1]
|
|
// 计算边界框
|
const bounds = this.calculateBounds(startPoint, endPoint)
|
|
// 获取瓦片集,用于遍历叶子节点
|
this.tileset = this.getTileset()
|
|
if (!this.tileset) {
|
console.warn('未找到瓦片集,无法生成占用网格')
|
return
|
}
|
|
// 生成网格
|
await this.createGrid(bounds)
|
|
if (this.config.enableLogging) {
|
console.log('使用叶子瓦片节点的3D占用网格生成完成')
|
console.log(`网格尺寸: ${this.config.gridWidth}m x ${this.config.gridHeight}m x ${this.config.gridDepth}m`)
|
console.log(`总网格数量: ${this.gridEntities.length}`)
|
}
|
}
|
|
// ================================
|
// 获取瓦片集
|
// ================================
|
getTileset () {
|
const primitives = this.viewer.scene.primitives
|
|
for (let i = 0; i < primitives.length; i++) {
|
const primitive = primitives.get(i)
|
if (primitive instanceof Cesium.Cesium3DTileset) {
|
return primitive
|
}
|
}
|
|
return null
|
}
|
|
// ================================
|
// 计算边界框 - 使用配置参数
|
// ================================
|
calculateBounds (startPoint, endPoint) {
|
// 转换为地理坐标
|
const startCartographic = Cesium.Cartographic.fromCartesian(startPoint.position)
|
const endCartographic = Cesium.Cartographic.fromCartesian(endPoint.position)
|
|
// 计算基础边界
|
let minLon = Math.min(startCartographic.longitude, endCartographic.longitude)
|
let maxLon = Math.max(startCartographic.longitude, endCartographic.longitude)
|
let minLat = Math.min(startCartographic.latitude, endCartographic.latitude)
|
let maxLat = Math.max(startCartographic.latitude, endCartographic.latitude)
|
|
// 应用水平扩展
|
const earthRadius = 6371000
|
const widthExtensionLon = this.config.widthExtension / (earthRadius * Math.cos((minLat + maxLat) / 2))
|
const widthExtensionLat = this.config.widthExtension / earthRadius
|
|
minLon -= widthExtensionLon
|
maxLon += widthExtensionLon
|
minLat -= widthExtensionLat
|
maxLat += widthExtensionLat
|
|
// 计算平均高度和垂直扩展
|
const avgHeight = (startPoint.height + endPoint.height) / 2
|
const heightOffset = 20 // 向上平移50米,避免网格被tileset遮盖
|
const minHeight = avgHeight - this.config.heightExtension + heightOffset
|
const maxHeight = avgHeight + this.config.heightExtension + heightOffset
|
|
const bounds = {
|
minLon: minLon,
|
maxLon: maxLon,
|
minLat: minLat,
|
maxLat: maxLat,
|
minHeight: minHeight,
|
maxHeight: maxHeight,
|
avgHeight: avgHeight
|
}
|
|
if (this.config.enableLogging) {
|
console.log('计算网格边界范围:', {
|
经度范围: `${Cesium.Math.toDegrees(minLon).toFixed(6)} 到 ${Cesium.Math.toDegrees(maxLon).toFixed(6)}`,
|
纬度范围: `${Cesium.Math.toDegrees(minLat).toFixed(6)} 到 ${Cesium.Math.toDegrees(maxLat).toFixed(6)}`,
|
高度范围: `${minHeight.toFixed(1)}m 到 ${maxHeight.toFixed(1)}m (向上平移50m)`,
|
水平扩展: `${this.config.widthExtension}m`,
|
垂直扩展: `${this.config.heightExtension}m`
|
})
|
}
|
|
return bounds
|
}
|
|
// ================================
|
// 收集3D瓦片的边界框信息
|
// ================================
|
async collectTilesBoundingBoxes (bounds) {
|
this.tilesBoundingBoxes = []
|
|
// 获取所有的图元
|
const primitives = this.viewer.scene.primitives
|
let tileset = null
|
|
// 查找3D Tiles图层
|
for (let i = 0; i < primitives.length; i++) {
|
const primitive = primitives.get(i)
|
if (primitive instanceof Cesium.Cesium3DTileset) {
|
tileset = primitive
|
break
|
}
|
}
|
|
if (!tileset || !tileset.root) {
|
console.warn('未找到3D Tiles图层')
|
return
|
}
|
|
// 等待瓦片集加载完成
|
if (!tileset.ready) {
|
await tileset.readyPromise
|
}
|
|
// 遍历瓦片树并收集边界框
|
this.traverseTiles(tileset.root, bounds)
|
|
console.log(`收集到${this.tilesBoundingBoxes.length}个瓦片边界框`)
|
}
|
|
// ================================
|
// 收集航线瓦片的边界框信息
|
// ================================
|
collectFlightPathTilesBoundingBoxes (flightPathTiles) {
|
console.log('开始收集航线瓦片边界框信息')
|
this.tilesBoundingBoxes = []
|
|
flightPathTiles.forEach((tile, index) => {
|
try {
|
// 获取边界体信息
|
let boundingVolume = null
|
|
if (tile.boundingVolume) {
|
boundingVolume = tile.boundingVolume
|
} else if (tile._boundingVolume) {
|
boundingVolume = tile._boundingVolume
|
} else if (tile.contentBoundingVolume) {
|
boundingVolume = tile.contentBoundingVolume
|
}
|
|
if (!boundingVolume) {
|
console.warn(`瓦片 ${index} 没有边界体信息`)
|
return
|
}
|
|
let boundingBox = null
|
|
// 处理不同类型的边界体
|
if (boundingVolume instanceof Cesium.OrientedBoundingBox) {
|
boundingBox = this.orientedBoundingBoxToAxisAligned(boundingVolume)
|
}
|
else if (boundingVolume instanceof Cesium.BoundingSphere) {
|
boundingBox = this.sphereToAxisAligned(boundingVolume)
|
}
|
else if (boundingVolume.orientedBoundingBox) {
|
boundingBox = this.orientedBoundingBoxToAxisAligned(boundingVolume.orientedBoundingBox)
|
}
|
else if (boundingVolume.boundingSphere) {
|
boundingBox = this.sphereToAxisAligned(boundingVolume.boundingSphere)
|
}
|
else if (boundingVolume._orientedBoundingBox) {
|
boundingBox = this.orientedBoundingBoxToAxisAligned(boundingVolume._orientedBoundingBox)
|
}
|
else if (boundingVolume._boundingSphere) {
|
boundingBox = this.sphereToAxisAligned(boundingVolume._boundingSphere)
|
}
|
|
if (boundingBox) {
|
this.tilesBoundingBoxes.push(boundingBox)
|
console.log(`添加瓦片 ${index} 的边界框`)
|
} else {
|
console.warn(`无法处理瓦片 ${index} 的边界体类型`)
|
}
|
} catch (error) {
|
console.warn(`处理瓦片 ${index} 边界体时出错:`, error)
|
}
|
})
|
|
console.log(`成功收集 ${this.tilesBoundingBoxes.length} 个航线瓦片边界框`)
|
}
|
|
// ================================
|
// 遍历瓦片树
|
// ================================
|
traverseTiles (tile, bounds) {
|
if (!tile) return
|
|
// 获取瓦片的边界框
|
const boundingVolume = tile.boundingVolume
|
if (boundingVolume) {
|
let boundingBox = null
|
|
try {
|
// 检查是否有中心点和半径(球体)
|
if (boundingVolume.center && boundingVolume.radius !== undefined) {
|
boundingBox = this.sphereToBox(boundingVolume)
|
}
|
// 检查是否有中心点和半轴(盒子)
|
else if (boundingVolume.center && boundingVolume.halfAxes) {
|
const halfAxes = boundingVolume.halfAxes
|
const halfExtents = new Cesium.Cartesian3(
|
Math.max(10, Cesium.Cartesian3.magnitude(new Cesium.Cartesian3(halfAxes[0], halfAxes[1], halfAxes[2]))),
|
Math.max(10, Cesium.Cartesian3.magnitude(new Cesium.Cartesian3(halfAxes[3], halfAxes[4], halfAxes[5]))),
|
Math.max(10, Cesium.Cartesian3.magnitude(new Cesium.Cartesian3(halfAxes[6], halfAxes[7], halfAxes[8])))
|
)
|
boundingBox = {
|
center: boundingVolume.center,
|
halfExtents: halfExtents
|
}
|
}
|
// 检查是否有区域信息
|
else if (boundingVolume.west !== undefined && boundingVolume.east !== undefined) {
|
boundingBox = this.regionToBox(boundingVolume)
|
}
|
// 默认情况:创建一个小的边界框
|
else if (boundingVolume.center) {
|
boundingBox = {
|
center: boundingVolume.center,
|
halfExtents: new Cesium.Cartesian3(50, 50, 50) // 默认50米
|
}
|
}
|
} catch (error) {
|
console.warn('处理边界体积时出错:', error)
|
return
|
}
|
|
if (boundingBox && this.isBoundingBoxInRegion(boundingBox, bounds)) {
|
this.tilesBoundingBoxes.push(boundingBox)
|
}
|
}
|
|
// 递归遍历子瓦片
|
if (tile.children && tile.children.length > 0) {
|
for (const child of tile.children) {
|
this.traverseTiles(child, bounds)
|
}
|
}
|
}
|
|
// ================================
|
// 将球体转换为包围盒
|
// ================================
|
sphereToBox (sphere) {
|
const center = sphere.center
|
const radius = sphere.radius
|
|
// 创建一个立方体包围盒
|
const halfExtents = new Cesium.Cartesian3(radius, radius, radius)
|
|
return {
|
center: center,
|
halfExtents: halfExtents
|
}
|
}
|
|
// ================================
|
// 将区域转换为包围盒
|
// ================================
|
regionToBox (region) {
|
// 计算区域的中心点
|
const centerLon = (region.west + region.east) / 2
|
const centerLat = (region.south + region.north) / 2
|
const centerHeight = (region.minimumHeight + region.maximumHeight) / 2
|
|
const center = Cesium.Cartesian3.fromRadians(centerLon, centerLat, centerHeight)
|
|
// 计算区域的半尺寸
|
const lonExtent = (region.east - region.west) / 2
|
const latExtent = (region.north - region.south) / 2
|
const heightExtent = (region.maximumHeight - region.minimumHeight) / 2
|
|
// 转换为笛卡尔坐标的半扩展
|
const earthRadius = 6371000
|
const halfExtents = new Cesium.Cartesian3(
|
lonExtent * earthRadius * Math.cos(centerLat),
|
latExtent * earthRadius,
|
heightExtent
|
)
|
|
return {
|
center: center,
|
halfExtents: halfExtents
|
}
|
}
|
|
// ================================
|
// 检查边界框是否在感兴趣区域内
|
// ================================
|
isBoundingBoxInRegion (boundingBox, bounds) {
|
// 将边界框中心转换为地理坐标
|
const centerCartographic = Cesium.Cartographic.fromCartesian(boundingBox.center)
|
|
// 检查是否在地理范围内
|
return (centerCartographic.longitude >= bounds.minLon &&
|
centerCartographic.longitude <= bounds.maxLon &&
|
centerCartographic.latitude >= bounds.minLat &&
|
centerCartographic.latitude <= bounds.maxLat &&
|
centerCartographic.height >= bounds.minHeight &&
|
centerCartographic.height <= bounds.maxHeight)
|
}
|
|
// ================================
|
// 创建网格 - 使用配置参数
|
// ================================
|
async createGrid (bounds) {
|
// 将地理坐标转换为距离(米)
|
const earthRadius = 6371000 // 地球半径(米)
|
|
// 计算经纬度范围对应的实际距离
|
const latDistance = (bounds.maxLat - bounds.minLat) * earthRadius
|
const lonDistance = (bounds.maxLon - bounds.minLon) * earthRadius * Math.cos((bounds.minLat + bounds.maxLat) / 2)
|
const heightDistance = bounds.maxHeight - bounds.minHeight
|
|
// 计算网格数量 - 使用配置的网格尺寸
|
const gridCountX = Math.ceil(lonDistance / this.config.gridWidth)
|
const gridCountY = Math.ceil(latDistance / this.config.gridHeight)
|
const gridCountZ = Math.ceil(heightDistance / this.config.gridDepth)
|
|
const totalGridCount = gridCountX * gridCountY * gridCountZ
|
|
if (this.config.enableLogging) {
|
console.log(`计划生成网格数量: ${gridCountX} x ${gridCountY} x ${gridCountZ} = ${totalGridCount}`)
|
console.log(`网格单元尺寸: 宽${this.config.gridWidth}m x 高${this.config.gridHeight}m x 深${this.config.gridDepth}m`)
|
console.log(`实际距离: 经度${lonDistance.toFixed(1)}m x 纬度${latDistance.toFixed(1)}m x 高度${heightDistance.toFixed(1)}m`)
|
console.log(`垂直方向: 高度范围${heightDistance.toFixed(1)}m ÷ 网格深度${this.config.gridDepth}m = ${gridCountZ}层网格`)
|
}
|
|
// 性能保护:检查网格数量是否超出限制
|
if (totalGridCount > this.config.maxGridCount) {
|
console.warn(`网格数量 ${totalGridCount} 超过最大限制 ${this.config.maxGridCount},将进行优化处理`)
|
|
// 自动调整网格尺寸
|
const scaleFactor = Math.cbrt(totalGridCount / this.config.maxGridCount)
|
const adjustedGridSize = this.config.gridSize * scaleFactor
|
|
console.log(`自动调整网格尺寸从 ${this.config.gridSize}m 到 ${adjustedGridSize.toFixed(1)}m`)
|
|
// 重新计算网格数量
|
const newGridCountX = Math.ceil(lonDistance / adjustedGridSize)
|
const newGridCountY = Math.ceil(latDistance / adjustedGridSize)
|
const newGridCountZ = Math.ceil(heightDistance / adjustedGridSize)
|
|
if (this.config.enableLogging) {
|
console.log(`调整后网格数量: ${newGridCountX} x ${newGridCountY} x ${newGridCountZ} = ${newGridCountX * newGridCountY * newGridCountZ}`)
|
}
|
|
// 使用调整后的参数
|
return this.createGridWithCustomSize(bounds, newGridCountX, newGridCountY, newGridCountZ, adjustedGridSize)
|
}
|
|
// 正常情况下生成网格
|
return this.createGridWithCustomSize(bounds, gridCountX, gridCountY, gridCountZ, this.config.gridSize)
|
}
|
|
// ================================
|
// 使用自定义尺寸创建网格
|
// ================================
|
async createGridWithCustomSize (bounds, gridCountX, gridCountY, gridCountZ, gridSize) {
|
let occupiedCount = 0
|
let freeCount = 0
|
|
// 批处理参数
|
const batchSize = this.config.enableBatching ? 100 : 1
|
let batchCount = 0
|
|
// 生成网格立方体
|
for (let i = 0; i < gridCountX; i++) {
|
for (let j = 0; j < gridCountY; j++) {
|
for (let k = 0; k < gridCountZ; k++) {
|
const gridCenter = this.calculateGridCenter(bounds, i, j, k, gridCountX, gridCountY, gridCountZ)
|
const isOccupied = await this.checkOccupancy(gridCenter)
|
|
// 计算网格索引号 (i, j, k)
|
const gridIndex = { x: i, y: j, z: k }
|
|
// 根据配置决定是否显示
|
let shouldCreate = true
|
if (this.config.showOccupiedOnly && !isOccupied) {
|
shouldCreate = false
|
}
|
if (this.config.showFreeOnly && isOccupied) {
|
shouldCreate = false
|
}
|
|
if (shouldCreate) {
|
this.createGridCube(gridCenter, isOccupied, gridSize, gridIndex)
|
}
|
|
// 统计
|
if (isOccupied) {
|
occupiedCount++
|
} else {
|
freeCount++
|
}
|
|
// 批处理优化:每处理一定数量后让出控制权
|
batchCount++
|
if (this.config.enableBatching && batchCount >= batchSize) {
|
batchCount = 0
|
await new Promise(resolve => setTimeout(resolve, 1)) // 让出1ms给浏览器
|
}
|
}
|
}
|
}
|
|
if (this.config.enableLogging) {
|
this.gridParams.hasGrid = true
|
console.log(`网格生成完成 - 占用: ${occupiedCount}, 空闲: ${freeCount}, 总计: ${this.gridEntities.length}`)
|
}
|
}
|
|
// ================================
|
// 计算网格中心点
|
// ================================
|
calculateGridCenter (bounds, i, j, k, gridCountX, gridCountY, gridCountZ) {
|
// 计算网格中心的地理坐标
|
const lonStep = (bounds.maxLon - bounds.minLon) / gridCountX
|
const latStep = (bounds.maxLat - bounds.minLat) / gridCountY
|
const heightStep = (bounds.maxHeight - bounds.minHeight) / gridCountZ
|
|
const lon = bounds.minLon + (i + 0.5) * lonStep
|
const lat = bounds.minLat + (j + 0.5) * latStep
|
const height = bounds.minHeight + (k + 0.5) * heightStep
|
|
return {
|
longitude: lon,
|
latitude: lat,
|
height: height
|
}
|
}
|
|
// ================================
|
// 检查占用状态 - 基于网格范围内的叶子瓦片节点及其外包盒求交
|
// ================================
|
async checkOccupancy (gridCenter) {
|
if (!this.tileset) {
|
return false // 没有瓦片集,默认未占用
|
}
|
|
// 计算网格单元的边界范围
|
const gridBounds = this.calculateGridBounds(gridCenter)
|
|
// 收集网格范围内的所有叶子瓦片节点
|
const leafTilesInGrid = []
|
this.findLeafTilesInGridBounds(this.tileset.root, gridBounds, leafTilesInGrid)
|
console.log(this.tileset.root, gridBounds, 'this.tileset.root')
|
if (leafTilesInGrid.length === 0) {
|
return false // 没有叶子瓦片,标记为未占用
|
}
|
|
// 使用叶子节点的外包盒与网格单元进行精确求交计算
|
const hasIntersection = this.checkGridTileIntersections(gridBounds, leafTilesInGrid)
|
|
if (hasIntersection) {
|
console.log(`网格单元 [${Cesium.Math.toDegrees(gridCenter.longitude).toFixed(6)}, ${Cesium.Math.toDegrees(gridCenter.latitude).toFixed(6)}, ${gridCenter.height.toFixed(1)}] 与 ${leafTilesInGrid.length} 个叶子瓦片相交`)
|
}
|
|
return hasIntersection
|
}
|
|
// ================================
|
// 递归查找网格范围内的叶子瓦片节点
|
// ================================
|
findLeafTilesInGridBounds (tile, gridBounds, leafTiles) {
|
if (!tile) {
|
return
|
}
|
|
// 检查是否为叶子节点
|
const isLeafNode = !tile.children || tile.children.length === 0
|
|
// 首先检查当前瓦片是否与网格边界相交
|
const intersects = this.isTileIntersectingGridBounds(tile, gridBounds)
|
|
if (!intersects) {
|
return // 如果当前瓦片不与网格相交,则跳过其所有子节点
|
}
|
|
if (isLeafNode) {
|
// 只有叶子节点才添加到结果中
|
leafTiles.push(tile)
|
} else {
|
// 中间节点与网格相交,继续遍历其子节点
|
for (let i = 0; i < tile.children.length; i++) {
|
this.findLeafTilesInGridBounds(tile.children[i], gridBounds, leafTiles)
|
}
|
}
|
}
|
|
// ================================
|
// 检查瓦片是否与网格边界相交(简化版本,用于预筛选)
|
// ================================
|
isTileIntersectingGridBounds (tile, gridBounds) {
|
// 获取瓦片的边界体信息
|
let boundingVolume = null
|
|
if (tile.boundingVolume) {
|
boundingVolume = tile.boundingVolume
|
} else if (tile._boundingVolume) {
|
boundingVolume = tile._boundingVolume
|
} else if (tile.contentBoundingVolume) {
|
boundingVolume = tile.contentBoundingVolume
|
}
|
|
if (!boundingVolume) {
|
return false // 没有边界体信息
|
}
|
|
try {
|
// 简化的边界检测,用于快速预筛选
|
let tileBounds = null
|
|
if (boundingVolume instanceof Cesium.OrientedBoundingBox) {
|
tileBounds = this.getSimpleBoundsFromOBB(boundingVolume)
|
}
|
else if (boundingVolume instanceof Cesium.BoundingSphere) {
|
tileBounds = this.getSimpleBoundsFromSphere(boundingVolume)
|
}
|
else if (boundingVolume.orientedBoundingBox) {
|
tileBounds = this.getSimpleBoundsFromOBB(boundingVolume.orientedBoundingBox)
|
}
|
else if (boundingVolume.boundingSphere) {
|
tileBounds = this.getSimpleBoundsFromSphere(boundingVolume.boundingSphere)
|
}
|
else if (boundingVolume._orientedBoundingBox) {
|
tileBounds = this.getSimpleBoundsFromOBB(boundingVolume._orientedBoundingBox)
|
}
|
else if (boundingVolume._boundingSphere) {
|
tileBounds = this.getSimpleBoundsFromSphere(boundingVolume._boundingSphere)
|
}
|
|
if (!tileBounds) {
|
return false
|
}
|
|
// 简单的3D AABB相交检测
|
const tolerance = 0.0001
|
|
const intersects = !(
|
gridBounds.maxLon < tileBounds.minLon - tolerance ||
|
gridBounds.minLon > tileBounds.maxLon + tolerance ||
|
gridBounds.maxLat < tileBounds.minLat - tolerance ||
|
gridBounds.minLat > tileBounds.maxLat + tolerance ||
|
gridBounds.maxHeight < tileBounds.minHeight - tolerance ||
|
gridBounds.minHeight > tileBounds.maxHeight + tolerance
|
)
|
|
return intersects
|
|
} catch (error) {
|
console.warn('预筛选瓦片边界体时出错:', error)
|
return true // 出错时保守地返回true,让后续精确计算处理
|
}
|
}
|
|
// ================================
|
// 从OrientedBoundingBox获取简化边界(用于预筛选)
|
// ================================
|
getSimpleBoundsFromOBB (obb) {
|
const center = obb.center
|
const halfAxes = obb.halfAxes
|
|
// 计算边界盒的大致范围
|
const xAxis = new Cesium.Cartesian3()
|
const yAxis = new Cesium.Cartesian3()
|
const zAxis = new Cesium.Cartesian3()
|
Cesium.Matrix3.getColumn(halfAxes, 0, xAxis)
|
Cesium.Matrix3.getColumn(halfAxes, 1, yAxis)
|
Cesium.Matrix3.getColumn(halfAxes, 2, zAxis)
|
|
// 估算边界盒的最大扩展
|
const maxExtent = Math.max(
|
Cesium.Cartesian3.magnitude(xAxis),
|
Cesium.Cartesian3.magnitude(yAxis),
|
Cesium.Cartesian3.magnitude(zAxis)
|
)
|
|
// 将中心转换为地理坐标
|
const centerCartographic = Cesium.Cartographic.fromCartesian(center)
|
const centerLon = Cesium.Math.toDegrees(centerCartographic.longitude)
|
const centerLat = Cesium.Math.toDegrees(centerCartographic.latitude)
|
const centerHeight = centerCartographic.height
|
|
// 估算经纬度范围
|
const deltaLon = maxExtent / (111320 * Math.cos(centerCartographic.latitude))
|
const deltaLat = maxExtent / 110540
|
|
return {
|
minLon: centerLon - deltaLon,
|
maxLon: centerLon + deltaLon,
|
minLat: centerLat - deltaLat,
|
maxLat: centerLat + deltaLat,
|
minHeight: centerHeight - maxExtent,
|
maxHeight: centerHeight + maxExtent
|
}
|
}
|
|
// ================================
|
// 从BoundingSphere获取简化边界(用于预筛选)
|
// ================================
|
getSimpleBoundsFromSphere (sphere) {
|
const center = sphere.center
|
const radius = sphere.radius
|
|
// 将球心转换为地理坐标
|
const centerCartographic = Cesium.Cartographic.fromCartesian(center)
|
const centerLon = Cesium.Math.toDegrees(centerCartographic.longitude)
|
const centerLat = Cesium.Math.toDegrees(centerCartographic.latitude)
|
const centerHeight = centerCartographic.height
|
|
// 估算经纬度范围
|
const deltaLon = radius / (111320 * Math.cos(centerCartographic.latitude))
|
const deltaLat = radius / 110540
|
|
return {
|
minLon: centerLon - deltaLon,
|
maxLon: centerLon + deltaLon,
|
minLat: centerLat - deltaLat,
|
maxLat: centerLat + deltaLat,
|
minHeight: centerHeight - radius,
|
maxHeight: centerHeight + radius
|
}
|
}
|
|
// ================================
|
// 检查网格与叶子瓦片的精确求交
|
// ================================
|
checkGridTileIntersections (gridBounds, leafTiles) {
|
for (const tile of leafTiles) {
|
if (this.isGridIntersectingTileBoundingBox(gridBounds, tile)) {
|
return true // 找到一个相交的叶子瓦片即可确定占用状态
|
}
|
}
|
return false
|
}
|
|
// ================================
|
// 检查网格是否与瓦片外包盒相交(精确计算)
|
// ================================
|
isGridIntersectingTileBoundingBox (gridBounds, tile) {
|
// 获取瓦片的边界体信息
|
let boundingVolume = null
|
|
if (tile.boundingVolume) {
|
boundingVolume = tile.boundingVolume
|
} else if (tile._boundingVolume) {
|
boundingVolume = tile._boundingVolume
|
} else if (tile.contentBoundingVolume) {
|
boundingVolume = tile.contentBoundingVolume
|
}
|
|
if (!boundingVolume) {
|
return false // 没有边界体信息
|
}
|
|
try {
|
// 获取瓦片边界框的8个顶点(轴对齐)
|
const tileVertices = this.getTileBoundingBoxVertices(boundingVolume)
|
|
if (!tileVertices) {
|
return false
|
}
|
|
// 将网格边界转换为轴对齐边界框的8个顶点
|
const gridVertices = this.getGridBoundingBoxVertices(gridBounds)
|
|
// 执行3D AABB求交检测
|
return this.checkAABBIntersection(gridVertices, tileVertices)
|
|
} catch (error) {
|
console.warn('处理瓦片边界体时出错:', error)
|
return false
|
}
|
}
|
|
// ================================
|
// 获取瓦片边界框的8个顶点(参考FlightPathTiles方法)
|
// ================================
|
getTileBoundingBoxVertices (boundingVolume) {
|
let center = null
|
let halfAxes = null
|
|
// 处理不同类型的边界体
|
if (boundingVolume instanceof Cesium.OrientedBoundingBox) {
|
center = boundingVolume.center
|
halfAxes = boundingVolume.halfAxes
|
} else if (boundingVolume.orientedBoundingBox) {
|
center = boundingVolume.orientedBoundingBox.center
|
halfAxes = boundingVolume.orientedBoundingBox.halfAxes
|
} else if (boundingVolume._orientedBoundingBox) {
|
center = boundingVolume._orientedBoundingBox.center
|
halfAxes = boundingVolume._orientedBoundingBox.halfAxes
|
} else if (boundingVolume instanceof Cesium.BoundingSphere) {
|
return this.getBoundingSphereVertices(boundingVolume)
|
} else if (boundingVolume.boundingSphere) {
|
return this.getBoundingSphereVertices(boundingVolume.boundingSphere)
|
} else if (boundingVolume._boundingSphere) {
|
return this.getBoundingSphereVertices(boundingVolume._boundingSphere)
|
}
|
|
if (!center || !halfAxes) {
|
return null
|
}
|
|
return this.calculateBoundingBoxVertices(center, halfAxes)
|
}
|
|
// ================================
|
// 计算边界框的8个顶点(轴对齐,参考FlightPathTiles方法)
|
// ================================
|
calculateBoundingBoxVertices (center, halfAxes) {
|
// 计算所有8个顶点,然后找到轴对齐的边界框
|
const tempVertices = []
|
const directions = [
|
[-1, -1, -1], [-1, -1, 1], [-1, 1, -1], [-1, 1, 1],
|
[1, -1, -1], [1, -1, 1], [1, 1, -1], [1, 1, 1]
|
]
|
|
// 提取三个轴向的向量
|
const xAxis = new Cesium.Cartesian3()
|
const yAxis = new Cesium.Cartesian3()
|
const zAxis = new Cesium.Cartesian3()
|
Cesium.Matrix3.getColumn(halfAxes, 0, xAxis)
|
Cesium.Matrix3.getColumn(halfAxes, 1, yAxis)
|
Cesium.Matrix3.getColumn(halfAxes, 2, zAxis)
|
|
// 计算原始的8个顶点
|
directions.forEach(dir => {
|
const vertex = Cesium.Cartesian3.clone(center)
|
Cesium.Cartesian3.add(vertex, Cesium.Cartesian3.multiplyByScalar(xAxis, dir[0], new Cesium.Cartesian3()), vertex)
|
Cesium.Cartesian3.add(vertex, Cesium.Cartesian3.multiplyByScalar(yAxis, dir[1], new Cesium.Cartesian3()), vertex)
|
Cesium.Cartesian3.add(vertex, Cesium.Cartesian3.multiplyByScalar(zAxis, dir[2], new Cesium.Cartesian3()), vertex)
|
tempVertices.push(vertex)
|
})
|
|
// 转换为地理坐标,找到边界范围
|
let minLon = Infinity, maxLon = -Infinity
|
let minLat = Infinity, maxLat = -Infinity
|
let minHeight = Infinity, maxHeight = -Infinity
|
|
tempVertices.forEach(vertex => {
|
const cartographic = Cesium.Cartographic.fromCartesian(vertex)
|
const lon = Cesium.Math.toDegrees(cartographic.longitude)
|
const lat = Cesium.Math.toDegrees(cartographic.latitude)
|
const height = cartographic.height
|
|
minLon = Math.min(minLon, lon)
|
maxLon = Math.max(maxLon, lon)
|
minLat = Math.min(minLat, lat)
|
maxLat = Math.max(maxLat, lat)
|
minHeight = Math.min(minHeight, height)
|
maxHeight = Math.max(maxHeight, height)
|
})
|
|
// 创建轴对齐的边界框顶点(平行于地面)
|
const vertices = [
|
// 底面4个顶点
|
Cesium.Cartesian3.fromDegrees(minLon, minLat, minHeight), // 0: 左下后
|
Cesium.Cartesian3.fromDegrees(maxLon, minLat, minHeight), // 1: 右下后
|
Cesium.Cartesian3.fromDegrees(minLon, maxLat, minHeight), // 2: 左上后
|
Cesium.Cartesian3.fromDegrees(maxLon, maxLat, minHeight), // 3: 右上后
|
// 顶面4个顶点
|
Cesium.Cartesian3.fromDegrees(minLon, minLat, maxHeight), // 4: 左下前
|
Cesium.Cartesian3.fromDegrees(maxLon, minLat, maxHeight), // 5: 右下前
|
Cesium.Cartesian3.fromDegrees(minLon, maxLat, maxHeight), // 6: 左上前
|
Cesium.Cartesian3.fromDegrees(maxLon, maxLat, maxHeight) // 7: 右上前
|
]
|
|
return vertices
|
}
|
|
// ================================
|
// 处理球体边界的情况
|
// ================================
|
getBoundingSphereVertices (sphere) {
|
const center = sphere.center
|
const radius = sphere.radius
|
|
// 将球心转换为地理坐标
|
const centerCartographic = Cesium.Cartographic.fromCartesian(center)
|
const centerLon = Cesium.Math.toDegrees(centerCartographic.longitude)
|
const centerLat = Cesium.Math.toDegrees(centerCartographic.latitude)
|
const centerHeight = centerCartographic.height
|
|
// 估算经纬度范围(简化处理)
|
const deltaLon = radius / (111320 * Math.cos(centerCartographic.latitude)) // 经度差
|
const deltaLat = radius / 110540 // 纬度差
|
|
// 创建轴对齐的边界框顶点
|
const vertices = [
|
// 底面4个顶点
|
Cesium.Cartesian3.fromDegrees(centerLon - deltaLon, centerLat - deltaLat, centerHeight - radius),
|
Cesium.Cartesian3.fromDegrees(centerLon + deltaLon, centerLat - deltaLat, centerHeight - radius),
|
Cesium.Cartesian3.fromDegrees(centerLon - deltaLon, centerLat + deltaLat, centerHeight - radius),
|
Cesium.Cartesian3.fromDegrees(centerLon + deltaLon, centerLat + deltaLat, centerHeight - radius),
|
// 顶面4个顶点
|
Cesium.Cartesian3.fromDegrees(centerLon - deltaLon, centerLat - deltaLat, centerHeight + radius),
|
Cesium.Cartesian3.fromDegrees(centerLon + deltaLon, centerLat - deltaLat, centerHeight + radius),
|
Cesium.Cartesian3.fromDegrees(centerLon - deltaLon, centerLat + deltaLat, centerHeight + radius),
|
Cesium.Cartesian3.fromDegrees(centerLon + deltaLon, centerLat + deltaLat, centerHeight + radius)
|
]
|
|
return vertices
|
}
|
|
// ================================
|
// 获取网格边界框的8个顶点
|
// ================================
|
getGridBoundingBoxVertices (gridBounds) {
|
const vertices = [
|
// 底面4个顶点
|
Cesium.Cartesian3.fromDegrees(gridBounds.minLon, gridBounds.minLat, gridBounds.minHeight),
|
Cesium.Cartesian3.fromDegrees(gridBounds.maxLon, gridBounds.minLat, gridBounds.minHeight),
|
Cesium.Cartesian3.fromDegrees(gridBounds.minLon, gridBounds.maxLat, gridBounds.minHeight),
|
Cesium.Cartesian3.fromDegrees(gridBounds.maxLon, gridBounds.maxLat, gridBounds.minHeight),
|
// 顶面4个顶点
|
Cesium.Cartesian3.fromDegrees(gridBounds.minLon, gridBounds.minLat, gridBounds.maxHeight),
|
Cesium.Cartesian3.fromDegrees(gridBounds.maxLon, gridBounds.minLat, gridBounds.maxHeight),
|
Cesium.Cartesian3.fromDegrees(gridBounds.minLon, gridBounds.maxLat, gridBounds.maxHeight),
|
Cesium.Cartesian3.fromDegrees(gridBounds.maxLon, gridBounds.maxLat, gridBounds.maxHeight)
|
]
|
|
return vertices
|
}
|
|
// ================================
|
// 检查两个轴对齐边界框(AABB)是否相交
|
// ================================
|
checkAABBIntersection (vertices1, vertices2) {
|
// 将顶点转换为地理坐标进行比较
|
const bounds1 = this.getVerticesBounds(vertices1)
|
const bounds2 = this.getVerticesBounds(vertices2)
|
|
// 3D AABB相交检测
|
const tolerance = 0.0001
|
|
const intersects = !(
|
bounds1.maxLon < bounds2.minLon - tolerance ||
|
bounds1.minLon > bounds2.maxLon + tolerance ||
|
bounds1.maxLat < bounds2.minLat - tolerance ||
|
bounds1.minLat > bounds2.maxLat + tolerance ||
|
bounds1.maxHeight < bounds2.minHeight - tolerance ||
|
bounds1.minHeight > bounds2.maxHeight + tolerance
|
)
|
|
return intersects
|
}
|
|
// ================================
|
// 从顶点计算边界范围
|
// ================================
|
getVerticesBounds (vertices) {
|
let minLon = Infinity, maxLon = -Infinity
|
let minLat = Infinity, maxLat = -Infinity
|
let minHeight = Infinity, maxHeight = -Infinity
|
|
vertices.forEach(vertex => {
|
const cartographic = Cesium.Cartographic.fromCartesian(vertex)
|
const lon = Cesium.Math.toDegrees(cartographic.longitude)
|
const lat = Cesium.Math.toDegrees(cartographic.latitude)
|
const height = cartographic.height
|
|
minLon = Math.min(minLon, lon)
|
maxLon = Math.max(maxLon, lon)
|
minLat = Math.min(minLat, lat)
|
maxLat = Math.max(maxLat, lat)
|
minHeight = Math.min(minHeight, height)
|
maxHeight = Math.max(maxHeight, height)
|
})
|
|
return {
|
minLon: minLon,
|
maxLon: maxLon,
|
minLat: minLat,
|
maxLat: maxLat,
|
minHeight: minHeight,
|
maxHeight: maxHeight
|
}
|
}
|
|
// ================================
|
// 计算网格单元的边界范围
|
// ================================
|
calculateGridBounds (gridCenter) {
|
const gridLon = Cesium.Math.toDegrees(gridCenter.longitude)
|
const gridLat = Cesium.Math.toDegrees(gridCenter.latitude)
|
const gridHeight = gridCenter.height
|
|
// 计算网格单元的边界范围
|
const earthRadius = 6371000
|
const halfSize = this.gridSize / 2
|
|
// 计算经纬度的半范围
|
const deltaLon = halfSize / (earthRadius * Math.cos(gridCenter.latitude))
|
const deltaLat = halfSize / earthRadius
|
|
return {
|
minLon: gridLon - Cesium.Math.toDegrees(deltaLon),
|
maxLon: gridLon + Cesium.Math.toDegrees(deltaLon),
|
minLat: gridLat - Cesium.Math.toDegrees(deltaLat),
|
maxLat: gridLat + Cesium.Math.toDegrees(deltaLat),
|
minHeight: gridHeight - halfSize,
|
maxHeight: gridHeight + halfSize
|
}
|
}
|
|
|
|
|
|
// ================================
|
// 创建网格单元的边界框
|
// ================================
|
createGridBoundingBox (gridCenter) {
|
// 将地理坐标转换为笛卡尔坐标
|
const centerCartesian = Cesium.Cartesian3.fromRadians(
|
gridCenter.longitude,
|
gridCenter.latitude,
|
gridCenter.height
|
)
|
|
// 创建半扩展向量
|
const halfSize = this.gridSize / 2
|
const halfExtents = new Cesium.Cartesian3(halfSize, halfSize, halfSize)
|
|
return {
|
center: centerCartesian,
|
halfExtents: halfExtents
|
}
|
}
|
|
// ================================
|
// 检查两个边界框是否相交
|
// ================================
|
checkBoundingBoxIntersection (box1, box2) {
|
// 计算两个边界框的最小和最大点
|
const box1Min = Cesium.Cartesian3.subtract(box1.center, box1.halfExtents, new Cesium.Cartesian3())
|
const box1Max = Cesium.Cartesian3.add(box1.center, box1.halfExtents, new Cesium.Cartesian3())
|
|
const box2Min = Cesium.Cartesian3.subtract(box2.center, box2.halfExtents, new Cesium.Cartesian3())
|
const box2Max = Cesium.Cartesian3.add(box2.center, box2.halfExtents, new Cesium.Cartesian3())
|
|
// 检查在三个轴上是否都有重叠
|
return (box1Min.x <= box2Max.x && box1Max.x >= box2Min.x &&
|
box1Min.y <= box2Max.y && box1Max.y >= box2Min.y &&
|
box1Min.z <= box2Max.z && box1Max.z >= box2Min.z)
|
}
|
|
// ================================
|
// 创建网格立方体 - 使用配置的颜色和尺寸
|
// ================================
|
createGridCube (gridCenter, isOccupied, customGridSize = null, gridIndex = null) {
|
// 将地理坐标转换为笛卡尔坐标
|
const position = Cesium.Cartesian3.fromRadians(
|
gridCenter.longitude,
|
gridCenter.latitude,
|
gridCenter.height
|
)
|
|
// 使用配置的颜色和透明度
|
const color = isOccupied ?
|
(this.config.occupiedColor || Cesium.Color.RED.withAlpha(this.config.occupiedAlpha)) :
|
(this.config.freeColor || Cesium.Color.GREEN.withAlpha(this.config.freeAlpha))
|
|
const outlineColor = isOccupied ?
|
this.config.occupiedOutlineColor :
|
this.config.freeOutlineColor
|
|
// 使用自定义尺寸或配置的网格尺寸
|
const gridSize = customGridSize || this.config.gridSize
|
const dimensions = new Cesium.Cartesian3(
|
this.config.gridWidth || gridSize,
|
this.config.gridHeight || gridSize,
|
this.config.gridDepth || gridSize
|
)
|
|
// 创建立方体实体
|
const entity = this.viewer.entities.add({
|
position: position,
|
show: isOccupied ? this.gridParams.showOccupancy : this.gridParams.showIdle,
|
box: {
|
dimensions: dimensions,
|
material: color,
|
outline: true,
|
outlineColor: outlineColor,
|
outlineWidth: this.config.outlineWidth
|
},
|
// 添加占用状态标记,用于统计
|
properties: {
|
isOccupied: isOccupied,
|
gridType: isOccupied ? 'occupied' : 'free',
|
createdAt: new Date().toISOString(),
|
gridIndex: gridIndex // 存储网格索引号
|
}
|
})
|
|
this.gridEntities.push(entity)
|
|
if (isOccupied && this.config.enableLogging) {
|
const indexStr = gridIndex ? `索引[${gridIndex.x},${gridIndex.y},${gridIndex.z}] ` : ''
|
console.log(`创建占用网格 ${indexStr}在位置: [${Cesium.Math.toDegrees(gridCenter.longitude).toFixed(6)}, ${Cesium.Math.toDegrees(gridCenter.latitude).toFixed(6)}, ${gridCenter.height.toFixed(1)}]`)
|
}
|
}
|
|
// ================================
|
// 清除网格
|
// ================================
|
clearGrid () {
|
// 移除所有网格实体
|
this.gridEntities.forEach(entity => {
|
this.viewer.entities.remove(entity)
|
})
|
this.gridEntities = []
|
|
// 清除瓦片边界框数据
|
this.tilesBoundingBoxes = []
|
|
console.log('已清除3D占用网格')
|
}
|
|
// ================================
|
// 切换网格显示
|
// ================================
|
toggleGridVisibility (visible) {
|
this.gridEntities.forEach(entity => {
|
entity.show = visible
|
})
|
|
console.log(`3D占用网格${visible ? '显示' : '隐藏'}`)
|
}
|
|
// ================================
|
// 获取网格统计信息 - 使用配置参数
|
// ================================
|
getGridStatistics () {
|
const totalGrids = this.gridEntities.length
|
const occupiedGrids = this.gridEntities.filter(entity =>
|
entity.properties && entity.properties.isOccupied
|
).length
|
const freeGrids = totalGrids - occupiedGrids
|
const occupancyRate = totalGrids > 0 ? Math.round((occupiedGrids / totalGrids) * 100) : 0
|
|
if (this.config.enableLogging) {
|
console.log(`网格统计: 总计${totalGrids}, 占用${occupiedGrids}, 空闲${freeGrids}, 占用率${occupancyRate}%`)
|
console.log(`网格配置: 尺寸${this.config.gridWidth}x${this.config.gridHeight}x${this.config.gridDepth}m, 扩展${this.config.heightExtension}m(垂直)/${this.config.widthExtension}m(水平)`)
|
}
|
|
return {
|
total: totalGrids,
|
occupied: occupiedGrids,
|
free: freeGrids,
|
occupancyRate: occupancyRate,
|
config: this.getConfig()
|
}
|
}
|
|
// ================================
|
// 获取占用网格数量
|
// ================================
|
getOccupiedGridCount () {
|
return this.gridEntities.filter(entity =>
|
entity.properties && entity.properties.isOccupied
|
).length
|
}
|
|
// 只显示占用
|
sheGridDisplay () {
|
this.gridEntities.forEach(entity => {
|
const isOccupied = entity.properties.isOccupied?._value
|
entity.show = isOccupied ? this.gridParams.showOccupancy : this.gridParams.showIdle
|
})
|
}
|
|
// ================================
|
// 将OrientedBoundingBox转换为轴对齐边界框
|
// ================================
|
orientedBoundingBoxToAxisAligned (obb) {
|
const center = obb.center
|
const halfAxes = obb.halfAxes
|
|
// 计算8个顶点
|
const vertices = []
|
const directions = [
|
[-1, -1, -1], [-1, -1, 1], [-1, 1, -1], [-1, 1, 1],
|
[1, -1, -1], [1, -1, 1], [1, 1, -1], [1, 1, 1]
|
]
|
|
// 提取三个轴向的向量
|
const xAxis = new Cesium.Cartesian3()
|
const yAxis = new Cesium.Cartesian3()
|
const zAxis = new Cesium.Cartesian3()
|
Cesium.Matrix3.getColumn(halfAxes, 0, xAxis)
|
Cesium.Matrix3.getColumn(halfAxes, 1, yAxis)
|
Cesium.Matrix3.getColumn(halfAxes, 2, zAxis)
|
|
directions.forEach(dir => {
|
const vertex = Cesium.Cartesian3.clone(center)
|
Cesium.Cartesian3.add(vertex, Cesium.Cartesian3.multiplyByScalar(xAxis, dir[0], new Cesium.Cartesian3()), vertex)
|
Cesium.Cartesian3.add(vertex, Cesium.Cartesian3.multiplyByScalar(yAxis, dir[1], new Cesium.Cartesian3()), vertex)
|
Cesium.Cartesian3.add(vertex, Cesium.Cartesian3.multiplyByScalar(zAxis, dir[2], new Cesium.Cartesian3()), vertex)
|
vertices.push(vertex)
|
})
|
|
// 转换为地理坐标,找到边界范围
|
let minLon = Infinity, maxLon = -Infinity
|
let minLat = Infinity, maxLat = -Infinity
|
let minHeight = Infinity, maxHeight = -Infinity
|
|
vertices.forEach(vertex => {
|
const cartographic = Cesium.Cartographic.fromCartesian(vertex)
|
const lon = Cesium.Math.toDegrees(cartographic.longitude)
|
const lat = Cesium.Math.toDegrees(cartographic.latitude)
|
const height = cartographic.height
|
|
minLon = Math.min(minLon, lon)
|
maxLon = Math.max(maxLon, lon)
|
minLat = Math.min(minLat, lat)
|
maxLat = Math.max(maxLat, lat)
|
minHeight = Math.min(minHeight, height)
|
maxHeight = Math.max(maxHeight, height)
|
})
|
|
return {
|
center: center,
|
minLon: minLon,
|
maxLon: maxLon,
|
minLat: minLat,
|
maxLat: maxLat,
|
minHeight: minHeight,
|
maxHeight: maxHeight
|
}
|
}
|
|
// ================================
|
// 将BoundingSphere转换为轴对齐边界框
|
// ================================
|
sphereToAxisAligned (sphere) {
|
const center = sphere.center
|
const radius = sphere.radius
|
|
// 将球心转换为地理坐标
|
const centerCartographic = Cesium.Cartographic.fromCartesian(center)
|
const centerLon = Cesium.Math.toDegrees(centerCartographic.longitude)
|
const centerLat = Cesium.Math.toDegrees(centerCartographic.latitude)
|
const centerHeight = centerCartographic.height
|
|
// 估算经纬度范围(简化处理)
|
const deltaLon = radius / (111320 * Math.cos(centerCartographic.latitude)) // 经度差
|
const deltaLat = radius / 110540 // 纬度差
|
|
return {
|
center: center,
|
minLon: centerLon - deltaLon,
|
maxLon: centerLon + deltaLon,
|
minLat: centerLat - deltaLat,
|
maxLat: centerLat + deltaLat,
|
minHeight: centerHeight - radius,
|
maxHeight: centerHeight + radius
|
}
|
}
|
}
|
|
export default OccupancyGrid
|