| | |
| | | VITE_API_URL = 'http://139.196.74.78:6789' |
| | | VITE_MEDIAPANEL_API_URL = 'https://dev.jxpskj.com:8026/cloud-bucket', |
| | | VITE_MEDIAPANEL_API_URL = 'https://dev.jxpskj.com:9000/cloud-bucket' |
| | | VITE_WS_API_URL = 'ws://139.196.74.78:6789/api/v1/ws' |
| | | VITE_APP_ENVIRONMENT=DEV |
| | | VITE_BASE_API = '/drone-api' |
| | |
| | | VITE_APP_ENVIRONMENT=PROD |
| | | VITE_APP_APIGATEWAY_BACKEND_HOST='' |
| | | VITE_API_URL = 'https://dev.jxpskj.com:36789' |
| | | VITE_MEDIAPANEL_API_URL = 'https://dev.jxpskj.com:8026/cloud-bucket', |
| | | VITE_MEDIAPANEL_API_URL = 'https://dev.jxpskj.com:9000/cloud-bucket' |
| | | VITE_WS_API_URL = 'wss://dev.jxpskj.com:36789/api/v1/ws' |
| | | VITE_BASE_API = '/drone-api' |
| | |
| | | "dependencies": { |
| | | "@amap/amap-jsapi-loader": "^1.0.1", |
| | | "@ant-design/icons-vue": "^6.0.1", |
| | | "@mapbox/togeojson": "^0.16.2", |
| | | "@turf/turf": "^6.5.0", |
| | | "@vitejs/plugin-legacy": "^1.6.2", |
| | | "agora-rtc-sdk-ng": "^4.12.1", |
| | |
| | | "vue-i18n": "^9.1.6", |
| | | "vue-router": "4", |
| | | "vuex": "^4.0.2", |
| | | "vuex-persistedstate": "^4.1.0" |
| | | "vuex-persistedstate": "^4.1.0", |
| | | "xml-js": "^1.6.11" |
| | | }, |
| | | "devDependencies": { |
| | | "@types/crypto-js": "^4.1.1", |
| | |
| | | <div class="setting-content"> |
| | | <div class="start-point common"> |
| | | <div class="title">起飞点</div> |
| | | <div class="value">{{ missionConfigSetting.takeOffRefPoint ? '已设置' : '未设置' }}</div> |
| | | <div class="value"> |
| | | {{ xmlTemplate.missionConfig?.takeOffRefPoint['#text'] !== '' ? '已设置' : '未设置' }} |
| | | </div> |
| | | </div> |
| | | <div class="photo-setting common"> |
| | | <div class="title">拍照设置</div> |
| | | <ul class="select-box"> |
| | | <li |
| | | class="select-item" |
| | | :style="{ |
| | | '--settingColor': folderSetting.payloadParam.imageFormat.includes(item.value) ? '#409eff' : '#3c3c3c', |
| | | }" |
| | | v-for="item in photoSetting" |
| | | :key="item.value" |
| | | @click="selectPhotoSetting(item.value)"> |
| | | <li class="select-item" v-for="item in photoSetting" :key="item.value" :style="{ |
| | | '--settingColor': xmlTemplate.Folder.payloadParam?.imageFormat['#text'] |
| | | .split(',') |
| | | .includes(item.value) |
| | | ? '#409eff' |
| | | : '#3c3c3c', |
| | | }" @click="selectPhotoSetting(item.value)"> |
| | | {{ item.title }}照片 |
| | | </li> |
| | | </ul> |
| | |
| | | <div class="climb-mode common"> |
| | | <a-tooltip placement="right" :title="tipDescriptionEnums.climbModeTip"> |
| | | <div class="mode-box"> |
| | | <a-radio-group v-model:value="missionConfigSetting.flyToWaylineMode" button-style="solid"> |
| | | <a-radio-group v-model:value="xmlTemplate.missionConfig.flyToWaylineMode['#text']" button-style="solid"> |
| | | <a-radio-button value="safely">垂直爬升</a-radio-button> |
| | | <a-radio-button value="pointToPoint">倾斜爬升</a-radio-button> |
| | | </a-radio-group> |
| | |
| | | <ul class="parameter-btn"> |
| | | <li @click="numberControls(true, 100, 'takeOffSecurityHeight')">+100</li> |
| | | <li @click="numberControls(true, 10, 'takeOffSecurityHeight')">+10</li> |
| | | <li> |
| | | <span>{{ missionConfigSetting.takeOffSecurityHeight }}</span |
| | | >m |
| | | <li class="parameter-input"> |
| | | <a-input-number :min="2" :max="1500" |
| | | v-model:value="xmlTemplate.missionConfig.takeOffSecurityHeight['#text']"></a-input-number>m |
| | | </li> |
| | | <li @click="numberControls(false, 100, 'takeOffSecurityHeight')">-100</li> |
| | | <li @click="numberControls(false, 10, 'takeOffSecurityHeight')">-10</li> |
| | |
| | | </div> |
| | | <!-- 高度模式 --> |
| | | <div class="height-mode common"> |
| | | <a-tooltip |
| | | placement="right" |
| | | :title="tipDescriptionEnums[folderSetting.waylineCoordinateSysParam.heightMode]"> |
| | | <a-tooltip placement="right" |
| | | :title="tipDescriptionEnums[xmlTemplate.Folder.waylineCoordinateSysParam.heightMode['#text']]"> |
| | | <div class="title">航线高度模式</div> |
| | | <div class="mode-box"> |
| | | <a-radio-group v-model:value="folderSetting.waylineCoordinateSysParam.heightMode" button-style="solid"> |
| | | <a-radio-group v-model:value="xmlTemplate.Folder.waylineCoordinateSysParam.heightMode['#text']" |
| | | button-style="solid"> |
| | | <a-radio-button v-for="item in heightModeSetting" :key="item.value" :value="item.value">{{ |
| | | item.title |
| | | }}</a-radio-button> |
| | | item.title |
| | | }}</a-radio-button> |
| | | </a-radio-group> |
| | | </div> |
| | | <div class="parameter-tool"> |
| | | <div class="example-img"> |
| | | <img |
| | | :src=" |
| | | folderSetting.waylineCoordinateSysParam.heightMode === heightModeSetting[0].value |
| | | ? heightModeSetting[0].img |
| | | : heightModeSetting[1].img |
| | | " |
| | | alt="height-img" /> |
| | | <img :src="true ? heightModeSetting[0].img : heightModeSetting[1].img" alt="height-img" /> |
| | | </div> |
| | | <div class="parameter-btn"> |
| | | <li @click="numberControls(true, 100, 'globalHeight')">+100</li> |
| | | <li @click="numberControls(true, 10, 'globalHeight')">+10</li> |
| | | <li> |
| | | <span>{{ Math.round(folderSetting.globalHeight) }}</span> m |
| | | <li class="parameter-input"> |
| | | <a-input-number :min="0" :max="10000" |
| | | v-model:value="xmlTemplate.Folder.globalHeight['#text']"></a-input-number>m |
| | | </li> |
| | | <li @click="numberControls(false, 100, 'globalHeight')">-100</li> |
| | | <li @click="numberControls(false, 10, 'globalHeight')">-10</li> |
| | |
| | | <div class="speed-box"> |
| | | <div class="subtract" @click="numberControls(false, 1, 'autoFlightSpeed')">-</div> |
| | | <div class="text"> |
| | | <a-input-number |
| | | :min="1" |
| | | :max="15" |
| | | class="value" |
| | | v-model:value="folderSetting.autoFlightSpeed"></a-input-number> |
| | | <a-input-number v-model:value="xmlTemplate.Folder.autoFlightSpeed['#text']" :min="1" :max="15" |
| | | class="value"> |
| | | </a-input-number> |
| | | m / s |
| | | </div> |
| | | <div class="add" @click="numberControls(true, 1, 'autoFlightSpeed')">+</div> |
| | |
| | | <script setup lang="ts"> |
| | | import { reactive, defineProps } from 'vue' |
| | | import { tipDescriptionEnums } from './enums' |
| | | import useKmzTsa, { kmlStr } from '/@/utils/cesium/use-kmz-tsa' |
| | | import useKmzTsa, { kmlStr, template as xmlTemplate } from '/@/utils/cesium/use-kmz-tsa' |
| | | import useMapDraw from '/@/utils/cesium/use-map-draw' |
| | | import { getKmlParams } from '/@/utils/cesium/kmz' |
| | | import { cesiumOperation } from '/@/hooks/use-cesium-tsa' |
| | |
| | | const mapDraw = useMapDraw('', '', '', global) |
| | | const { removeAllDataSource, removeAllPoint } = cesiumOperation() |
| | | |
| | | const kmzUtils = useKmzTsa() |
| | | interface setting { |
| | | imageFormat: string[] |
| | | } |
| | | |
| | | const props = defineProps({ |
| | | loadCompleted: Boolean, |
| | | }) |
| | | |
| | | const getResource = (name: string) => { |
| | | return new URL(`/src/assets/icons/${name}`, import.meta.url).href |
| | | } |
| | | const getSvgResource = (name: string) => { |
| | | return new URL(`/src/assets/svg/${name}`, import.meta.url).href |
| | | } |
| | |
| | | const climbImage = getSvgResource('climb.svg') |
| | | const imgs = [] |
| | | |
| | | const missionConfigSetting = reactive<missionConfig | any>({ |
| | | flyToWaylineMode: 'safely', |
| | | // 航线结束动作 |
| | | finishAction: 'goHome', |
| | | // 失控是否继续执行航线 |
| | | exitOnRCLost: 'goContinue', |
| | | // 失控动作 |
| | | executeRCLostAction: 'goBack', |
| | | // 安全起飞高度 |
| | | takeOffSecurityHeight: 20, |
| | | // 全局航线速度 |
| | | globalTransitionalSpeed: 10, |
| | | // 参考起飞点 |
| | | takeOffRefPoint: '', |
| | | // 参考起飞点海拔高度 |
| | | takeOffRefPointAGLHeight: 0, |
| | | // 全局返航高度 |
| | | globalRTHHeight: 100, |
| | | droneInfo: { |
| | | droneEnumValue: 67, |
| | | droneSubEnumValue: 0, |
| | | }, |
| | | payloadInfo: { |
| | | payloadEnumValue: 52, |
| | | payloadSubEnumValue: 1, |
| | | payloadPositionIndex: 0, |
| | | }, |
| | | }) |
| | | |
| | | // 拍照设置 |
| | | const photoSetting = reactive([ |
| | | interface photoSetting { |
| | | title: string |
| | | value: string |
| | | } |
| | | const photoSetting = reactive<photoSetting[]>([ |
| | | { title: '广角', value: 'wide' }, |
| | | { title: '变焦', value: 'zoom' }, |
| | | { title: '红外', value: 'ir' }, |
| | |
| | | }, |
| | | ]) |
| | | |
| | | // folder设置 |
| | | const folderSetting = reactive<Folder | any>({ |
| | | templateType: 'waypoint', |
| | | templateId: 0, |
| | | waylineCoordinateSysParam: { |
| | | coordinateMode: 'WGS84', |
| | | heightMode: 'EGM96', |
| | | }, |
| | | autoFlightSpeed: 10, |
| | | globalHeight: 100, |
| | | payloadParam: { |
| | | payloadPositionIndex: 0, |
| | | imageFormat: '', |
| | | }, |
| | | caliFlightEnable: 0, |
| | | gimbalPitchMode: 'manual', |
| | | }) |
| | | |
| | | // 计算 |
| | | const numberControls = (isAdd: boolean, computeVal: number, key: string) => { |
| | | if (isAdd) { |
| | | if (missionConfigSetting[key]) { |
| | | missionConfigSetting[key] += computeVal |
| | | } else { |
| | | folderSetting[key] += computeVal |
| | | } |
| | | } else { |
| | | if (missionConfigSetting[key]) { |
| | | missionConfigSetting[key] -= computeVal |
| | | } else { |
| | | if (folderSetting[key] - computeVal <= 0) { |
| | | return message.warn('飞行高度较低!!!!') |
| | | } |
| | | folderSetting[key] -= computeVal |
| | | } |
| | | let obj = { '#text': 0 } |
| | | if (key === 'takeOffSecurityHeight') { |
| | | obj = xmlTemplate.value.missionConfig.takeOffSecurityHeight |
| | | } |
| | | |
| | | if (key === 'globalHeight') { |
| | | const entities = mapDraw.kmlEntities.value |
| | | removeAllDataSource() |
| | | removeAllPoint() |
| | | mapDraw.drawWayline(entities, kmlStr.value) |
| | | obj = xmlTemplate.value.Folder.globalHeight |
| | | } |
| | | } |
| | | |
| | | // 获取机场默认值 |
| | | watch( |
| | | () => props.loadCompleted, |
| | | (val) => { |
| | | if (val) { |
| | | getDefaultValue(missionConfigSetting) |
| | | getDefaultValue(folderSetting) |
| | | } |
| | | }, |
| | | ) |
| | | const getDefaultValue = (setting: any) => { |
| | | Object.keys(setting).forEach((key: string) => { |
| | | const param: RegExpMatchArray | any = getKmlParams(kmlStr.value, true, { |
| | | name: key, |
| | | findRegx: '([\\s\\S]*?)', |
| | | }) |
| | | if (Object.prototype.toString.call(setting[key]) === '[object Object]') { |
| | | const value: RegExpMatchArray | any = getKmlParams(param[0], true, { |
| | | name: key, |
| | | findRegx: '([\\s\\S]*?)', |
| | | }) |
| | | Object.keys(setting[key]).forEach((v) => { |
| | | const keyValue: RegExpMatchArray | any = getKmlParams(value[1], true, { |
| | | name: v, |
| | | findRegx: '([\\s\\S]*?)', |
| | | }) |
| | | if (!Number(keyValue[1])) { |
| | | setting[key][v] = keyValue[1] |
| | | } else { |
| | | setting[key][v] = Number(keyValue[1]) |
| | | } |
| | | }) |
| | | } else { |
| | | if (!Number(param[1])) { |
| | | setting[key] = param[1] |
| | | } else { |
| | | setting[key] = Number(param[1]) |
| | | } |
| | | } |
| | | }) |
| | | return setting |
| | | } |
| | | |
| | | // 全局设置写入kmz文件 |
| | | watch( |
| | | () => missionConfigSetting, |
| | | (settingVal) => { |
| | | if (!props.loadCompleted) return |
| | | kmzUtils.edit(settingVal, 'missionConfig', true) |
| | | }, |
| | | { |
| | | deep: true, |
| | | }, |
| | | ) |
| | | |
| | | // folder设置写入kmz文件 |
| | | watch( |
| | | () => folderSetting, |
| | | (newVal) => { |
| | | if (!props.loadCompleted) return |
| | | kmzUtils.edit(newVal, 'Folder', true) |
| | | }, |
| | | { |
| | | deep: true, |
| | | }, |
| | | ) |
| | | |
| | | // 拍照设置 |
| | | const selectPhotoSetting = (value: string) => { |
| | | const arr: string[] = folderSetting.payloadParam.imageFormat.split(',') |
| | | const index: number = arr.findIndex((item) => item === value) |
| | | if (index !== -1) { |
| | | arr.splice(index, 1) |
| | | if (key === 'autoFlightSpeed') { |
| | | obj = xmlTemplate.value.Folder.autoFlightSpeed |
| | | } |
| | | let value = Number(obj['#text']) |
| | | if (isAdd) { |
| | | value += computeVal |
| | | } else { |
| | | arr.push(value) |
| | | value -= computeVal |
| | | } |
| | | folderSetting.payloadParam.imageFormat = arr.join(',') |
| | | obj['#text'] = value |
| | | } |
| | | |
| | | // 拍照选择 |
| | | const selectPhotoSetting = (value: string) => { |
| | | const imageFormat = xmlTemplate.value.Folder.payloadParam.imageFormat |
| | | const imageFormatArr = imageFormat['#text'].split(',') |
| | | if (imageFormatArr.includes(value)) { |
| | | const index = imageFormatArr.findIndex((item: string) => item === value) |
| | | imageFormatArr.splice(index, 1) |
| | | } else { |
| | | imageFormatArr.push(value) |
| | | } |
| | | xmlTemplate.value.Folder.payloadParam.imageFormat['#text'] = imageFormatArr.join(',') |
| | | } |
| | | </script> |
| | | |
| | |
| | | .start-point { |
| | | display: flex; |
| | | justify-content: flex-start; |
| | | |
| | | .value { |
| | | margin-left: auto; |
| | | color: #409eff; |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .parameter-input { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | :deep() { |
| | | .ant-input-number { |
| | | background-color: transparent; |
| | | color: #409eff; |
| | | font-size: 23px; |
| | | border: 0; |
| | | |
| | | .ant-input-number-handler-wrap { |
| | | display: none; |
| | | } |
| | | |
| | | .ant-input-number-input-wrap { |
| | | input { |
| | | font-weight: bold; |
| | | text-align: center; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | :deep() { |
| | | .ant-popover-content { |
| | | |
| | | .ant-popover-arrow, |
| | | .ant-popover-inner { |
| | | background-color: #101010; |
| | |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import _ from 'lodash' |
| | | import _, { template } from 'lodash' |
| | | import * as Cesium from 'cesium' |
| | | import setting from './components/setting.vue' |
| | | import useKmzTsa, { kmlStr } from '/@/utils/cesium/use-kmz-tsa' |
| | | import useKmzTsa, { kmlStr, template as xmlTemplate } 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, { kmlEntities as globalEntities } from '/@/utils/cesium/use-map-draw' |
| | | import useMapDraw, { kmlEntities as globalEntities, selectPointIndex as pointIdx } from '/@/utils/cesium/use-map-draw' |
| | | import { getKmlParams } from '/@/utils/cesium/kmz' |
| | | import { getHaeHeight } from '/@/utils/cesium/mapUtils' |
| | | const store = useMyStore() |
| | |
| | | global.$viewer.entities.remove(nextPointEntity) |
| | | kmlEntities.value.forEach((entity: Cesium.Entity | any, i: number) => { |
| | | entity.billboard.image = createBillboard(i + 1, '#61d396') |
| | | entity.label.show = false |
| | | }) |
| | | selectPointIndex.value = null |
| | | pointIdx.value = null |
| | | return |
| | | } |
| | | if (prevPointEntity) { |
| | |
| | | kmlEntities.value.forEach((entity: Cesium.Entity | any, i: number) => { |
| | | if (i === index) { |
| | | entity.billboard.image = createBillboard(index + 1, '#f3be4f') |
| | | entity.label.show = true |
| | | } else { |
| | | entity.billboard.image = createBillboard(i + 1, '#61d396') |
| | | entity.label.show = false |
| | | } |
| | | }) |
| | | selectPointIndex.value = index |
| | | pointIdx.value = index |
| | | } |
| | | |
| | | // 添加右键事件,弹出窗口 |
| | |
| | | const longitude = Cesium.Math.toDegrees(c2Postion.longitude) |
| | | const latitude = Cesium.Math.toDegrees(c2Postion.latitude) |
| | | // 获取全局高度 |
| | | const globalHeight: any = getKmlParams(kmlStr.value, true, { |
| | | name: 'globalHeight', |
| | | findRegx: '([\\s\\S]*?)', |
| | | }) |
| | | const height = Number(globalHeight[1]) |
| | | const globalHeight: string = xmlTemplate.value.Folder.globalHeight['#text'] |
| | | const height = Number(globalHeight) |
| | | // 右键点击的位置 |
| | | const createPointPosition = Cesium.Cartesian3.fromDegrees(longitude, latitude, height) |
| | | 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 takeOffRefPointAGLHeight = xmlTemplate.value.missionConfig.takeOffRefPointAGLHeight |
| | | const posterHeight = Number(takeOffRefPointAGLHeight['#text']) |
| | | // 当前点位个数 |
| | | const points = xmlTemplate.value.Folder.Placemark |
| | | // 加入到解析的JSON对象中 |
| | | const setting = { |
| | | Point: { |
| | | coordinates: `${longitude},${latitude}`, |
| | | coordinates: { '#text': `${longitude.toFixed(6)},${latitude.toFixed(6)}` }, |
| | | }, |
| | | index: tragetPointArr.value.length, |
| | | height, |
| | | ellipsoidHeight: height - posterHeight, |
| | | index: { '#text': points.length }, |
| | | height: { '#text': height }, |
| | | ellipsoidHeight: { '#text': height - posterHeight }, |
| | | isRisky: { '#text': 0 }, |
| | | useGlobalHeadingParam: { '#text': 1 }, |
| | | useGlobalHeight: { '#text': 1 }, |
| | | useGlobalSpeed: { '#text': 1 }, |
| | | useGlobalTurnParam: { '#text': 1 }, |
| | | useStraightLine: { '#text': 1 }, |
| | | waypointHeadingParam: { |
| | | waypointHeadingAngle: { '#text': 0 }, |
| | | waypointHeadingMode: { '#text': 'followWayline' }, |
| | | waypointHeadingPathMode: { '#text': 'followBadArc' }, |
| | | waypointHeadingPoiIndex: { '#text': 0 }, |
| | | waypointPoiPoint: { '#text': '0.000000,0.000000,0.000000' }, |
| | | }, |
| | | waypointSpeed: { '#text': 10 }, |
| | | waypointTurnParam: { |
| | | waypointTurnDampingDist: { '#text': 0.2 }, |
| | | waypointTurnMode: { '#text': 'toPointAndStopWithDiscontinuityCurvature' }, |
| | | }, |
| | | } |
| | | removeAllDataSource() |
| | | removeAllPoint() |
| | | if (className === 'finally-point') { |
| | | kmzUtils.writePoint(setting, 'add') |
| | | globalEntities.value.push(entity) |
| | | xmlTemplate.value.Folder.Placemark.push(setting) |
| | | tragetPointArr.value.push({ |
| | | position: createPointPosition, |
| | | eventList: [], |
| | | isUseGlobalHeight: Boolean(setting.useGlobalHeight['#text']), |
| | | }) |
| | | } |
| | | if (className === 'add-prev-point') { |
| | | kmzUtils.writePoint(setting, 'prev', selectPointIndex.value) |
| | | globalEntities.value.splice(selectPointIndex.value, 0, entity) |
| | | xmlTemplate.value.Folder.Placemark.splice(selectPointIndex.value, 0, setting) |
| | | tragetPointArr.value.splice(selectPointIndex.value, 0, { |
| | | position: createPointPosition, |
| | | eventList: [], |
| | | isUseGlobalHeight: Boolean(setting.useGlobalHeight['#text']), |
| | | }) |
| | | } |
| | | if (className === 'add-next-point') { |
| | | kmzUtils.writePoint(setting, 'next', selectPointIndex.value) |
| | | const index = _.cloneDeep(selectPointIndex.value + 1) |
| | | globalEntities.value.splice(index, 0, entity) |
| | | xmlTemplate.value.Folder.Placemark.splice(index, 0, setting) |
| | | tragetPointArr.value.splice(index, 0, { |
| | | position: createPointPosition, |
| | | eventList: [], |
| | | isUseGlobalHeight: Boolean(setting.useGlobalHeight['#text']), |
| | | }) |
| | | } |
| | | mapDraw.drawWayline(globalEntities.value, kmlStr.value) |
| | | // clearCesiumMap() |
| | | // createMapMarker(undefined, [...kmlEntities.value, entity]) |
| | | // 更新点位序号 |
| | | xmlTemplate.value.Folder.Placemark.forEach((placemark: { index: { [x: string]: number } }, index: number) => { |
| | | placemark.index['#text'] = index |
| | | }) |
| | | |
| | | const xmlStr = kmzUtils.generateXML(xmlTemplate.value) |
| | | mapDraw.drawWayline(globalEntities.value, xmlStr) |
| | | }) |
| | | } |
| | | |
| | |
| | | import axios from 'axios' |
| | | import JsZip from 'jszip' |
| | | import toGeoJson from '@mapbox/togeojson' |
| | | // import Xml2Json from 'xml2json' |
| | | // const xmlJson = new Xml2Json() |
| | | |
| | | const noWmpl: string[] = ['Folder', 'Placemark', 'Point', 'coordinates'] |
| | | const noWmpl: string[] = ['Document', 'Folder', 'Placemark', 'Point', 'coordinates'] |
| | | |
| | | /** |
| | | * @description: 读取kmz文件解析出kml文件 |
| | |
| | | return paramGroup |
| | | } |
| | | |
| | | export const insertChar = (str: string, index: number, char: string) => { |
| | | if (index > str.length) { |
| | | return str + char |
| | | } else if (index < 0) { |
| | | return char + str |
| | | } else { |
| | | return str.slice(0, index) + char + str.slice(index) |
| | | } |
| | | } |
| | | |
| | | export const getTagNameFromXml = (xmlString: string): string | null => { |
| | | const parser = new DOMParser() |
| | | const xmlDoc = parser.parseFromString(xmlString, 'text/xml') |
| | |
| | | } |
| | | |
| | | // 将xml转为json |
| | | const deepParse = (xmlDoc: Document, tagName: string) => { |
| | | const xmlObj: { [key: string]: any } = {} |
| | | const rootDom = xmlDoc.getElementsByTagName(tagName)[0] |
| | | for (let i = 0; i < rootDom.children.length; i++) { |
| | | const child = rootDom.children[i] |
| | | const childName = child.nodeName.replace('wpml:', '') |
| | | if (child.children.length > 0) { |
| | | xmlObj[childName] = deepParse(xmlDoc, child.nodeName) |
| | | } else { |
| | | if (xmlObj[childName] !== undefined) { |
| | | if (!Array.isArray(xmlObj[childName])) { |
| | | xmlObj[childName] = [xmlObj[childName]] |
| | | const deepParse = ( |
| | | xml: |
| | | | { |
| | | nodeType: number |
| | | childNodes: string | any[] |
| | | nodeValue: string |
| | | attributes: { length: number; item: (arg0: number) => any } |
| | | } |
| | | | any, |
| | | ) => { |
| | | let obj: any = {} |
| | | // 如果是文档节点,遍历其子节点 |
| | | if (xml.nodeType === 1) { |
| | | // element node |
| | | // 处理子节点 |
| | | if (xml.childNodes.length > 0) { |
| | | for (let i = 0; i < xml.childNodes.length; i++) { |
| | | const item = xml.childNodes[i] |
| | | const nodeName = item.nodeName.replace('wpml:', '') |
| | | |
| | | // 检查是否是文本节点 |
| | | if (item.nodeType === 3) { |
| | | // text node |
| | | // 处理文本节点的内容 |
| | | if (item.nodeValue.trim() !== '') { |
| | | obj[nodeName] = item.nodeValue.trim() |
| | | } |
| | | } else if (item.nodeType === 1) { |
| | | // element node |
| | | // 递归调用处理子节点 |
| | | if (obj[nodeName] === undefined) { |
| | | obj[nodeName] = deepParse(item) |
| | | } else { |
| | | if (!obj[nodeName].push) { |
| | | const old = obj[nodeName] |
| | | obj[nodeName] = [] |
| | | obj[nodeName].push(old) |
| | | } |
| | | obj[nodeName].push(deepParse(item)) |
| | | } |
| | | } |
| | | xmlObj[childName].push(child.textContent) |
| | | } else { |
| | | const value = child.textContent?.split('\n').join('').split(' ').join('') |
| | | xmlObj[childName.replace('wpml:', '')] = value |
| | | } |
| | | } |
| | | } else if (xml.nodeType === 3) { |
| | | // text node |
| | | // 处理文本节点的内容 |
| | | obj = xml.nodeValue.trim() |
| | | } |
| | | return xmlObj |
| | | return obj |
| | | } |
| | | export const XMLToJSON = (xmlStr: string, tagName: string) => { |
| | | |
| | | /** |
| | | * @description: XML转JSON |
| | | * @param {string} xmlStr xml字符串 |
| | | * @return {*} string |
| | | */ |
| | | export const XMLToJSON = (xmlStr: string) => { |
| | | const parser = new DOMParser() |
| | | const xmlDoc = parser.parseFromString(xmlStr, 'text/xml') |
| | | const xmlObj = deepParse(xmlDoc, tagName) |
| | | delete xmlObj.parsererror |
| | | const xmlObj = deepParse(xmlDoc.documentElement) |
| | | return xmlObj |
| | | } |
| | | |
| | | // json转xml |
| | | export const JSONToXML = (JSONData = {}) => { |
| | | if (!JSONData) return '' |
| | | let res = '' |
| | | const JSONParse = (obj: { [x: string]: any }) => { |
| | | for (const key in obj) { |
| | | if (Array.isArray(obj[key])) { |
| | | obj[key].forEach((item: any, index: any) => { |
| | | let xmlTag = '' |
| | | if (noWmpl.includes(key)) { |
| | | xmlTag = `<${key} rowNum="${index}">${item}</${key}>` |
| | | /** |
| | | * @description: JSON转XML |
| | | * @param {any} obj 要转换的对象 |
| | | * @param {string} rootName xml根节点名称 |
| | | * @return {*} string |
| | | */ |
| | | export const JSONToXML = (obj: any, rootName?: string | undefined) => { |
| | | let xml = '' |
| | | |
| | | const buildXml = (obj: string | any[], rootName?: string | undefined) => { |
| | | let xml = '' |
| | | if (Array.isArray(obj)) { |
| | | obj.forEach((item) => { |
| | | xml += `<${rootName}>${buildXml(item)}</${rootName}>` |
| | | }) |
| | | } else if (typeof obj === 'object') { |
| | | Object.keys(obj).forEach((key) => { |
| | | if (key === '#text') { |
| | | xml += obj[key] |
| | | } else { |
| | | if (key === 'Placemark') { |
| | | xml += `${buildXml(obj[key], key)}` |
| | | } else { |
| | | xmlTag = `<wpml:${key} rowNum="${index}">${item}</wpml:${key}>` |
| | | const str = !noWmpl.includes(key) ? 'wpml:' : '' |
| | | xml += `<${str}${key}>${buildXml(obj[key], key)}</${str}${key}>` |
| | | } |
| | | res += xmlTag |
| | | }) |
| | | } else if (typeof obj[key] === 'object') { |
| | | res += (noWmpl.includes(key) ? `<${key}>` : `<wpml:${key}>`) |
| | | JSONParse(obj[key]) |
| | | res += (noWmpl.includes(key) ? `</${key}>` : `</wpml:${key}>`) |
| | | } else { |
| | | res += (noWmpl.includes(key) ? `<${key}>${obj[key] || ''}</${key}>` : `<wpml:${key}>${obj[key] || ''}</wpml:${key}>`) |
| | | } |
| | | } |
| | | }) |
| | | } else { |
| | | xml += obj |
| | | } |
| | | return xml |
| | | } |
| | | JSONParse(JSONData) |
| | | return res |
| | | |
| | | xml += ` |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.5"> |
| | | ${buildXml(obj, rootName)} |
| | | </kml> |
| | | ` |
| | | |
| | | return xml |
| | | } |
| | |
| | | import { ref, getCurrentInstance } from 'vue' |
| | | import { ref } from 'vue' |
| | | import { |
| | | analyzeKmzFile, |
| | | getKmlParams, |
| | | generateKmlFormat, |
| | | XMLToJSON, |
| | | JSONToXML |
| | | } from '/@/utils/cesium/kmz' |
| | |
| | | const kmlStr = ref('') |
| | | const kmzFile = ref<any>(null) |
| | | |
| | | const template = ref({ |
| | | const template = ref<any>({ |
| | | author: '', |
| | | createTime: '', |
| | | updateTime: '', |
| | |
| | | ], |
| | | }, |
| | | }) |
| | | const waylines = ref({ |
| | | const waylines = ref<any>({ |
| | | missionConfig: { |
| | | flyToWaylineMode: 'safely', |
| | | finishAction: 'goHome', |
| | |
| | | }, |
| | | }) |
| | | |
| | | // 点位参数 |
| | | 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 fileRes = await analyzeKmzFile(fileUrl) |
| | |
| | | return false |
| | | } |
| | | kmlStr.value = kmzRes |
| | | const tplXmlJson = XMLToJSON(kmzRes) |
| | | template.value = tplXmlJson.Document |
| | | return kmzRes |
| | | } |
| | | const create = () => { |
| | | const nowTime = new Date().getTime() |
| | | const str = ` |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.5"> |
| | | <Document> |
| | | <wpml:author>17304076412</wpml:author> |
| | | <wpml:createTime>${nowTime}</wpml:createTime> |
| | | <wpml:updateTime>${nowTime}</wpml:updateTime> |
| | | <wpml:missionConfig> |
| | | </wpml:missionConfig> |
| | | <Folder> |
| | | </Folder> |
| | | </Document> |
| | | </kml> |
| | | ` |
| | | kmlStr.value = str |
| | | } |
| | | const writePoint = (settingParmas: any = {}, place: string, pointNumber: number = 0) => { |
| | | 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', |
| | | }) || [''] |
| | | 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 edit = (settingParmas: any = {}, keyName?: string, isKeyWpml: boolean = false) => { |
| | | Object.keys(settingParmas).forEach((key: string) => { |
| | | const regxVal: any = getKmlParams(kmlStr.value, true, { |
| | | name: key, |
| | | findRegx: '([\\s\\S]*?)', |
| | | }) |
| | | const str = regxVal[1] |
| | | if (str) { |
| | | if (key === 'globalHeight') { |
| | | const regxVal: any = getKmlParams(kmlStr.value, true, { |
| | | name: 'height', |
| | | findRegx: '([\\s\\S]*?)', |
| | | mode: 'g', |
| | | }) |
| | | 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]*?)', |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | const replaceStr = generateKmlFormat({ [key]: settingParmas[key] }) |
| | | kmlStr.value = kmlStr.value.replace(regxVal[0], replaceStr) |
| | | } else { |
| | | console.warn('没有找到对应的属性') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | } |
| | | const save = () => { |
| | | console.log('存在问题') |
| | | } |
| | | |
| | | const generateXML = <T>(xmlJson: T) => { |
| | | return JSONToXML(xmlJson) |
| | | } |
| | | |
| | | // 将wpmz/template.kml解析传来json转换回去,重新复制以便于后续绘制 |
| | | watch(() => template.value, (newobj) => { |
| | | const xmlString = JSONToXML(newobj) |
| | | kmlStr.value = xmlString |
| | | }, { |
| | | deep: true |
| | | }) |
| | | |
| | | return { |
| | | init, |
| | | create, |
| | | // write, |
| | | writePoint, |
| | | edit, |
| | | save, |
| | | generateXML |
| | | } |
| | | } |
| | | |
| | | export { kmlStr, fileAuthor } |
| | | export { kmlStr, fileAuthor, template, waylines } |
| | | export default useKmzTsa |
| | |
| | | import ImageTrailMaterial from '/@/utils/cesium/ImageTrailMaterial' |
| | | import { ref } from 'vue' |
| | | import { cesiumOperation } from '/@/hooks/use-cesium-tsa' |
| | | import { template as xmlTemplate } from '/@/utils/cesium/use-kmz-tsa' |
| | | const { addPolyline, getEntityById } = cesiumOperation() |
| | | |
| | | const getResource = (name: string) => { |
| | |
| | | } |
| | | interface tragetPoint { |
| | | position: Cesium.Cartesian3 |
| | | isUseGlobalHeight: boolean, |
| | | isUseGlobalHeight: boolean |
| | | eventList: eventParmas[] |
| | | } |
| | | |
| | |
| | | // kml中的全部实体 |
| | | const kmlEntities = ref<Cesium.Entity[]>([]) |
| | | |
| | | const selectPointIndex = ref<number | null>(null) |
| | | |
| | | const useMapDraw = ( |
| | | dataSource: Cesium.DataSource | any, |
| | | entitiesList: Cesium.Entity[] | any, |
| | |
| | | btmStartPoint = Cesium.Cartesian3.fromDegrees( |
| | | Number(startPointPosition[1]), |
| | | Number(startPointPosition[0]), |
| | | Number(startPointPosition[2]) |
| | | Number(startPointPosition[2]), |
| | | ) |
| | | // 参考起飞点“海拔高度,与“参考起飞点”中的椭球高度对应 |
| | | // const posterHeightRegStr = getKmlParams(kmlRes, true, { |
| | |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new Cesium.Cartesian2(0, -40), |
| | | show: false |
| | | }) |
| | | // 广告牌隐藏 |
| | | // entity.label._show = false |
| | | // 修改点的信息 |
| | | entity.point = new Cesium.PointGraphics({ |
| | | pixelSize: 20, |
| | |
| | | } |
| | | } |
| | | |
| | | export { waylinePointsEvent, waylineDetails, kmlEntities } |
| | | export { waylinePointsEvent, waylineDetails, kmlEntities, selectPointIndex } |
| | | export default useMapDraw |
| | |
| | | vue(), |
| | | cesium(), |
| | | eslintPlugin({ |
| | | fix: true |
| | | fix: true, |
| | | }), |
| | | ViteComponents({ |
| | | customComponentResolvers: [AntDesignVueResolver()], |
| | | }), |
| | | viteSvgIcons({ |
| | | // 指定需要缓存的图标文件夹 |
| | | // 指定需要缓存的图标文件夹 |
| | | iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], |
| | | // 指定symbolId格式 |
| | | symbolId: 'icon-[dir]-[name]', |
| | |
| | | entry: path.resolve(__dirname, './src/main.ts'), // 入口文件 |
| | | localEnabled: command === 'serve', // serve开发环境下 |
| | | // enabled: command !== 'serve' || mode === 'test', // 打包环境下/发布测试包, |
| | | config: { // vconsole 配置项 |
| | | config: { |
| | | // vconsole 配置项 |
| | | maxLogNumber: 1000, |
| | | theme: 'light' |
| | | } |
| | | theme: 'light', |
| | | }, |
| | | }), |
| | | AutoImport({ |
| | | dts: 'types/auto-imports.d.ts', |
| | | imports: ['vue', 'vue-router'], |
| | | // 解决eslint报错问题 |
| | | eslintrc: { |
| | | // 这里先设置成true然后npm run dev 运行之后会生成 .eslintrc-auto-import.json 文件之后,在改为false |
| | | // 这里先设置成true然后npm run dev 运行之后会生成 .eslintrc-auto-import.json 文件之后,在改为false |
| | | enabled: false, |
| | | filepath: './.eslintrc-auto-import.json', // 生成的文件路径 |
| | | globalsPropValue: true, |
| | | }, |
| | | }), |
| | | PkgConfig(), |
| | | OptimizationPersist() |
| | | // [svgBuilder('./src/assets/icons/')] // All svg under src/icons/svg/ have been imported here, no need to import separately |
| | | OptimizationPersist(), |
| | | // [svgBuilder('./src/assets/icons/')] // All svg under src/icons/svg/ have been imported here, no need to import separately |
| | | ], |
| | | server: { |
| | | open: true, |
| | |
| | | [env.VITE_BASE_API]: { |
| | | // 代理请求之后的请求地址(你的真实接口地址) |
| | | target: env.VITE_API_URL, |
| | | rewrite: path => path.replace(new RegExp(`^${env.VITE_BASE_API}`), ''), |
| | | rewrite: (path) => path.replace(new RegExp(`^${env.VITE_BASE_API}`), ''), |
| | | // 跨域 |
| | | changeOrigin: true |
| | | } |
| | | } |
| | | changeOrigin: true, |
| | | }, |
| | | }, |
| | | }, |
| | | envDir: './env', |
| | | resolve: { |
| | | alias: [{ |
| | | // https://github.com/vitejs/vite/issues/279#issuecomment-635646269 |
| | | find: '/@', |
| | | replacement: path.resolve(__dirname, './src'), |
| | | } |
| | | ] |
| | | alias: [ |
| | | { |
| | | // https://github.com/vitejs/vite/issues/279#issuecomment-635646269 |
| | | find: '/@', |
| | | replacement: path.resolve(__dirname, './src'), |
| | | }, |
| | | ], |
| | | }, |
| | | css: { |
| | | preprocessorOptions: { |
| | | scss: { |
| | | // example : additionalData: `@import "./src/design/styles/variables";` |
| | | // dont need include file extend .scss |
| | | additionalData: '@import "./src/styles/variables";' |
| | | // example : additionalData: `@import "./src/design/styles/variables";` |
| | | // dont need include file extend .scss |
| | | additionalData: '@import "./src/styles/variables";', |
| | | }, |
| | | } |
| | | }, |
| | | }, |
| | | base: './', |
| | | build: { |
| | | target: ['es2015'], // 最低支持 es2015 |
| | | sourcemap: true |
| | | } |
| | | sourcemap: true, |
| | | }, |
| | | } |
| | | }) |
| | | // export default ({ command, mode }: ConfigEnv): UserConfigExport => defineConfig({ |