吉安感知网项目-前端
罗广辉
2026-08-13 1d552a3bad95caae4af15322df28e6240b797693
applications/drone-command/src/utils/cesium/publicCesium.js
@@ -2,6 +2,7 @@
import * as Cesium from 'cesium'
import { Cartesian3, Terrain, Viewer } from 'cesium'
import store from '@/store/index'
import jaGeojsonRaw from '@/assets/geojson/ja.geojson?raw'
import getBaseConfig from '@/buildConfig/config'
@@ -19,7 +20,6 @@
} = getBaseConfig()
import { addBlueFilter } from '@/utils/cesium/common'
import { useBoundary } from '@/utils/cesium/useBoundary'
let smallMpaCenterDistance = null
let curCameraHpr = null
@@ -32,6 +32,36 @@
/**
 * 公共的Cesium (蓝色,2d)
 */
const DEFAULT_VIEW_RECTANGLE = (() => {
   try {
      const geojson = JSON.parse(jaGeojsonRaw)
      let west = Infinity
      let east = -Infinity
      let south = Infinity
      let north = -Infinity
      const walk = coords => {
         if (!Array.isArray(coords)) return
         if (typeof coords[0] === 'number' && typeof coords[1] === 'number') {
            const lng = coords[0]
            const lat = coords[1]
            if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
            west = Math.min(west, lng)
            east = Math.max(east, lng)
            south = Math.min(south, lat)
            north = Math.max(north, lat)
            return
         }
         coords.forEach(walk)
      }
      geojson?.features?.forEach(feature => walk(feature?.geometry?.coordinates))
      if ([west, east, south, north].every(Number.isFinite)) {
         return Cesium.Rectangle.fromDegrees(west, south, east, north)
      }
   } catch (error) {}
   return Cesium.Rectangle.fromDegrees(66, 4, 135, 53.55)
})()
// 封装的不用管
let cn = {
@@ -70,6 +100,7 @@
 * @param {boolean} [options.flyToContour=false] - 是否飞行到轮廓,默认为 false
 * @param {number} [options.multiple=1.4] - 范围倍数
 * @param {number} [options.dockOptions={}] - 机巢显示
 * @param {array} [options.terrainLoadCallback] - 地形加载回调
 * @param {array} [options.boundaryChange] - 边界改变事件
 * @param {array} [options.boundaryColor] - 边界颜色
 * @param {array} [options.dockRangeType] - 机巢范围类型 1显示覆盖圆;2显示覆盖圈
@@ -82,8 +113,10 @@
      this.handlerRightEvents = { handler: null }
      this.handlerMouseMoveEvents = { handler: null }
      this.viewer = null
      this.terrainProvider = null
      this.terrainLoading = null
      this.terrainEnabled = false
      this.globalBaseMapLayers = []
      this.boundary = null
      this.init(options)
   }
@@ -92,21 +125,14 @@
         dom,
         flatMode = true,
         terrain = false,
         layerMode = 0,
         layerMode = 17,
         contour = true,
         flyToContour = false,
         multiple = 1.4,
         dockOptions = {},
         boundaryColor = '#7FFFD4',
         boundaryChange,
         dockRangeType = 1,
         useDockHeight = false
         terrainLoadCallback,
      } = options
      Cesium.Camera.DEFAULT_VIEW_FACTOR = -0.45
      // 西南东北,默认显示中国
      Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(66, 4, 135, 53.55)
      Cesium.Camera.DEFAULT_VIEW_RECTANGLE = DEFAULT_VIEW_RECTANGLE
      const viewerOptions = {
         infoBox: false, // 禁用沙箱,解决控制台报错
         animation: false, // 左下角的动画仪表盘
@@ -121,6 +147,10 @@
         fullscreenButton: false,
      }
      // 尝试解决出现多个id相同的dom
      const domIdList = document.querySelectorAll(`#${dom}`)
      if (domIdList.length>1) domIdList[1].remove()
      this.viewer = new Viewer(dom, viewerOptions)
      this.viewerDom = dom
@@ -128,115 +158,113 @@
      this.viewer.shadows = false
      this.viewer.shadowMap.darkness = 0.5
      let time = new Date()
      time.setHours(12, 0, 0, 0)
      let utc = Cesium.JulianDate.fromDate(new Date(time))
      this.setFixedNoonLighting()
      this.viewer.clockViewModel.currentTime = Cesium.JulianDate.addHours(utc, 0, new Cesium.JulianDate())
      // 边界,机巢
      this.boundary = useBoundary(this.viewer, { multiple, dockOptions, boundaryChange, boundaryColor, dockRangeType, useDockHeight })
      this.viewer?.imageryLayers.removeAll()
      this.viewer._cesiumWidget._creditContainer.style.display = 'none'
      handler = new Cesium.ScreenSpaceEventHandler(this.viewer?.scene.canvas)
      this.viewer?.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK) // 禁用双击
      if (terrain) {
         // 正则:第一位非0,第二位任意数字,后10位都是0
         const re = /^[1-9]\d0{10}$/
         let result = null
         // 1️⃣先判断 areaCode
         if (re.test(store.state.user.userInfo.detail.areaCode)) {
            result = store.state.user.userInfo.detail.areaCode.slice(0, 6)
         } else {
            // 2️⃣如果不满足,从 ancestors 里找
            const matchedItems = store.state.user.userInfo.detail.ancestors
               .split(',')
               .filter(item => re.test(item)) // 过滤出符合条件的
            // 取第一个符合条件的前6位(可以改成 last one 看需求)
            if (matchedItems.length > 0) {
               result = matchedItems[0].slice(0, 6)
            }
         }
         try {
            Cesium.CesiumTerrainProvider.fromUrl(`${import.meta.env.VITE_APP_TERRAIN_URL}${result}`, {
               requestVertexNormals: true, // 启用地形法线增强立体感
               requestWaterMask: true, // 启用水体遮罩效果
            }).then(terrainProvider => {
               this.viewer.terrainProvider = terrainProvider
            })
         } catch (error) {
            console.error('地形加载失败:', error)
         }
      }
      this.setTerrainVisible(terrain, terrainLoadCallback)
      this.viewer.scene.screenSpaceCameraController.maximumZoomDistance = 4500000
      this.switchLayers(layerMode)
      this.switchFlatMode(flatMode)
      this.switchContour(contour)
      flyToContour && this.flyToContour(contour)
      if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
         let dpr = window.devicePixelRatio
         while (dpr >= 2.0) dpr /= 2.0 // 避免过高缩放导致模糊
         this.viewer.resolutionScale = dpr // 设置分辨率缩放比例
      }
   }
   getViewer () {
      return this.viewer
   }
   async ensureTerrainProvider () {
      if (this.terrainProvider) return this.terrainProvider
      if (this.terrainLoading) return this.terrainLoading
      this.terrainLoading = Cesium.CesiumTerrainProvider.fromUrl(
         `${import.meta.env.VITE_APP_TERRAIN_URL}/360000`,
         {
            requestVertexNormals: true, // 启用地形法线增强立体感
            requestWaterMask: true, // 启用水体遮罩效果
         }
      ).then(provider => {
         this.terrainProvider = provider
         return provider
      })
      return this.terrainLoading
   }
   async setTerrainVisible (visible, terrainLoadCallback) {
      if (!this.viewer) return
      if (!visible) {
         this.viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider()
         this.terrainEnabled = false
         return
      }
      try {
         const provider = await this.ensureTerrainProvider()
         if (!this.viewer) return
         this.viewer.terrainProvider = provider
         this.terrainEnabled = true
         terrainLoadCallback?.()
      } catch (error) {
         this.terrainEnabled = false
      }
   }
   // 销毁viewer
   viewerDestroy (removeDom = true) {
      if (!this.viewer) return
      // // 👇 关键:先停止所有动态行为
      // this.viewer.clock.shouldAnimate = false;
      // this.viewer.scene.requestRenderMode = false;
      // // 取消所有可能的 pending 渲染
      // if (this.viewer.cesiumWidget) {
      //    this.viewer.cesiumWidget.render = () => {}; // 空函数覆盖
      // }
      // // 清理实体和图层
      // this.viewer?.entities.removeAll();
      // this.viewer?.imageryLayers.removeAll();
      // // 清理数据源
      // try {
      //    const dataSources = this.viewer.dataSources;
      //    while (dataSources.length > 0) {
      //       dataSources.remove(dataSources.get(0));
      //    }
      // } catch (e) {
      //    console.warn('清理数据源时出错:', e);
      // }
      // // 清理场景中的图元
      // try {
      //    this.viewer?.scene.primitives.removeAll();
      // } catch (e) {
      //    console.warn('清理图元时出错:', e);
      // }
      // // 获取webgl上下文
      // let gl = this.viewer?.scene.context._originalGLContext
      // if (gl) {
      //    try {
      //       gl.canvas.width = 1
      //       gl.canvas.height = 1
      //       gl.getExtension("WEBGL_lose_context").loseContext()
      //    } catch (e) {
      //       console.warn('释放WebGL上下文时出错:', e)
      //    }
      //    gl = null
      // }
      this.viewer?.destroy() // 销毁Viewer实例
      this.viewer = null
      smallMpaCenterDistance = null
      if (this.viewer) {
         this.viewer?.entities.removeAll()
         this.viewer?.imageryLayers.removeAll()
         this.viewer?.dataSources.removeAll()
         // try {
         //    this.viewer?.scene?.primitives?.removeAll()
         //    this.viewer?.entities?.removeAll()
         // } catch (e) {
         //    console.warn('清理资源时出错:', e)
         //    console.log(this.viewer, 1111111111111)
         // }
         // 获取webgl上下文
         let gl = this.viewer?.scene.context._originalGLContext
         gl.canvas.width = 1
         gl.canvas.height = 1
         gl.getExtension("WEBGL_lose_context").loseContext()
         gl = null
         this.viewer?.destroy() // 销毁Viewer实例
         this.viewer = null
         var cesiumContainer = document.getElementById(this.viewerDom)
         if (cesiumContainer && removeDom) {
            cesiumContainer.remove() // 移除与地图相关的DOM元素
            this.viewerDom = null
         }
      var cesiumContainer = document.getElementById(this.viewerDom)
      if (cesiumContainer && removeDom) {
         cesiumContainer.remove() // 移除与地图相关的DOM元素
         this.viewerDom = null
      }
   }
   setShowDock (sns) {
      this.boundary?.setShowDock(sns)
   }
   // 切换轮廓显示
   async switchContour (open) {
      if (open) {
         await this.boundary?.openContour()
      } else {
         this.boundary?.closeContour()
      }
   }
   // // 飞向轮廓居中
   flyToContour () {
      this.boundary?.flyToBoundary()
   }
   // 飞行 flyto
@@ -257,7 +285,7 @@
      this.globalBaseMapLayers?.forEach(item => {
         if (item.mapLayer) item.mapLayer.show = false
      })
      // store.state.common.mapSetting.mode = mode
      store.state.common.mapSetting.mode = mode
      // 标准地图(天地图矢量)加载
      if (mode === 0) {
         mapLayers.push(
@@ -324,9 +352,7 @@
         this.viewer?.imageryLayers.lowerToBottom(find.mapLayer)
      })
   }
   // 设置或重置滤镜
   setCurrentLayerFilter (data, mode) {
      if (mode === 17) {
         data.mapLayer.brightness = 0.6 // 亮度(默认值)
@@ -843,4 +869,25 @@
         this.viewer?.imageryLayers.remove(imageryLayers)
      }
   }
   setFixedNoonLighting (utcOffsetHours = 8) {
      if (!this.viewer) return
      const now = new Date()
      const utcNoon = Date.UTC(
         now.getUTCFullYear(),
         now.getUTCMonth(),
         now.getUTCDate(),
         12 - Number(utcOffsetHours || 0),
         0,
         0,
         0
      )
      const julian = Cesium.JulianDate.fromDate(new Date(utcNoon))
      this.viewer.clock.currentTime = julian
      this.viewer.clockViewModel.currentTime = julian
      this.viewer.clock.shouldAnimate = false
      this.viewer.clock.multiplier = 0
      this.viewer.clockViewModel.multiplier = 0
      this.viewer.scene.requestRender()
   }
}