forked from drone/command-center-dashboard

罗广辉
2025-04-21 2800fa4f32f3900509cb4d6eefaf2bfaf54efdd7
src/utils/cesium/mapUtil.js
@@ -1,148 +1,134 @@
import * as Cesium from 'cesium';
import * as turf from '@turf/turf';
import * as Cesium from 'cesium'
import * as turf from '@turf/turf'
export const getLngLatDistance = (lat1, lng1, lat2, lng2) => {
  const radLat1 = (lat1 * Math.PI) / 180.0
  const radLat2 = (lat2 * Math.PI) / 180.0
  const a = radLat1 - radLat2
  const b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0
  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),
      ),
    )
  s = s * 6378.137 // EARTH_RADIUS;
  s = Math.round(s * 10000) / 10000
  return s * 1000
   const radLat1 = (lat1 * Math.PI) / 180.0
   const radLat2 = (lat2 * Math.PI) / 180.0
   const a = radLat1 - radLat2
   const b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0
   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)
         )
      )
   s = s * 6378.137 // EARTH_RADIUS;
   s = Math.round(s * 10000) / 10000
   return s * 1000
}
// 获取限polyline长度
export const getPolylineLength = (entity) => {
  let length = 0
export const getPolylineLength = entity => {
   let length = 0
  // 获取Polyline的所有顶点位置
  const positions = entity.polyline.positions.getValue()
   // 获取Polyline的所有顶点位置
   const positions = entity.polyline.positions.getValue()
  for (let i = 0; i < positions.length - 1; ++i) {
    const startPosition = positions[i]
    const endPosition = positions[i + 1]
   for (let i = 0; i < positions.length - 1; ++i) {
      const startPosition = positions[i]
      const endPosition = positions[i + 1]
    // 使用Cesium提供的distanceBetween函数计算两个顶点之间的距离
    const distance = Cesium.Cartesian3.distance(startPosition, endPosition)
      // 使用Cesium提供的distanceBetween函数计算两个顶点之间的距离
      const distance = Cesium.Cartesian3.distance(startPosition, endPosition)
    // 将每个顶点之间的距离相加得到总长度
    length += distance
  }
      // 将每个顶点之间的距离相加得到总长度
      length += distance
   }
  return length
   return length
}
// 创建三角广告牌
export const createTriangleMarker = (title, color) => {
  // 创建canvas绘制广告牌
  const billboard = document.createElement('canvas')
  billboard.width = 30
  billboard.height = 30
  const ctx = billboard.getContext('2d')
  ctx.beginPath()
  ctx.moveTo(0, 0)
  ctx.lineTo(30, 0)
  ctx.lineTo(15, 22)
  ctx.fillStyle = color
  ctx.fill()
  ctx.font = '18px serif'
  ctx.fillStyle = '#ffffff'
  ctx.fillText(title, 10, 15)
  ctx.closePath()
  return billboard
   // 创建canvas绘制广告牌
   const billboard = document.createElement('canvas')
   billboard.width = 30
   billboard.height = 30
   const ctx = billboard.getContext('2d')
   ctx.beginPath()
   ctx.moveTo(0, 0)
   ctx.lineTo(30, 0)
   ctx.lineTo(15, 22)
   ctx.fillStyle = color
   ctx.fill()
   ctx.font = '18px serif'
   ctx.fillStyle = '#ffffff'
   ctx.fillText(title, 10, 15)
   ctx.closePath()
   return billboard
}
export const createCircleBillboard = (title, color) => {
  const billboard = document.createElement('canvas')
  const ctx = billboard.getContext('2d')
  ctx.beginPath()
  ctx.ellipse(150, 90, 11, 11, 0, 0, Math.PI * 2)
  ctx.fillStyle = color
  ctx.fill()
  ctx.font = 'bold 15px serif'
  ctx.textAlign = 'center'
  ctx.fillStyle = '#ffffff'
  ctx.fillText(title, 150, 95)
  ctx.closePath()
  return billboard
   const billboard = document.createElement('canvas')
   const ctx = billboard.getContext('2d')
   ctx.beginPath()
   ctx.ellipse(150, 90, 11, 11, 0, 0, Math.PI * 2)
   ctx.fillStyle = color
   ctx.fill()
   ctx.font = 'bold 15px serif'
   ctx.textAlign = 'center'
   ctx.fillStyle = '#ffffff'
   ctx.fillText(title, 150, 95)
   ctx.closePath()
   return billboard
}
// 获取当前经纬度海拔
export const getHaeHeight = (start, end) => {
  const ellipsoid = Cesium.Ellipsoid.WGS84 // 选择合适的椭圆体模型
  // const { longitude, latitude, height } = start
  // const { longitude: endLng, latitude: endLat, height: endHeight } = end
  // const startC3Position = new Cesium.Cartographic(longitude, latitude, height)
  // const endC3Position = new Cesium.Cartographic(endLng, endLat, endHeight)
  // const startCartesianPosition = ellipsoid.cartographicToCartesian(startC3Position)
  // const endCartesianPosition = ellipsoid.cartographicToCartesian(endC3Position)
  // // 计算两个位置之间的距离
  // const distance = new Cesium.EllipsoidGeodesic(startCartesianPosition, endCartesianPosition, ellipsoid)
  // return distance
  const { longitude, latitude, height } = end
  const cartographic = Cesium.Cartographic.fromDegrees(
    longitude,
    latitude,
    height,
  )
  const cartesianPosition = ellipsoid.cartographicToCartesian(cartographic)
  const haeHeight = ellipsoid.cartesianToCartographic(cartesianPosition).height
  return haeHeight
   const ellipsoid = Cesium.Ellipsoid.WGS84 // 选择合适的椭圆体模型
   // const { longitude, latitude, height } = start
   // const { longitude: endLng, latitude: endLat, height: endHeight } = end
   // const startC3Position = new Cesium.Cartographic(longitude, latitude, height)
   // const endC3Position = new Cesium.Cartographic(endLng, endLat, endHeight)
   // const startCartesianPosition = ellipsoid.cartographicToCartesian(startC3Position)
   // const endCartesianPosition = ellipsoid.cartographicToCartesian(endC3Position)
   // // 计算两个位置之间的距离
   // const distance = new Cesium.EllipsoidGeodesic(startCartesianPosition, endCartesianPosition, ellipsoid)
   // return distance
   const { longitude, latitude, height } = end
   const cartographic = Cesium.Cartographic.fromDegrees(longitude, latitude, height)
   const cartesianPosition = ellipsoid.cartographicToCartesian(cartographic)
   const haeHeight = ellipsoid.cartesianToCartographic(cartesianPosition).height
   return haeHeight
}
// Cesium Cartesian3 转正常经纬度
export const cartesian3Convert = (cartesian3Position, viewer) => {
  // 转换为经纬度
  const ellipsoid = viewer.scene.globe.ellipsoid
  const cartographicPosition = Cesium.Cartographic.fromCartesian(
    cartesian3Position,
    ellipsoid,
  )
  const longitude = Cesium.Math.toDegrees(cartographicPosition.longitude)
  const latitude = Cesium.Math.toDegrees(cartographicPosition.latitude)
  const height = cartographicPosition.height
   // 转换为经纬度
   const ellipsoid = viewer.scene.globe.ellipsoid
   const cartographicPosition = Cesium.Cartographic.fromCartesian(cartesian3Position, ellipsoid)
   const longitude = Cesium.Math.toDegrees(cartographicPosition.longitude)
   const latitude = Cesium.Math.toDegrees(cartographicPosition.latitude)
   const height = cartographicPosition.height
  //   console.log('经度: ' + longitude);
  //   console.log('纬度: ' + latitude);
  //   console.log('高度: ' + height);
   //   console.log('经度: ' + longitude);
   //   console.log('纬度: ' + latitude);
   //   console.log('高度: ' + height);
  return {
    longitude,
    latitude,
    height,
  }
   return {
      longitude,
      latitude,
      height,
   }
}
// Cesium Cartesian2 转正常经纬度
export const cartesian2Convert = (position, viewer) => {
  const ellipsoid = viewer.scene.globe.ellipsoid
  const c3Position = viewer.scene.globe.pick(
    viewer.camera.getPickRay(position),
    viewer.scene,
  )
  const c2Postion = ellipsoid.cartesianToCartographic(c3Position)
  const longitude = Cesium.Math.toDegrees(c2Postion.longitude)
  const latitude = Cesium.Math.toDegrees(c2Postion.latitude)
  return { longitude, latitude }
   const ellipsoid = viewer.scene.globe.ellipsoid
   const c3Position = viewer.scene.globe.pick(viewer.camera.getPickRay(position), viewer.scene)
   const c2Postion = ellipsoid.cartesianToCartographic(c3Position)
   const longitude = Cesium.Math.toDegrees(c2Postion.longitude)
   const latitude = Cesium.Math.toDegrees(c2Postion.latitude)
   return { longitude, latitude }
}
// 根据经纬度获取距离
export const getLnglatDist = (lng1, lat1, lng2, lat2) => {
  const start = new Cesium.Cartesian3.fromDegrees(
    Number(lng1),
    Number(lat1),
    0,
  )
  const end = new Cesium.Cartesian3.fromDegrees(Number(lng2), Number(lat2), 0)
  return Cesium.Cartesian3.distance(start, end)
   const start = new Cesium.Cartesian3.fromDegrees(Number(lng1), Number(lat1), 0)
   const end = new Cesium.Cartesian3.fromDegrees(Number(lng2), Number(lat2), 0)
   return Cesium.Cartesian3.distance(start, end)
}
/**
@@ -150,37 +136,37 @@
 * @param coordinateList {Array<Array<Object>>} [[{lat, lng}]]
 * @return { Object } {lat lng}
 */
export const getCenterPoint = (coordinateList) => {
  coordinateList = coordinateList.map((item) => {
    const [lng, lat] = item
    return [{ lat, lng }]
  })
  const geoCoordinateListFlat = coordinateList.reduce((s, v) => {
    return (s = s.concat(v))
  }, [])
  const total = geoCoordinateListFlat.length
  let X = 0
  let Y = 0
  let Z = 0
  for (const g of geoCoordinateListFlat) {
    const lat = (g.lat * Math.PI) / 180
    const lon = (g.lng * Math.PI) / 180
    const x = Math.cos(lat) * Math.cos(lon)
    const y = Math.cos(lat) * Math.sin(lon)
    const z = Math.sin(lat)
    X += x
    Y += y
    Z += z
  }
export const getCenterPoint = coordinateList => {
   coordinateList = coordinateList.map(item => {
      const [lng, lat] = item
      return [{ lat, lng }]
   })
   const geoCoordinateListFlat = coordinateList.reduce((s, v) => {
      return (s = s.concat(v))
   }, [])
   const total = geoCoordinateListFlat.length
   let X = 0
   let Y = 0
   let Z = 0
   for (const g of geoCoordinateListFlat) {
      const lat = (g.lat * Math.PI) / 180
      const lon = (g.lng * Math.PI) / 180
      const x = Math.cos(lat) * Math.cos(lon)
      const y = Math.cos(lat) * Math.sin(lon)
      const z = Math.sin(lat)
      X += x
      Y += y
      Z += z
   }
  X = X / total
  Y = Y / total
  Z = Z / total
  const Lon = Math.atan2(Y, X)
  const Hyp = Math.sqrt(X * X + Y * Y)
  const Lat = Math.atan2(Z, Hyp)
   X = X / total
   Y = Y / total
   Z = Z / total
   const Lon = Math.atan2(Y, X)
   const Hyp = Math.sqrt(X * X + Y * Y)
   const Lat = Math.atan2(Z, Hyp)
  return { lng: (Lon * 180) / Math.PI, lat: (Lat * 180) / Math.PI }
   return { lng: (Lon * 180) / Math.PI, lat: (Lat * 180) / Math.PI }
}
/**
@@ -190,183 +176,207 @@
 * @return {*} { index: 1 }
 */
export const getShortestDistance = (lnglat1, lnglat2) => {
  const arr = []
  lnglat1.forEach((v, i) => {
    const [lng, lat] = v
    lnglat2.forEach((s, i2) => {
      const [vLng, vLat] = s
      const distance = getLnglatDist(lng, lat, vLng, vLat)
      arr.push({
        distance,
        index: [i, i2],
        lnglat: [v, s],
      })
    })
  })
  const newArr = arr.sort((a, b) => a.distance - b.distance)
  return newArr[0]
   const arr = []
   lnglat1.forEach((v, i) => {
      const [lng, lat] = v
      lnglat2.forEach((s, i2) => {
         const [vLng, vLat] = s
         const distance = getLnglatDist(lng, lat, vLng, vLat)
         arr.push({
            distance,
            index: [i, i2],
            lnglat: [v, s],
         })
      })
   })
   const newArr = arr.sort((a, b) => a.distance - b.distance)
   return newArr[0]
}
// 获取当前经纬度地形数据
export const getLnglatAltitude = (longitude, latitude, viewer) => {
  return new Promise((resolve, reject) => {
    // 假设 viewer 已经初始化并且 terrainProvider 是有效的
    const terrainProvider = viewer?.terrainProvider
   return new Promise((resolve, reject) => {
      // 假设 viewer 已经初始化并且 terrainProvider 是有效的
      const terrainProvider = viewer?.terrainProvider
    // 创建 Cartographic 对象
    const cartographic = Cesium.Cartographic.fromDegrees(longitude, latitude)
      // 创建 Cartographic 对象
      const cartographic = Cesium.Cartographic.fromDegrees(longitude, latitude)
    // 获取地形数据的Promise
    const promise = Cesium.sampleTerrainMostDetailed(terrainProvider, [
      cartographic,
    ])
      // 获取地形数据的Promise
      const promise = Cesium.sampleTerrainMostDetailed(terrainProvider, [cartographic])
    // 使用 Cesium.when 处理 Promise
    promise
      .then(function (updatedPositions) {
        // updatedPositions 是一个数组,包含更新后的 Cartographic 对象
        const updatedPosition = updatedPositions[0]
        const height = updatedPosition.height
      // 使用 Cesium.when 处理 Promise
      promise
         .then(function (updatedPositions) {
            // updatedPositions 是一个数组,包含更新后的 Cartographic 对象
            const updatedPosition = updatedPositions[0]
            const height = updatedPosition.height
        resolve({
          longitude,
          latitude,
          height,
        })
        // 在这里,你可以使用 height 值进行后续操作
      })
      .catch(function (error) {
        // console.error('获取高程时发生错误:', error);
        reject(error)
      })
  })
            resolve({
               longitude,
               latitude,
               height,
            })
            // 在这里,你可以使用 height 值进行后续操作
         })
         .catch(function (error) {
            // console.error('获取高程时发生错误:', error);
            reject(error)
         })
   })
}
// 批量获取经纬度对应高度信息
export const getPositionsHeight = (data, viewer, droneHeight = null) => {
  return new Promise((resolve, reject) => {
    if (!data || !data.length) {
      resolve([])
   return new Promise((resolve, reject) => {
      if (!data || !data.length) {
         resolve([])
      return
    }
         return
      }
    // 假设 viewer 已经初始化并且 terrainProvider 是有效的
    const terrainProvider = viewer?.terrainProvider
      // 假设 viewer 已经初始化并且 terrainProvider 是有效的
      const terrainProvider = viewer?.terrainProvider
    // 创建 Cartographic 对象
    const cartographics = data.map((item) => {
      const [lng, lat] = item?.['Point']?.['coordinates']?.['#text'].split(',')
      // 创建 Cartographic 对象
      const cartographics = data.map(item => {
         const [lng, lat] = item?.['Point']?.['coordinates']?.['#text'].split(',')
      return Cesium.Cartographic.fromDegrees(Number(lng), Number(lat))
    })
         return Cesium.Cartographic.fromDegrees(Number(lng), Number(lat))
      })
    // 获取地形数据的Promise
    const promise = Cesium.sampleTerrainMostDetailed(
      terrainProvider,
      cartographics,
    )
      // 获取地形数据的Promise
      const promise = Cesium.sampleTerrainMostDetailed(terrainProvider, cartographics)
    // 使用 Cesium.when 处理 Promise
    promise
      .then(function (updatedPositions) {
        // updatedPositions 是一个数组,包含更新后的 Cartographic 对象
        const newPosition = updatedPositions.map((item, index) => {
          const longitude = Cesium.Math.toDegrees(item.longitude)
          const latitude = Cesium.Math.toDegrees(item.latitude)
      // 使用 Cesium.when 处理 Promise
      promise
         .then(function (updatedPositions) {
            // updatedPositions 是一个数组,包含更新后的 Cartographic 对象
            const newPosition = updatedPositions.map((item, index) => {
               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
          }
               if (droneHeight) {
                  FinalHeight = Number(data[index]?.['ellipsoidHeight']?.['#text']) + droneHeight
               }
          return {
            ...data[index],
            longitude,
            latitude,
            FinalHeight,
            customHeight: Number(item.height),
          }
        })
               return {
                  ...data[index],
                  longitude,
                  latitude,
                  FinalHeight,
                  customHeight: Number(item.height),
               }
            })
        resolve(newPosition)
        // 在这里,你可以使用 height 值进行后续操作
      })
      .catch(function (error) {
        // console.error('获取高程时发生错误:', error);
        reject(error)
      })
  })
            resolve(newPosition)
            // 在这里,你可以使用 height 值进行后续操作
         })
         .catch(function (error) {
            // console.error('获取高程时发生错误:', error);
            reject(error)
         })
   })
}
// 获取面中最高点
export const getPolygonMaxHeight = (turfPolygon, viewer) => {
  return new Promise((resolve, reject) => {
    const turfExtent = turf.bbox(turfPolygon)
   return new Promise((resolve, reject) => {
      const turfExtent = turf.bbox(turfPolygon)
    const turfSamplePoints = turf.pointGrid(turfExtent, 0.01, {
      units: 'kilometers',
      mask: turfPolygon,
    })
      const turfSamplePoints = turf.pointGrid(turfExtent, 0.01, {
         units: 'kilometers',
         mask: turfPolygon,
      })
    // 假设 viewer 已经初始化并且 terrainProvider 是有效的
    const terrainProvider = viewer?.terrainProvider
      // 假设 viewer 已经初始化并且 terrainProvider 是有效的
      const terrainProvider = viewer?.terrainProvider
    const cesiumSamplePoints = []
    for (let i = 0; i < turfSamplePoints.features.length; i++) {
      const coord = turfSamplePoints.features[i].geometry.coordinates
      cesiumSamplePoints.push(
        Cesium.Cartographic.fromDegrees(coord[0], coord[1]),
      )
    }
      const cesiumSamplePoints = []
      for (let i = 0; i < turfSamplePoints.features.length; i++) {
         const coord = turfSamplePoints.features[i].geometry.coordinates
         cesiumSamplePoints.push(Cesium.Cartographic.fromDegrees(coord[0], coord[1]))
      }
    // 获取地形数据的Promise
    const promise = Cesium.sampleTerrainMostDetailed(
      terrainProvider,
      cesiumSamplePoints,
    )
      // 获取地形数据的Promise
      const promise = Cesium.sampleTerrainMostDetailed(terrainProvider, cesiumSamplePoints)
    promise
      .then(function (updatedPositions) {
        const newPosition = updatedPositions.map((item) => Number(item.height))
      promise
         .then(function (updatedPositions) {
            const newPosition = updatedPositions.map(item => Number(item.height))
        resolve(Math.max(...newPosition))
      })
      .catch(function (error) {
        reject(error)
      })
  })
            resolve(Math.max(...newPosition))
         })
         .catch(function (error) {
            reject(error)
         })
   })
}
// 展示当前的高度
export const getWaylineShowHeight = (positions) => {
  const { lng, lat } = getCenterPoint(positions)
  let maxDist = 0
  positions.forEach((item) => {
    const [lng1, lat1] = item
    let pointsDist = getLnglatDist(lng1, lat1, lng, lat)
    if (pointsDist > maxDist) {
      maxDist = pointsDist
    }
  })
  return {
    lng,
    lat,
    showHeight: maxDist * 8,
  }
export const getWaylineShowHeight = positions => {
   const { lng, lat } = getCenterPoint(positions)
   let maxDist = 0
   positions.forEach(item => {
      const [lng1, lat1] = item
      let pointsDist = getLnglatDist(lng1, lat1, lng, lat)
      if (pointsDist > maxDist) {
         maxDist = pointsDist
      }
   })
   return {
      lng,
      lat,
      showHeight: maxDist * 8,
   }
}
// 获取当前面是否有交叉点
export const isIntersection = (coordinates) => {
  const polyDikuai = turf.polygon([coordinates])
  const result = turf.kinks(polyDikuai)
  const resultFeatures = result.features
  if (resultFeatures.length == 0) {
    //无相交
    return false
  } else {
    //有相交
    return true
  }
export const isIntersection = coordinates => {
   const polyDikuai = turf.polygon([coordinates])
   const result = turf.kinks(polyDikuai)
   const resultFeatures = result.features
   if (resultFeatures.length == 0) {
      //无相交
      return false
   } else {
      //有相交
      return true
   }
}
/**
 * 飞到中心点并且所有点在可视范围
 * @param lngLatArr
 * @param viewer
 * @param rangeMultiple 范围倍数越大飞的越高默认2
 */
export function flyVisual(lngLatArr,viewer,rangeMultiple = 2) {
   if (!Array.isArray(lngLatArr) || lngLatArr.length === 0) return
   const positions = lngLatArr.map(([lon, lat]) =>
      Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat))
   )
   // 计算包围盒 BoundingSphere(所有点的外接球)
   const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
   viewer.camera.flyToBoundingSphere(boundingSphere, {
      duration: 0,
      offset: new Cesium.HeadingPitchRange(0, 0, boundingSphere.radius * rangeMultiple),
   })
}
// 获取视口地图中心点
export function getMapCenterPoint(viewer) {
   const centerResult = viewer.camera.pickEllipsoid(
      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;
   const latitude = (curPosition.latitude * 180) / Math.PI;
   return { longitude, latitude };
}