<!-- 文件描述:工具条 创建时间:2024/4/16 10:54 创建人:Zhaipeixiu --> <script setup> import {ChevronBack,ChevronForward, CreateOutline, DuplicateSharp, EyeSharp, Layers, Settings} from '@vicons/ionicons5' import {RulerAlt} from '@vicons/carbon' import {TerrainSharp} from '@vicons/material' import {DrawPolygon} from '@vicons/fa' import {useMessage} from 'naive-ui' import {ref, defineEmits} from "vue"; import {useStaticStore} from "@/store/staticOptions.js"; import {dataProcess_fromQT, dataProcess_fromQT_route} from "@/assets/js/websocketProtocol.ts"; import SpatialAnalysis from "@/components/SpatialAnalysis.vue"; import LayerManager from "@/components/map/LayerManager.vue"; import CollisionDetection from "./CollisionDetection.vue" import {useLayerStore} from "@/store/layerManagerStore.ts"; import {Cartesian3} from "cesium"; import {ByDirectionAndLen, getDistance, getElevation} from "@/utils/map/geocomputation.ts"; const emit = defineEmits(['resizeMap']) const message = useMessage(); let SceneValue; let showModal = ref(false); let hasPlane = ref(false); let showDetection = ref(false); let store = useStaticStore(); let lStore = useLayerStore(); let groundHeight = ref(-1) let detectDivHeight = ref(25) const spatialAnalyse = ref(null) const layerManager = ref(null) const collisionDetection = ref(null) SceneValue = ref('untrace'); let frameCount = 0 let lastPos = undefined; //飞机上一时点的坐标 function handleSceneSelect(key){ if(!hasPlane.value) return; if(key === 'untrace') { window.measureViewer.setNoTrack() }else if(key === 'fallow') { window.viewer.trackedEntity = window.viewer.entities.getById(store.uav.ModelIDinMap); } } let layerValue = ref('layer1'); let barIsOpen = ref(true); function openCloseBar() { // 获取元素 let toolbar = document.querySelector('.panel-toolbar989834y34'); if(toolbar?.classList.contains('open')){ //关闭 toolbar?.classList.remove('open'); } else { // 打开 toolbar?.classList.add('open'); } barIsOpen.value = !barIsOpen.value } function handleEditSelect(key) { if(key === 'createLine') { } else{ // 航线管理页面 } } function handleAnalyseSelect(key) { if(key === 'visibility') { store.analysisVars.analysisType = 2 //弹出参数窗口 spatialAnalyse.value?.openParamsDialog() } if(key === 'profile') { store.analysisVars.analysisType = 1 //弹出参数窗口 spatialAnalyse.value?.openParamsDialog() } } function handleDrawSelect(key) { if(key === 'clear') { if(window.measureViewer.clearDraw()){ message.warning('无可清除图形') } } else{ window.measureViewer.drawGraphics(key) } } // 测量菜单选中事件 function handleSelect(key) { if(key === 'distance') { measure(); } else if(key === 'area') { measureArea() }else if(key === 'clear') { measureEnd() } } async function handleFile() { const [fileHandle] = await window?.showOpenFilePicker({ types: [ { description: "kml/json", accept: {"text/kml": ['.kml', '.kmz', '.json', '.geojson']} } ] }) // 获取文件File对象 const file = await fileHandle?.getFile() console.group("获取到的文件") console.log(fileHandle) console.log(file.name) if(file.name.toLowerCase().endsWith('kml')||file.name.toLowerCase().endsWith('kmz')){ window.measureViewer.addKml(file) } else if(file.name.toLowerCase().endsWith('geojson')||file.name.toLowerCase().endsWith('json')||file.name.toLowerCase().endsWith('topojson')) { window.measureViewer.addJson(file) } } /** * 多点距离测量 */ function measure(){ window.measureViewer.clearDisEntity() window.measureViewer.activate() } /** * 清除面积测量和多点距离测量 */ function measureEnd(){ if(window.measureViewer.vertexEntities.length>0 || window.measureViewer.activeShapePoints.length>0){ window.measureViewer.deactivate() window.measureViewer.stopAreaMeasure() window.measureViewer.clearDisEntity() window.measureViewer.clearAreaEntity() window.measureViewer.viewer.scene.requestRender() }else{ message.warning('无可清除元素') } } /** * 面积测量 */ function measureArea() { window.measureViewer.clearAreaEntity() window.measureViewer.activateAreaMeasure(); } /** * 连接websocket * @returns {Promise<void>} */ async function connectWebSocket() { store.webskt.ws = new WebSocket('ws://'+store.webskt.ws_config.address+':'+store.webskt.ws_config.port); store.webskt.ws.onopen = function(event){ console.log("Connection open ...") store.webskt.ws.send("hello QT!") } store.webskt.ws.onmessage = (event) => { //收到消息后的处理流程.... let sktData = JSON.parse(event.data) // console.log(sktData); frameCount++ if(sktData.type === 0){ let ycData = dataProcess_fromQT(sktData) if (ycData != null) { // 更新遥测数据(飞机位置) window.measureViewer.updateDynamicData(ycData) lStore.validYCData = true // 添加飞机三维图标 if(!hasPlane.value){ window.measureViewer.addAirplaneEntity(store.models.fp98, ycData.uavId + ycData.uavType) SceneValue.value = 'fallow' hasPlane.value = true; } // 加载和更新碰撞检测图(50帧更新一次) if(frameCount>50 && lStore.openDetect){ emit('resizeMap', detectDivHeight.value) showDetection.value = true frameCount = 0 let currentPos = Cartesian3.fromDegrees(ycData.lon, ycData.lat,ycData.alt) // 显示飞机当前位置的地表投影点海拔高度 和 飞机高度(折线图) new Promise(resolve => { // 计算对地高度 m groundHeight.value = ycData.alt - getElevation(window.viewer, currentPos) // 计算当前点与上一点的距离 米 if(lastPos===undefined){ lastPos = currentPos; } let dis = getDistance(currentPos, lastPos) * 1000 lastPos = currentPos resolve(dis) }).then(res=>{ collisionDetection.value.drawDetection(ycData.alt, res, ycData.alt-groundHeight.value) }) // 当前飞机高度(折线图) 和飞机前方10公里处的地形高度(以飞机当前航向推算) let max_dis = 5000 new Promise(resolve => { // 计算前方5公里处的坐标点 let detectPos = ByDirectionAndLen(currentPos, ycData.heading, max_dis) // 计算地形剖面 collisionDetection.value.drawTerrain(ycData.alt, currentPos, detectPos, max_dis) resolve(detectPos) }) } } else{ //遥测解析错误或无数据 lStore.validYCData = false } } if(sktData.type === 1){ let routeData = dataProcess_fromQT_route(sktData) if(routeData != null){ lStore.navi.airlines.push(routeData) lStore.navi.currentRouteID = routeData.code console.log(routeData) window.measureViewer.showAirLine(routeData) } } }; } //关闭动态碰撞检测功能 function shutDownDetec() { if(lStore.openDetect){ emit('resizeMap', -1) }else{ emit('resizeMap', detectDivHeight.value) } showDetection.value = !showDetection.value; lStore.openDetect = !lStore.openDetect } /** * 关闭websocket连接 */ function closeWS(){ if(store.webskt.ws){ store.webskt.ws.close(); } } function manageLayer(){ layerManager.value?.open_closeSidebar() } </script> <template> <n-space class="panel-toolbar989834y34"> <n-row justify-content="space-between"> <n-tooltip placement="bottom" trigger="hover" > <template #trigger> <n-button tertiary type="warning" @click="manageLayer"> <template #icon> <n-icon><Layers/></n-icon> </template> </n-button> </template> <span> 图层管理 </span> </n-tooltip> <n-tooltip placement="bottom" trigger="hover"> <template #trigger> <n-button tertiary circle type="warning" @click="handleFile"> <template #icon> <n-icon><DuplicateSharp /></n-icon> </template> </n-button> </template> <span> 添加数据 </span> </n-tooltip> <n-dropdown :options="store.menuOptions.AnalyzeOptions" @select="handleAnalyseSelect"> <n-button tertiary type="warning"> <template #icon> <n-icon><TerrainSharp/></n-icon> </template> </n-button> </n-dropdown> <n-dropdown :options="store.menuOptions.EditOptions" @select="handleEditSelect"> <n-button tertiary circle type="warning"> <!--航线绘制--> <template #icon> <n-icon><CreateOutline/></n-icon> </template> </n-button> </n-dropdown> <n-dropdown :options="store.menuOptions.MeasureOptions" @select="handleSelect"> <n-button tertiary type="warning"> <template #icon> <n-icon><RulerAlt/></n-icon> </template> </n-button> </n-dropdown> <n-dropdown :options="store.menuOptions.DrawOptions" @select="handleDrawSelect"> <n-button tertiary circle type="warning"> <template #icon> <n-icon><DrawPolygon/></n-icon> </template> </n-button> </n-dropdown> <n-tooltip placement="bottom" trigger="hover"> <template #trigger> <n-button tertiary type="warning" @click="showModal = true"> <template #icon> <n-icon><Settings/></n-icon> </template> </n-button> </template> <span> WebSocket配置 </span> </n-tooltip> <n-popselect v-model:value="SceneValue" :options="store.menuOptions.sceneOptions" @update:value="handleSceneSelect" size="medium"> <n-button tertiary circle type="warning"> <template #icon> <n-icon><EyeSharp/></n-icon> </template> </n-button> </n-popselect> <n-button tertiary circle type="warning" @click="openCloseBar"> <template #icon> <n-icon v-if="barIsOpen"><ChevronBack/></n-icon> <n-icon v-else><ChevronForward/></n-icon> </template> </n-button> </n-row> </n-space> <n-button id="bt_switch" size="small" :type="showDetection? 'error': 'success'" v-if="lStore.validYCData" @click="shutDownDetec" :style="{bottom:'0vh'}"> {{showDetection? '关闭图表': '显示图表'}} </n-button> <n-collapse-transition v-show="showDetection"> <n-flex id="detectionGraph" justify="center" :style="{height: detectDivHeight +'vh'}"> <CollisionDetection ref="collisionDetection" ></CollisionDetection> </n-flex> </n-collapse-transition> <n-modal v-model:show="showModal" style="width: 30%" :mask-closable="false" preset="dialog" title=""> <template #header> <div>WebsSocket配置</div> </template> <div style="margin: 2rem 2rem .5rem 1rem"> <n-space> <n-form ref="formRef" :model="store.webskt.ws_config" label-placement="left" label-width="auto" require-mark-placement="right-hanging"> <n-form-item label="服务器地址"> <n-input v-model:value="store.webskt.ws_config.address" placeholder="127.0.0.1"/> </n-form-item> <n-form-item label="端口号"> <n-input-number v-model:value="store.webskt.ws_config.port" placeholder=8000 /> </n-form-item> </n-form> </n-space> <n-space justify="center"> <n-button @click="connectWebSocket" type="primary" size="small">开启通信</n-button> <!-- <n-button @click=" sendMessage" type="primary" size="small">发消息</n-button>--> <n-button @click="closeWS" type="warning" size="small">关闭通信</n-button> </n-space> </div> </n-modal> <SpatialAnalysis ref="spatialAnalyse"></SpatialAnalysis> <LayerManager ref="layerManager"></LayerManager> </template> <style> .panel-toolbar989834y34{ position: absolute; top: 10px; left: 2px; border-radius: 7px; background: rgba(21, 21, 21, 0.94); transition: left 0.3s; } .panel-toolbar989834y34.open{ position: absolute; left: -20rem; } #bt_switch{ position: absolute; z-index: 2; } #detectionGraph{ position: absolute; bottom: -25vh; width: 100vW; border-radius: 7px; background: rgba(255, 255, 255, 0.8); } </style>