GuLiMmo
2024-03-08 016de324c0145632532fcd5450b96a2d1291dac6
update:  右键添加航点完成、kmz文件编辑等
7 files modified
1 files added
435 ■■■■ changed files
src/components/GMap.vue 5 ●●●● patch | view | raw | blame | history
src/components/waylinetool/route-profile.vue 115 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/components/route-edit/components/setting.vue 32 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/components/route-edit/index.vue 140 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/wayline.vue 4 ●●●● patch | view | raw | blame | history
src/utils/cesium/kmz.ts 1 ●●●● patch | view | raw | blame | history
src/utils/cesium/use-kmz-tsa.ts 96 ●●●● patch | view | raw | blame | history
src/utils/cesium/use-map-draw.ts 42 ●●●● patch | view | raw | blame | history
src/components/GMap.vue
@@ -736,6 +736,7 @@
      </DroneControlPanel>
    </div>
    <waylineTool />
    <routeProfile />
  </div>
</template>
@@ -784,6 +785,7 @@
import { convertTimestampToDate } from '/@/utils/time'
import { cesiumOperation } from '/@/hooks/use-cesium-tsa'
import waylineTool from './waylinetool/index.vue'
import routeProfile from './waylinetool/route-profile.vue'
export default defineComponent({
  components: {
    BorderOutlined,
@@ -809,7 +811,8 @@
    CarryOutOutlined,
    RocketOutlined,
    DesktopOutlined,
    waylineTool
    waylineTool,
    routeProfile
  },
  name: 'GMap',
  props: {},
src/components/waylinetool/route-profile.vue
New file
@@ -0,0 +1,115 @@
<template>
  <div class="profile-container" v-if="isShow">
    <div class="wayline-author">
      <div class="wayline-author__file-name" >{{ author.fileName }}</div>
      <div class="wayline-author__name"><UserOutlined /> {{ author.author }}</div>
    </div>
    <ul class="wayline-details">
      <li class="wayline-details__info" v-for="(info, index) in details" :key="index">
        <div class="title">{{ info.title }}</div>
        <div class="value">{{ info.value }}</div>
      </li>
    </ul>
  </div>
</template>
<script setup lang="ts">
import { ERouterName } from '/@/types/index'
import { UserOutlined } from '@ant-design/icons-vue'
import { waylineDetails } from '/@/utils/cesium/use-map-draw'
import { fileAuthor, kmlStr } from '/@/utils/cesium/use-kmz-tsa'
const author = reactive({
  fileName: '',
  author: '',
})
const details = ref<any>([])
const route = useRoute()
const isShow = computed(() => !kmlStr.value && isWaylineRoute && author.fileName && author.author && details.value.length > 0)
watch(
  () => fileAuthor,
  (val) => {
    author.fileName = val.name
    author.author = val.author
  },
  {
    deep: true,
  },
)
watch(
  () => waylineDetails,
  (val) => {
    details.value = val
  },
  {
    deep: true,
  },
)
const isWaylineRoute = ref<boolean>(false)
watch(
  () => route,
  (val) => {
    console.log(val)
    if (val.name === ERouterName.WAYLINE) {
      isWaylineRoute.value = true
    } else {
      isWaylineRoute.value = false
    }
  }, {
    deep: true,
    immediate: true
  }
)
</script>
<style lang="scss" scoped>
.profile-container {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  pointer-events: none;
  .wayline-author {
    color: #fff;
    font-weight: bold;
    padding: 10px;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 100%);
    &__file-name {
      font-size: 17px;
    }
  }
  .wayline-details {
    width: 100%;
    height: 70px;
    padding: 0;
    margin: 0;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    &__info {
      width: fit-content;
      list-style-type: none;
      margin-left: 10px;
      font-weight: bold;
      .title {
        color: rgba(255, 255, 255, 0.65);
        text-align: center;
      }
      .value {
        color: #fff;
        text-align: center;
        font-size: 17px;
      }
    }
  }
}
</style>
src/pages/page-web/projects/components/route-edit/components/setting.vue
@@ -526,28 +526,26 @@
      }
    }
  }
}
  :deep() {
    .ant-popover-content {
      .ant-popover-arrow,
      .ant-popover-inner {
        background-color: #101010;
      }
:deep() {
  .ant-popover-content {
    .ant-popover-arrow,
    .ant-popover-inner {
      background-color: #101010;
    }
      .ant-popover-arrow {
        border-top-color: #101010 !important;
        border-left-color: #101010 !important;
      }
    .ant-popover-arrow {
      border-top-color: #101010 !important;
      border-left-color: #101010 !important;
    }
      .ant-popover-inner {
        border-radius: 15px;
    .ant-popover-inner {
      border-radius: 15px;
      .ant-popover-inner-content {
        padding: 16px;
        .ant-popover-inner-content {
          padding: 16px;
        }
      }
    }
  }
}
</style>
../../../../../../utils/cesium/types/kml
src/pages/page-web/projects/components/route-edit/index.vue
@@ -1,12 +1,26 @@
<template>
  <div class="route-edit-box">
    <div class="btn-group">
      <a-button type="text" @click="backPage">
        <template #icon>
          <ArrowLeftOutlined />
      <a-popover
        :visible="modfiyPopupShow"
        overlayClassName="popup-container"
        placement="leftBottom"
        :getPopupContainer="(triggerNode: any) => triggerNode.parentNode">
        <template #content>
          <div class="popup-text">返回将退出航线编辑器,您要保存吗?如果不保存,您更改的内容将会丢失。</div>
          <div class="btn-tool">
            <a-button size="small" class="btn-cancel" @click="modfiyPopupShow = false">取消</a-button>
            <a-button size="small" class="btn-cancel" @click="handleModfiySave(false)">不保存</a-button>
            <a-button size="small" type="primary" @click="handleModfiySave(true)">保存</a-button>
          </div>
        </template>
        返回
      </a-button>
        <a-button type="text" @click="backPage">
          <template #icon>
            <ArrowLeftOutlined />
          </template>
          返回
        </a-button>
      </a-popover>
      <div class="save-button" @click="saveWaylineFile">
        <SaveOutlined />
      </div>
@@ -34,10 +48,15 @@
        :key="index"
        :class="{ 'active-point': index == selectPointIndex }"
        @click="pointSelect(item, index)">
        <div class="graph">
          <div class="left"></div>
          <div class="right">{{ index + 1 }}</div>
        </div>
        <a-tooltip placement="top">
          <template #title>
            <span>{{ item.isUseGlobalHeight ? '跟随全局' : '不跟随全局' }}</span>
          </template>
          <div class="graph">
            <div class="left" :style="{ borderTopColor: item.isUseGlobalHeight ? '#61d396' : '#409eff' }"></div>
            <div class="right">{{ index + 1 }}</div>
          </div>
        </a-tooltip>
        <div class="graph-right">
          <div v-for="(event, index) in item.eventList" :key="index" class="s-event-icon">
            <a-tooltip placement="top">
@@ -88,6 +107,7 @@
interface tragetPoint {
  position: Cesium.Cartesian3
  isUseGlobalHeight: boolean
  eventList: eventParmas[]
}
@@ -246,7 +266,7 @@
      },
      {
        class: 'add-next-point',
        title: `在${Number(selectPointIndex.value) + 1}号航点前插入`,
        title: `在${Number(selectPointIndex.value) + 1}号航点后插入`,
      },
    ]
    const defaultEvents = [{ class: 'finally-point', title: '在最后航点新增航点' }]
@@ -320,6 +340,11 @@
    const entity: Cesium.Entity = global.$viewer.entities.add({
      position: createPointPosition,
    })
    const posterHeightRegStr = getKmlParams(kmlStr.value, true, {
      name: 'takeOffRefPointAGLHeight',
      findRegx: '([\\s\\S]*?)',
    }) || ['', 0]
    const posterHeight = Number(posterHeightRegStr[1])
    // 加入kml文件中
    const setting = {
      Point: {
@@ -327,27 +352,36 @@
      },
      index: tragetPointArr.value.length,
      height,
      ellipsoidHeight: height,
      ellipsoidHeight: height - posterHeight,
    }
    removeAllDataSource()
    removeAllPoint()
    if (className === 'finally-point') {
      removeAllDataSource()
      removeAllPoint()
      kmzUtils.writePoint(setting)
      kmzUtils.writePoint(setting, 'add')
      globalEntities.value.push(entity)
      mapDraw.drawWayline(globalEntities.value, kmlStr.value)
      tragetPointArr.value.push({
        position: createPointPosition,
        eventList: [],
      })
    }
    if (className === 'add-prev-point') {
      // console.log(1)
      kmzUtils.writePoint(setting, 'prev', selectPointIndex.value)
      globalEntities.value.splice(selectPointIndex.value, 0, entity)
      tragetPointArr.value.splice(selectPointIndex.value, 0, {
        position: createPointPosition,
        eventList: [],
      })
    }
    if (className === 'add-next-point') {
      // const index = selectPointIndex.value
      // globalEntities.value.splice(index, 0, entity)
      // console.log()
      kmzUtils.writePoint(setting, 'next', selectPointIndex.value)
      const index = _.cloneDeep(selectPointIndex.value + 1)
      globalEntities.value.splice(index, 0, entity)
      tragetPointArr.value.splice(index, 0, {
        position: createPointPosition,
        eventList: [],
      })
    }
    mapDraw.drawWayline(globalEntities.value, kmlStr.value)
    // clearCesiumMap()
    // createMapMarker(undefined, [...kmlEntities.value, entity])
  })
@@ -361,8 +395,25 @@
  }
}
const modfiyPopupShow = ref<boolean>(false)
const backPage = () => {
  emits('backFn')
  if (isModify.value) {
    modfiyPopupShow.value = true
  } else {
    mapDraw.clearWaylineData()
    kmlStr.value = ''
    emits('backFn')
  }
}
const handleModfiySave = (isSave: boolean) => {
  if (isSave) {
    saveWaylineFile()
    modfiyPopupShow.value = false
  } else {
    mapDraw.clearWaylineData()
    kmlStr.value = ''
    emits('backFn')
  }
}
// 清空画布
const clearCesiumMap = () => {
@@ -379,7 +430,22 @@
// 保存文件
const saveWaylineFile = () => {
  kmzUtils.save()
  mapDraw.clearWaylineData()
  kmlStr.value = ''
  emits('backFn')
}
// 判断当前文件是否被修改
const isModify = ref<boolean>(false)
watch(
  () => kmlStr.value,
  (val) => {
    isModify.value = true
  },
  {
    deep: true,
  },
)
onMounted(() => {
  // 清空画布
@@ -410,17 +476,48 @@
    align-items: center;
    justify-content: flex-start;
    :deep() {
      .popup-container {
        .ant-popover-content {
          .ant-popover-arrow,
          .ant-popover-inner {
            border-color: #282828;
            background-color: #282828;
            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
            .popup-text {
              color: #fff;
              width: 400px;
            }
            .btn-tool {
              margin-top: 10px;
              display: flex;
              justify-content: flex-end;
              .ant-btn {
                border: 0;
              }
              .btn-cancel {
                background-color: #3c3c3c;
                margin-right: 5px;
              }
            }
          }
        }
      }
    }
    .ant-btn {
      color: #fff;
    }
    .save-button {
      font-size: 20px;
      margin: 0 10px 0 auto;
      cursor: pointer;
      &:hover {
        color: #409eff;
      }
    }
    .setting-btn {
      background-color: #3c3c3c;
      margin-right: 10px;
@@ -472,6 +569,7 @@
    padding: 0;
    height: calc(100vh - 180px);
    overflow: auto;
    li {
      cursor: pointer;
      padding: 10px 0;
@@ -522,8 +620,10 @@
    }
  }
}
.active-point {
  background-color: #3c3c3c;
  .graph {
    .left {
      border-top-color: #f3bf4e !important;
src/pages/page-web/projects/wayline.vue
@@ -230,6 +230,7 @@
  importKmzFile,
  getWayLineFile,
} from '/@/api/wayline'
import EventBus from '/@/event-bus/'
import { ELocalStorageKey, ERouterName } from '/@/types'
import { EDeviceType } from '/@/types/device'
import { useMyStore } from '/@/store'
@@ -242,6 +243,7 @@
import * as Cesium from 'cesium'
import { cesiumOperation } from '/@/hooks/use-cesium-tsa'
import useMapDraw from '/@/utils/cesium/use-map-draw'
import { fileAuthor } from '/@/utils/cesium/use-kmz-tsa'
import axios from 'axios'
import JSZIP from 'jszip'
// 初始化jszip
@@ -394,6 +396,8 @@
function selectRoute (wayline: WaylineFile) {
  loading.value = true
  fileAuthor.name = wayline.name
  fileAuthor.author = wayline.user_name
  getWayLineFile(workspaceId.value, wayline.id)
    .then((res) => {
      store.commit('SET_SELECT_WAYLINE_INFO', wayline)
src/utils/cesium/kmz.ts
@@ -1,5 +1,4 @@
import axios from 'axios'
import { mode } from 'crypto-js'
import JsZip from 'jszip'
const noWmpl: string[] = ['Folder', 'Placemark', 'Point', 'coordinates']
src/utils/cesium/use-kmz-tsa.ts
@@ -1,11 +1,15 @@
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'
import { point } from '@turf/turf'
const JsZip = new JSZIP()
const fileAuthor = reactive({
  name: '',
  author: '',
})
const kmlStr = ref('')
const kmzFile = ref<any>(null)
@@ -71,7 +75,7 @@
  // const write = (settingParmas: any = {}, name: string) => {
  // }
  const writePoint = (settingParmas: any = {}) => {
  const writePoint = (settingParmas: any = {}, place: string, pointNumber: number = 0) => {
    Object.keys(settingParmas).forEach((key: string) => {
      placemark[key] = settingParmas[key]
    })
@@ -81,11 +85,46 @@
      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
    if (place === 'add') {
      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
    }
    if (place === 'prev') {
      const currentPoint: string = points[pointNumber]
      const index = kmlStr.value.indexOf(currentPoint)
      const beforeStr = kmlStr.value.slice(0, index)
      const afterStr = kmlStr.value.slice(index + currentPoint.length)
      kmlStr.value = beforeStr + Placemark + currentPoint + afterStr
      // 更新点位序号
      const pointIndexList = getKmlParams(kmlStr.value, true, {
        name: 'index',
        findRegx: '([\\s\\S]*?)',
        mode: 'g',
      })
      pointIndexList?.forEach((pintIndex: string, index: number) => {
        kmlStr.value.replace(pintIndex, `<wpml:index>${index}</wpml:index>`)
      })
    }
    if (place === 'next') {
      const currentPoint: string = points[pointNumber + 1]
      const index = kmlStr.value.indexOf(currentPoint)
      const beforeStr = kmlStr.value.slice(0, index)
      const currentPointLength = currentPoint?.length || 0
      const afterStr = kmlStr.value.slice(index + currentPointLength)
      kmlStr.value = beforeStr + Placemark + currentPoint + afterStr
      // 更新点位序号
      const pointIndexList = getKmlParams(kmlStr.value, true, {
        name: 'index',
        findRegx: '([\\s\\S]*?)',
        mode: 'g',
      })
      pointIndexList?.forEach((pintIndex: string, index: number) => {
        kmlStr.value.replace(pintIndex, `<wpml:index>${index}</wpml:index>`)
      })
    }
  }
  const del = () => {}
@@ -104,9 +143,44 @@
            findRegx: '([\\s\\S]*?)',
            mode: 'g',
          })
          regxVal.forEach((heightStr: string) => {
            const rstr = generateKmlFormat({ height: settingParmas[key] })
            kmlStr.value = kmlStr.value.replace(heightStr, rstr)
          const ellipsoidHeightRegx = getKmlParams(kmlStr.value, true, {
            name: 'ellipsoidHeight',
            findRegx: '([\\s\\S]*?)',
            mode: 'g',
          }) || ['']
          // 文件中的海拔高度
          const takeOffRefPointAGLHeightRegxStr = getKmlParams(kmlStr.value, true, {
            name: 'takeOffRefPointAGLHeight',
            findRegx: '([\\s\\S]*?)'
          }) || ['', '0']
          const posterHeight = Number(takeOffRefPointAGLHeightRegxStr[1])
          // 获取每个点是否使用全局高度
          const useGlobalHeightRegStr = getKmlParams(kmlStr.value, true, {
            name: 'useGlobalHeight',
            findRegx: '([\\s\\S]*?)',
            mode: 'g',
          }) || ['', '0']
          regxVal.forEach((heightStr: string, index: number) => {
            // 使用正则取值
            const getUseGlobalHeightValStr = getKmlParams(useGlobalHeightRegStr[index], true, {
              name: 'useGlobalHeight',
              findRegx: '([\\s\\S]*?)',
            }) || ['', '0']
            const isGlobalHeight = Boolean(Number(getUseGlobalHeightValStr[1]))
            if (isGlobalHeight) {
              // 替换height
              const rstr = generateKmlFormat({ height: settingParmas[key] })
              const rEllipsoidHeightStr = generateKmlFormat({ ellipsoidHeight: settingParmas[key] - posterHeight })
              kmlStr.value = kmlStr.value.replace(heightStr, rstr)
              kmlStr.value = kmlStr.value.replace(ellipsoidHeightRegx[index], rEllipsoidHeightStr)
              // 替换
            } else {
              const heightRegxVal: any = getKmlParams(heightStr, true, {
                name: 'height',
                findRegx: '([\\s\\S]*?)',
              })
              console.log(heightRegxVal)
            }
          })
        }
        const replaceStr = generateKmlFormat({ [key]: settingParmas[key] })
@@ -136,5 +210,5 @@
  }
}
export { kmlStr }
export { kmlStr, fileAuthor }
export default useKmzTsa
src/utils/cesium/use-map-draw.ts
@@ -22,6 +22,7 @@
}
interface tragetPoint {
  position: Cesium.Cartesian3
  isUseGlobalHeight: boolean,
  eventList: eventParmas[]
}
@@ -122,9 +123,10 @@
  //  记录点位信息
  let cartesianArr: Cesium.Cartesian3[] = []
  //   绘制路线
  // 绘制路线
  // 起飞点
  // let btmStartPoint: Cesium.Cartesian3 | null = null
  const drawWayline = async (entities?: Cesium.Entity[], kmlStr?: string) => {
    console.log(kmlStr)
    if (!entities && !kmlStr) {
      dataSource.show = false
    }
@@ -149,6 +151,23 @@
    })
    // 起飞点
    let btmStartPoint = null
    // 获取文件中的起飞点
    const startPointRegStr = getKmlParams(kmlRes, true, {
      name: 'takeOffRefPoint',
      findRegx: '([\\s\\S]*?)',
    }) || ['', '0.00000,0.00000,0.00000']
    const startPointPosition = startPointRegStr[1].split(',')
    btmStartPoint = Cesium.Cartesian3.fromDegrees(
      Number(startPointPosition[1]),
      Number(startPointPosition[0]),
      Number(startPointPosition[2])
    )
    // 参考起飞点“海拔高度,与“参考起飞点”中的椭球高度对应
    // const posterHeightRegStr = getKmlParams(kmlRes, true, {
    //   name: 'takeOffRefPointAGLHeight',
    //   findRegx: '([\\s\\S]*?)',
    // }) || ['', 0]
    // const posterHeight = Number(posterHeightRegStr[1])
    // 判断是否存在实体集
    if (entities) {
      cartesianArr = []
@@ -172,15 +191,23 @@
        name: 'height',
        findRegx: '([\\s\\S]*?)',
      })
      // 获取是否使用全局高度
      const useGlobalHeightRegStr = getKmlParams(point, true, {
        name: 'useGlobalHeight',
        findRegx: '([\\s\\S]*?)',
      }) || ['', '0']
      const isUseGlobalHeight = Boolean(Number(useGlobalHeightRegStr[1]))
      const AslHeight = Number(getPointHeight[1])
      const HeaHeight = Number(getHaeHeight[1])
      entity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude, AslHeight)
      if (i === 0) {
        btmStartPoint = Cesium.Cartesian3.fromDegrees(longitude, latitude, 0)
      }
      // 创建广告牌信息
      const title = Number(i + 1) === 1 ? 'S' : i
      const billboard = createTriangleMarker(title, '#61d396')
      const title = Number(i + 1) === 1 ? 'S' : i + 1
      let billboard = null
      if (isUseGlobalHeight) {
        billboard = createTriangleMarker(title, '#61d396')
      } else {
        billboard = createTriangleMarker(title, '#409eff')
      }
      // 修改id
      entity._id = 'tragetPoint' + i
      // 修改广告牌样式
@@ -228,6 +255,7 @@
      // 当前航线点位事件信息等
      waylinePointsEvent.value.push({
        position: entity.position._value,
        isUseGlobalHeight,
        eventList: [],
      })
    }