GuLiMmo
2024-03-07 a51171f691ec516929fbb29356ad9074d0439b6e
update: 右键新增航线、事件编辑、kmz文件编辑
6 files modified
347 ■■■■ changed files
src/pages/page-web/projects/components/route-edit/components/setting.vue 6 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/components/route-edit/index.vue 212 ●●●●● patch | view | raw | blame | history
src/utils/cesium/kmz.ts 16 ●●●● patch | view | raw | blame | history
src/utils/cesium/mapUtils.ts 31 ●●●●● patch | view | raw | blame | history
src/utils/cesium/use-kmz-tsa.ts 73 ●●●● patch | view | raw | blame | history
src/utils/cesium/use-map-draw.ts 9 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/components/route-edit/components/setting.vue
@@ -86,7 +86,7 @@
                  <li @click="numberControls(true, 100, 'globalHeight')">+100</li>
                  <li @click="numberControls(true, 10, 'globalHeight')">+10</li>
                  <li>
                    <span>{{ Number(folderSetting.globalHeight) }}</span> m
                    <span>{{ Math.round(folderSetting.globalHeight) }}</span> m
                  </li>
                  <li @click="numberControls(false, 100, 'globalHeight')">-100</li>
                  <li @click="numberControls(false, 10, 'globalHeight')">-10</li>
@@ -124,6 +124,7 @@
import useMapDraw from '/@/utils/cesium/use-map-draw'
import { getKmlParams } from '/@/utils/cesium/kmz'
import { cesiumOperation } from '/@/hooks/use-cesium-tsa'
import { message } from 'ant-design-vue'
import type { missionConfig, payloadParam as payloadParamConfig, Folder } from '/@/utils/cesium/types/kml'
const { appContext }: any = getCurrentInstance()
const global = appContext.config.globalProperties
@@ -227,6 +228,9 @@
    if (missionConfigSetting[key]) {
      missionConfigSetting[key] -= computeVal
    } else {
      if (folderSetting[key] - computeVal <= 0) {
        return message.warn('飞行高度较低!!!!')
      }
      folderSetting[key] -= computeVal
    }
  }
src/pages/page-web/projects/components/route-edit/index.vue
@@ -7,17 +7,14 @@
        </template>
        返回
      </a-button>
      <div class="save-button">
      <div class="save-button" @click="saveWaylineFile">
        <SaveOutlined />
      </div>
      <setting v-model:loadCompleted="loadCompleted">
        <template #show>
          <a-button type="text" class="setting-btn">
            <div class="router-setting">
              <img
                class="wayline-icon"
                :src="getResource('wayline.png')"
                alt="icon" />
              <img class="wayline-icon" :src="getResource('wayline.png')" alt="icon" />
              <span class="text">航线设置</span>
              <CaretDownOutlined />
            </div>
@@ -26,10 +23,7 @@
      </setting>
    </div>
    <div class="wayline-info">
      <div
        class="info-box"
        v-for="(item, index) in waylineDetails"
        :key="index">
      <div class="info-box" v-for="(item, index) in waylineDetails" :key="index">
        <div class="title">{{ item.title }}</div>
        <div class="info">{{ item.value }}</div>
      </div>
@@ -45,10 +39,7 @@
          <div class="right">{{ index + 1 }}</div>
        </div>
        <div class="graph-right">
          <div
            v-for="(event, index) in item.eventList"
            :key="index"
            class="s-event-icon">
          <div v-for="(event, index) in item.eventList" :key="index" class="s-event-icon">
            <a-tooltip placement="top">
              <template #title>
                {{ event.name }}
@@ -66,24 +57,23 @@
import _ from 'lodash'
import * as Cesium from 'cesium'
import setting from './components/setting.vue'
import useKmzTsa from '/@/utils/cesium/use-kmz-tsa'
import useKmzTsa, { kmlStr } from '/@/utils/cesium/use-kmz-tsa'
import { ref, reactive, defineEmits, defineProps, watch, onMounted } from 'vue'
import { ArrowLeftOutlined, CaretDownOutlined, SaveOutlined } from '@ant-design/icons-vue'
import { cesiumOperation } from '/@/hooks/use-cesium-tsa'
import { useMyStore } from '/@/store'
import ImageTrailMaterial from '/@/utils/cesium/ImageTrailMaterial'
import useMapDraw from '/@/utils/cesium/use-map-draw'
import useMapDraw, { kmlEntities as globalEntities } from '/@/utils/cesium/use-map-draw'
import { getKmlParams } from '/@/utils/cesium/kmz'
import { getHaeHeight } from '/@/utils/cesium/mapUtils'
const store = useMyStore()
const { appContext }: any = getCurrentInstance()
const global = appContext.config.globalProperties
const {
  removeAllPoint,
  getEntityById,
  addPolyline,
  addRightClick,
  removeRightClickEvent,
} = cesiumOperation()
const kmzUtils = useKmzTsa()
const { removeAllPoint, getEntityById, addPolyline, removeAllDataSource, addRightClick, removeRightClickEvent } =
  cesiumOperation()
interface waylineDetails {
  title: string
@@ -116,15 +106,12 @@
const tragetPointArr = ref<tragetPoint[]>([])
let kmlDataSource:
  | { entities: { values: { _children: any }[] }; show: boolean }
  | null
  | any = null
let kmlDataSource: { entities: { values: { _children: any }[] }; show: boolean } | null | any = null
let mouseRightClickEvent: any = null
// 绘制地图上的东西
let mapDraw = null
let mapDraw: any = null
// 航线详情
const waylineDetails = ref<waylineDetails[]>([])
// 样式
@@ -141,31 +128,29 @@
  if (kmlDataSource) {
    global.$viewer.dataSources.remove(kmlDataSource)
  }
  global.$viewer.dataSources
    .add(Cesium.KmlDataSource.load(filePath.value, options))
    .then((res: any) => {
      kmlDataSource = res
      mapDraw = useMapDraw(kmlDataSource, [], filePath.value, global)
      const {
        drawWayline,
        waylineDetails: details,
        waylinePointsEvent,
        kmlEntities: entities,
        clearWaylineData
      } = mapDraw
      clearWaylineData()
      drawWayline()
      useKmzTsa().init(filePath.value).then(kmlRes => {
        loadCompleted.value = true
      })
      waylineDetails.value = details
      tragetPointArr.value = waylinePointsEvent.value
      kmlEntities.value = entities.value
      if (!mouseRightClickEvent) {
        // 添加右键事件
        addMapPointEvent()
      }
  global.$viewer.dataSources.add(Cesium.KmlDataSource.load(filePath.value, options)).then((res: any) => {
    kmlDataSource = res
    mapDraw = useMapDraw(kmlDataSource, [], filePath.value, global)
    const {
      drawWayline,
      waylineDetails: details,
      waylinePointsEvent,
      kmlEntities: entities,
      clearWaylineData,
    } = mapDraw
    clearWaylineData()
    drawWayline()
    kmzUtils.init(filePath.value).then((kmlRes) => {
      loadCompleted.value = true
    })
    waylineDetails.value = details
    tragetPointArr.value = waylinePointsEvent.value
    kmlEntities.value = entities.value
    if (!mouseRightClickEvent) {
      // 添加右键事件
      addMapPointEvent()
    }
  })
}
// const createPointOrPolyline = () => {}
@@ -219,12 +204,7 @@
  const nextPoint = points[index + 1]?.position
  if (prevPoint) {
    // 获取中心点
    const centerPoint = Cesium.Cartesian3.lerp(
      currentPoint,
      prevPoint,
      0.5,
      new Cesium.Cartesian3(),
    )
    const centerPoint = Cesium.Cartesian3.lerp(currentPoint, prevPoint, 0.5, new Cesium.Cartesian3())
    // 获取两个点之间的距离
    let distance = Cesium.Cartesian3.distance(currentPoint, prevPoint)
    distance = Math.round(distance)
@@ -232,12 +212,7 @@
  }
  if (nextPoint) {
    // 获取中心点
    const centerPoint = Cesium.Cartesian3.lerp(
      currentPoint,
      nextPoint,
      0.5,
      new Cesium.Cartesian3(),
    )
    const centerPoint = Cesium.Cartesian3.lerp(currentPoint, nextPoint, 0.5, new Cesium.Cartesian3())
    // 获取两个点之间的距离
    let distance = Cesium.Cartesian3.distance(currentPoint, nextPoint)
    distance = Math.round(distance)
@@ -274,13 +249,8 @@
        title: `在${Number(selectPointIndex.value) + 1}号航点前插入`,
      },
    ]
    const defaultEvents = [
      { class: 'finally-point', title: '在最后航点新增航点' },
    ]
    const events =
      selectPointIndex.value !== null
        ? [...defaultEvents, ...pointEvents]
        : defaultEvents
    const defaultEvents = [{ class: 'finally-point', title: '在最后航点新增航点' }]
    const events = selectPointIndex.value !== null ? [...defaultEvents, ...pointEvents] : defaultEvents
    popupDom = createPointPopupDom(events)
    global.$viewer.container.appendChild(popupDom)
    popupDom.style.transform = `translate3d(${x}px, ${y}px, 0)`
@@ -330,84 +300,53 @@
  `
  return pointPopup
}
// 给DOM添加点击事件
const addMenuEvent = (dom: HTMLElement, position: Cesium.Cartesian2) => {
  dom.addEventListener('click', (e: any) => {
    removeCesiumChildDom(popupDom)
    const className = e.target.className
    const c3Position = global.$viewer.scene.globe.pick(
      global.$viewer.camera.getPickRay(position),
      global.$viewer.scene,
    )
    const c3Position = global.$viewer.scene.globe.pick(global.$viewer.camera.getPickRay(position), global.$viewer.scene)
    const c2Postion = ellipsoid.cartesianToCartographic(c3Position)
    const longitude = Cesium.Math.toDegrees(c2Postion.longitude)
    const latitude = Cesium.Math.toDegrees(c2Postion.latitude)
    const height = 70
    const createPointPosition = Cesium.Cartesian3.fromDegrees(
      longitude,
      latitude,
    // 获取全局高度
    const globalHeight: any = getKmlParams(kmlStr.value, true, {
      name: 'globalHeight',
      findRegx: '([\\s\\S]*?)',
    })
    const height = Number(globalHeight[1])
    const createPointPosition = Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
    const entity: Cesium.Entity = global.$viewer.entities.add({
      position: createPointPosition,
    })
    // 加入kml文件中
    const setting = {
      Point: {
        coordinates: `${longitude},${latitude}`,
      },
      index: tragetPointArr.value.length,
      height,
    )
    let entity: Cesium.Entity | any = null
      ellipsoidHeight: height,
    }
    if (className === 'finally-point') {
      entity = global.$viewer.entities.add({
        position: createPointPosition,
        label: {
          text: 'ASL:70m',
          font: '13px monospace',
          showBackground: true,
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          pixelOffset: new Cesium.Cartesian2(0, -40),
        },
        billboard: {
          image: createBillboard(tragetPointArr.value.length + 1, '#61d396'),
          pixelOffset: new Cesium.Cartesian2(0, -20),
        },
        point: {
          pixelSize: 20,
          color: Cesium.Color.GHOSTWHITE,
          outlineColor: Cesium.Color.BLACK,
        },
      })
      const prePostion =
        tragetPointArr.value[tragetPointArr.value.length - 1].position
      const polylinePositions = [prePostion, createPointPosition]
      addPolyline({
        polyline: {
          positions: polylinePositions,
          width: 7,
          material: new ImageTrailMaterial({
            backgroundColor: Cesium.Color.fromBytes(96, 210, 149),
            image: getResource('arrow-right.png'),
            imageW: 7,
            duration: 0,
            animation: false,
          }),
          clampToGround: false, // 关闭贴地效果,保留高度
        },
      })
      addPolyline({
        polyline: {
          positions: Cesium.Cartesian3.fromDegreesArrayHeights([
            longitude,
            latitude,
            height,
            longitude,
            latitude,
            0,
          ]),
          width: 1,
          material: new Cesium.PolylineDashMaterialProperty({
            color: Cesium.Color.WHITE,
          }),
        },
      })
      removeAllDataSource()
      removeAllPoint()
      kmzUtils.writePoint(setting)
      globalEntities.value.push(entity)
      mapDraw.drawWayline(globalEntities.value, kmlStr.value)
      tragetPointArr.value.push({
        position: createPointPosition,
        eventList: [],
      })
    }
    if (className === 'add-prev-point') {
      // console.log(1)
    }
    if (className === 'add-next-point') {
      // const index = selectPointIndex.value
      // globalEntities.value.splice(index, 0, entity)
      // console.log()
    }
    // clearCesiumMap()
    // createMapMarker(undefined, [...kmlEntities.value, entity])
@@ -437,6 +376,11 @@
  }
}
// 保存文件
const saveWaylineFile = () => {
  kmzUtils.save()
}
onMounted(() => {
  // 清空画布
  clearCesiumMap()
src/utils/cesium/kmz.ts
@@ -2,6 +2,8 @@
import { mode } from 'crypto-js'
import JsZip from 'jszip'
const noWmpl: string[] = ['Folder', 'Placemark', 'Point', 'coordinates']
/**
 * @description: 读取kmz文件解析出kml文件
 * @param {string} filePath 文件地址
@@ -15,7 +17,10 @@
    .then((kmzZip) => {
      // 通过文件名找到 KML 文件
      const kmlFile = kmzZip.file(/\.kml$/i)[0]
      return kmlFile.async('text')
      return {
        file: kmzZip,
        content: kmlFile.async('text')
      }
    })
}
@@ -47,11 +52,14 @@
    if (Object.prototype.toString.call(settingParmas[key]) === '[object Object]') {
      let parmaStr = ''
      Object.keys(settingParmas[key]).forEach((v) => {
        parmaStr += `<wpml:${v}>${settingParmas[key][v]}</wpml:${v}>`
        const xml = noWmpl.includes(key) ? `<${v}>${settingParmas[key][v]}</${v}>` : `<wpml:${v}>${settingParmas[key][v]}</wpml:${v}>`
        parmaStr += xml
      })
      paramGroup += `<wpml:${key}>${parmaStr}</wpml:${key}>`
      const xml = noWmpl.includes(key) ? `<${key}>${parmaStr}</${key}>` : `<wpml:${key}>${parmaStr}</wpml:${key}>`
      paramGroup += xml
    } else {
      paramGroup += `<wpml:${key}>${settingParmas[key]}</wpml:${key}>`
      const xml = noWmpl.includes(key) ? `<${key}>${settingParmas[key]}</${key}>` : `<wpml:${key}>${settingParmas[key]}</wpml:${key}>`
      paramGroup += xml
    }
  })
  return paramGroup
src/utils/cesium/mapUtils.ts
@@ -1,11 +1,6 @@
import * as Cesium from 'cesium'
export const getLngLatDistance = (
  lat1: number,
  lng1: number,
  lat2: number,
  lng2: number,
) => {
export const getLngLatDistance = (lat1: number, lng1: number, lat2: number, lng2: number) => {
  const radLat1 = (lat1 * Math.PI) / 180.0
  const radLat2 = (lat2 * Math.PI) / 180.0
  const a = radLat1 - radLat2
@@ -13,10 +8,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
@@ -63,3 +55,22 @@
  ctx.closePath()
  return billboard
}
// 获取当前经纬度海拔
export const getHaeHeight = (start: { longitude: number; latitude: number; height: number }, end: { longitude: number; latitude: number; height: number }) => {
  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
}
src/utils/cesium/use-kmz-tsa.ts
@@ -1,11 +1,47 @@
import { ref } from 'vue'
import { ref, getCurrentInstance } from 'vue'
import { analyzeKmzFile, getKmlParams, generateKmlFormat } from '/@/utils/cesium/kmz'
import { getLngLatAltitude } from './mapUtils'
import JSZIP from 'jszip'
import { saveAs } from 'file-saver'
const JsZip = new JSZIP()
const kmlStr = ref('')
const kmzFile = ref<any>(null)
// 点位参数
const placemark: any = {
  Point: {
    coordinates: '',
  },
  index: 0,
  executeHeight: 80,
  height: 80,
  waypointSpeed: 10,
  waypointHeadingParam: {
    waypointHeadingMode: 'followWayline',
    waypointHeadingAngle: 0,
    waypointPoiPoint: '0.000000,0.000000,0.000000',
    waypointHeadingPathMode: 'followBadArc',
    waypointHeadingPoiIndex: 0,
  },
  waypointTurnParam: {
    waypointTurnMode: 'toPointAndStopWithDiscontinuityCurvature',
    waypointTurnDampingDist: 0.2,
  },
  useGlobalHeight: 1,
  useGlobalSpeed: 1,
  useGlobalHeadingParam: 1,
  useGlobalTurnParam: 1,
  useStraightLine: 1,
  actionGroup: {},
}
const useKmzTsa = () => {
  const init = async (fileUrl: string, fileContent?: string) => {
    const kmzRes: string = await analyzeKmzFile(fileUrl)
    const fileRes = await analyzeKmzFile(fileUrl)
    kmzFile.value = fileRes.file
    const kmzRes = await fileRes.content
    if (fileContent) {
      kmlStr.value = fileContent
      return false
@@ -33,10 +69,26 @@
    kmlStr.value = str
  }
  const write = (settingParmas: any = {}, name?: string, isWpml?: boolean = false) => {}
  const del = () => {
  // const write = (settingParmas: any = {}, name: string) => {
  // }
  const writePoint = (settingParmas: any = {}) => {
    Object.keys(settingParmas).forEach((key: string) => {
      placemark[key] = settingParmas[key]
    })
    const Placemark = `<Placemark>${generateKmlFormat(placemark)}</Placemark>`
    const points: RegExpMatchArray = getKmlParams(kmlStr.value, false, {
      name: 'Placemark',
      findRegx: '([\\s\\S]*?)',
      mode: 'g',
    }) || ['']
    const lastPoint = points[points.length - 1]
    const index = kmlStr.value.indexOf(lastPoint)
    const beforeStr = kmlStr.value.slice(0, index)
    const afterStr = kmlStr.value.slice(index + lastPoint.length)
    kmlStr.value = beforeStr + lastPoint + Placemark + afterStr
  }
  const del = () => {}
  const edit = (settingParmas: any = {}, keyName?: string, isKeyWpml: boolean = false) => {
    Object.keys(settingParmas).forEach((key: string) => {
@@ -50,7 +102,7 @@
          const regxVal: any = getKmlParams(kmlStr.value, true, {
            name: 'height',
            findRegx: '([\\s\\S]*?)',
            mode: 'g'
            mode: 'g',
          })
          regxVal.forEach((heightStr: string) => {
            const rstr = generateKmlFormat({ height: settingParmas[key] })
@@ -66,13 +118,18 @@
  }
  const save = () => {
    console.log(kmlStr.value)
    JsZip.file('wpmz/template.kml', kmlStr.value)
    kmzFile.value.file('wpmz/template.kml', kmlStr.value)
    JsZip.generateAsync({ type: 'blob' }).then((content) => {
      saveAs(content, '新建航线-' + new Date().toLocaleString() + '.kmz')
    })
  }
  return {
    init,
    create,
    write,
    // write,
    writePoint,
    del,
    edit,
    save,
src/utils/cesium/use-map-draw.ts
@@ -123,7 +123,8 @@
  let cartesianArr: Cesium.Cartesian3[] = []
  //   绘制路线
  const drawWayline = async (entities: Cesium.Entity[], kmlStr: string) => {
  const drawWayline = async (entities?: Cesium.Entity[], kmlStr?: string) => {
    console.log(kmlStr)
    if (!entities && !kmlStr) {
      dataSource.show = false
    }
@@ -137,7 +138,8 @@
    if (kmlStr) {
      kmlRes = kmlStr
    } else {
      kmlRes = await analyzeKmzFile(fileUrl)
      const fileRes = await analyzeKmzFile(fileUrl)
      kmlRes = await fileRes.content
    }
    // 所有航点
    kmlPoints = getKmlParams(kmlRes, false, {
@@ -293,7 +295,8 @@
    if (kmlStr) {
      kmlRes = kmlStr
    } else {
      await analyzeKmzFile(fileUrl)
      const fileRes = await analyzeKmzFile(fileUrl)
      kmlRes = await fileRes.content
    }
    // 所有航点
    const points = getKmlParams(kmlRes, false, {