You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
GCSGUI/src/components/toolbar.vue

475 lines
14 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!--
文件描述工具条
创建时间2024/4/16 10:54
创建人Zhaipeixiu
-->
<script setup>
import {ChevronBack,ChevronForward, CreateOutline, DuplicateSharp, EyeSharp, Layers, Settings,ReloadSharp} 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/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";
import RouteManageViewer from "@/assets/js/RouteManageViewer.js";
import RouteOptions from "@/components/RouteOptions.vue";
import {useRouteStore} from "@/store/RouteStore";
import MarkerDialog from "@/components/MarkerDialog.vue";
import { useRouter } from 'vue-router'
import {getUnicode} from "@/types/entityoptions.ts";
const router = useRouter()
const emit = defineEmits(['resizeMap'])
const message = useMessage();
let SceneValue;
let showModal = ref(false);
let showRouteModal = 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)
const markerDialog = 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 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') { // 绘制航线
showRouteModal.value = true;
}
else if(key === 'marker') { // 标记障碍物
markerDialog.value?.openDlg()
}
else{
// 转到航线管理页面
router.push({
name: 'routeManage',
})
}
}
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){
message.success('WebSocket已连接\n url:'+'ws://'+store.webskt.ws_config.address+':'+store.webskt.ws_config.port)
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 && !store.networkStatus.offlineMode){
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()
}
function refreshPage(){
window.location.reload()
}
/**
* 绘制航线,目前只绘制任务航线
* @param routeParams 航线属性type:{name: string,code: number,isOpen: bool,height: number,routePts: []}
*/
function startDrawRoute(routeParams) {
showRouteModal.value = false
let drawLine = new RouteManageViewer(window.viewer)
drawLine.routeParams.isClose =routeParams.isClose
drawLine.routeParams.height = routeParams.height
drawLine.start().then(result => {
// result是经纬度坐标数组需转换为 AirlinePoint数组
let AirlinePoints = []
result[0].forEach((pt,index) => {
let AirlinePoint = {lon:pt.lon, lat:pt.lat, alt:pt.alt, ch1:routeParams.isClose? 0:2, ch2:0x03, speed:0, nPt:index+1}
AirlinePoints.push(AirlinePoint)
})
AirlinePoints.at(-1).ch2 = 0x01;
// 航线加入store
let aRoute = {
code: routeParams.code, PtNum: result[0].length, isClose: routeParams.isClose, unicode: getUnicode(),
name: routeParams.name, points: AirlinePoints, totalDistance: 0
}
useRouteStore().addRoute(aRoute)
// 航线加入图层管理
lStore.addLayer(aRoute.name,undefined,undefined, result[1],true)
}).catch(reject => {
message.error(reject)
})
}
</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="connectWebSocket">
<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-tooltip placement="bottom" trigger="hover">
<template #trigger>
<n-button tertiary type="warning" @click="refreshPage">
<template #icon>
<n-icon><ReloadSharp/></n-icon>
</template>
</n-button>
</template>
<span> 刷新页面 </span>
</n-tooltip>
<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="closeWS" type="warning" size="small">关闭通信</n-button>
</n-space>
</div>
</n-modal>
<SpatialAnalysis ref="spatialAnalyse"></SpatialAnalysis>
<LayerManager ref="layerManager"></LayerManager>
<RouteOptions v-model:show="showRouteModal" @routeDraw="startDrawRoute" @cancelDraw="showRouteModal = false"/>
<MarkerDialog ref="markerDialog"></MarkerDialog>
</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>