无人机管理后台前端(已迁走)
张含笑
2025-09-01 2ca94de8ede18ac07ccfd8dec7b6f6a707adde9b
src/utils/cesium/mapUtil.js
@@ -1,5 +1,6 @@
import * as Cesium from 'cesium'
import * as turf from '@turf/turf'
const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
export const getLngLatDistance = (lat1, lng1, lat2, lng2) => {
   const radLat1 = (lat1 * Math.PI) / 180.0
@@ -9,10 +10,7 @@
   let s =
      2 *
      Math.asin(
         Math.sqrt(
            Math.pow(Math.sin(a / 2), 2) +
            Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)
         )
         Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))
      )
   s = s * 6378.137 // EARTH_RADIUS;
   s = Math.round(s * 10000) / 10000
@@ -216,7 +214,7 @@
               longitude,
               latitude,
               height,
               cartesian3
               cartesian3,
            })
            // 在这里,你可以使用 height 值进行后续操作
         })
@@ -257,8 +255,7 @@
               const longitude = Cesium.Math.toDegrees(item.longitude)
               const latitude = Cesium.Math.toDegrees(item.latitude)
               let FinalHeight =
                  Number(data[index]?.['ellipsoidHeight']?.['#text']) + Number(item.height)
               let FinalHeight = Number(data[index]?.['ellipsoidHeight']?.['#text']) + Number(item.height)
               if (droneHeight) {
                  FinalHeight = Number(data[index]?.['ellipsoidHeight']?.['#text']) + droneHeight
@@ -404,30 +401,60 @@
/**
 * 飞到中心点并且所有点在可视范围
 * @param lngLatArr
 * @param viewer
 * @param rangeMultiple 范围倍数越大飞的越高默认3
 *  @param positionsData 二维数组,每项为 [lon, lat] 或 三维数组 [lon, lat, height]
 *  @param viewer Cesium.Viewer 实例
 *  @param multiple 缩放倍数-默认为4
 *  @param pitch 俯仰角-默认为-90
 */
export function flyVisual (lngLatArr, viewer, rangeMultiple = 3) {
   if (!Array.isArray(lngLatArr) || lngLatArr.length === 0) return
   const positions = lngLatArr.map(([lon, lat]) =>
      Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat))
export function flyVisual({ positionsData, viewer, multiple = 4, pitch = -90 }) {
   if (!Array.isArray(positionsData) || positionsData.length === 0) return
   const positions = positionsData.map(([lon, lat, height]) =>
      Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat), Number(height || 0))
   )
   // 计算包围盒 BoundingSphere(所有点的外接球)
   // 计算最高高度和中心点经纬度
   let maxHeight = -Infinity
   let sumLon = 0,
      sumLat = 0
   for (const [lon, lat, height] of positionsData) {
      sumLon += Number(lon)
      sumLat += Number(lat)
      maxHeight = Math.max(maxHeight, Number(height || 0))
   }
   const centerLon = sumLon / positionsData.length
   const centerLat = sumLat / positionsData.length
   const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
   viewer.camera.flyToBoundingSphere(boundingSphere, {
   // 暂时飞向 BoundingSphere
   viewer.camera.flyToBoundingSphere(Cesium.BoundingSphere.fromPoints(positions), {
      duration: 0,
      offset: new Cesium.HeadingPitchRange(0, -90, boundingSphere.radius * rangeMultiple),
      offset: {
         heading: Cesium.Math.toRadians(0),
         pitch: Cesium.Math.toRadians(pitch),
         range: boundingSphere.radius * multiple,
      },
      complete: () => {
         // 飞行完成后检查相机高度是否小于最高点
         const cameraHeight = Cesium.Cartographic.fromCartesian(viewer.camera.position).height
         if (cameraHeight < maxHeight) {
            viewer.camera.flyTo({
               destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, maxHeight + 100), // 加100米缓冲
               duration: 1.5,
               orientation: {
                  heading: viewer.camera.heading,
                  pitch: viewer.camera.pitch,
                  roll: viewer.camera.roll,
               },
            })
         }
      },
   })
}
// 获取视口地图中心点
export function getMapCenterPoint (viewer) {
export function getMapCenterPoint(viewer) {
   const centerResult = viewer.camera.pickEllipsoid(
      new Cesium.Cartesian2(
         viewer.canvas.clientWidth / 2,
         viewer.canvas.clientHeight / 2
      )
      new Cesium.Cartesian2(viewer.canvas.clientWidth / 2, viewer.canvas.clientHeight / 2)
   )
   const curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(centerResult)
   const longitude = (curPosition.longitude * 180) / Math.PI
@@ -436,7 +463,7 @@
}
// 获取屏幕四个角经纬度
export const getScreenCorner = (viewer) => {
export const getScreenCorner = viewer => {
   const camera = viewer.scene.camera
   const canvas = viewer.canvas
@@ -470,3 +497,65 @@
      lt: [leftBottomLonLat[0], rightTopLonLat[1]], // 左上角
   }
}
// areaCode转换为目录结构, 返回数组,
export function areaCodeToArr(code) {
   const codeStr = code.toString()
   const provinceCode = codeStr.slice(0, 2) + '0000'
   const cityCode = codeStr.slice(0, 4) + '00'
   if (codeStr.slice(2, 4) === '00' && codeStr.slice(4, 6) === '00') {
      return [provinceCode]
   } else if (codeStr.slice(4, 6) === '00') {
      return [provinceCode, cityCode]
   }
   return [provinceCode, cityCode, codeStr]
}
// 根据区域code获取轮廓
export const getContourByCode = async areaCode => {
   const defaultDir = `${VITE_APP_REGION_URL}/100000/`
   const hierarchy = areaCodeToArr(areaCode.slice(0, 6))
   const jsonPathPre = hierarchy.slice(0, hierarchy.length - 1).join('/')
   const res = await fetch(`${defaultDir}${jsonPathPre}/index.json`)
   const parentGJson = await res.json()
   let features = parentGJson.features.find(item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1]))
   return { type: 'FeatureCollection', features: [features] }
}
// 辅助函数:创建旋转文字的画布
export function createRotatedTextCanvas(time) {
   const prefix = '预计'
   const suffix = '分钟'
   const canvas = document.createElement('canvas')
   const context = canvas.getContext('2d')
   // 设置字体
   context.font = 'bold 16px Source Han Sans CN'
   // 测量各部分文本宽度
   const prefixWidth = context.measureText(prefix).width
   const timeWidth = context.measureText(time).width
   const suffixWidth = context.measureText(suffix).width
   const totalWidth = prefixWidth + timeWidth + suffixWidth
   const textHeight = 20 // 估算高度
   // 设置画布大小(增加边距)
   canvas.width = totalWidth + 30
   canvas.height = textHeight * 3
   // 重置上下文
   context.font = 'bold 16px Source Han Sans CN'
   context.textBaseline = 'middle'
   context.textAlign = 'left' // 改为左对齐,方便分段绘制
   // 清除画布
   context.clearRect(0, 0, canvas.width, canvas.height)
   // 绘制“预计”(白色)
   context.fillStyle = '#FFFFFF'
   context.fillText(prefix, 10, 15)
   // 绘制时间(蓝色)
   context.font = 'bold 30px Source Han Sans CN'
   context.fillStyle = '#1EE7E7' // 亮蓝色
   context.fillText(time, 10 + prefixWidth, 12)
   // 绘制“分钟”(白色)
   context.font = 'bold 16px Source Han Sans CN'
   context.fillStyle = '#FFFFFF'
   context.fillText(suffix, 20 + prefixWidth + timeWidth, 15)
   return canvas
}