diff --git a/src/assets/js/RouteManageViewer.js b/src/assets/js/RouteManageViewer.js new file mode 100644 index 0000000..2b6d3c0 --- /dev/null +++ b/src/assets/js/RouteManageViewer.js @@ -0,0 +1,168 @@ +import {DataSource} from "cesium"; +import * as Cesium from 'cesium' + + +export default class RouteManageViewer { + constructor(viewer, isClose, height) { + this.viewer = viewer + this.scene = viewer.scene + this.routeParams = { + isClose: isClose, + height: height, + } + this.positions = [] + this.temPositions = [] // 鼠标移动时产生的临时点 + this.vertexEntities = [] // 节点元素 + this.lineEntity = undefined // 折线元素 + this.lineEntitys = [] // 折线元素数组,每完成一次航线绘制,将折线元素加入此数组 + } + + // -------------------------------------------// + //开始绘制 + start() { + this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas) + this.viewer.scene.globe.depthTestAgainstTerrain = true + + //设置鼠标样式 + this.viewer._element.style.cursor = 'crosshair'; + this.viewer.enableCursorStyle = true; + this.temPositions = []; + this.positions = []; + return new Promise((resolve,reject) => { + //注册鼠标事件 + //单击鼠标左键画点点击事件 + this.handler.setInputAction(e => { + let position = this.viewer.scene.pickPosition(e.position); + if (!position) { + const ellipsoid = this.viewer.scene.globe.ellipsoid; + position = this.viewer.scene.camera.pickEllipsoid(e.position, ellipsoid); + } + //TODO: 海拔高度转Cartesain3 Z 值 + if (position){ + this.positions.push(position); + } + if (this.positions.length === 1) { //首次点击 + this.createLineEntity(); + } + this.createVertex(); + }, Cesium.ScreenSpaceEventType.LEFT_CLICK); + + this.handler.setInputAction(e => { + if (this.positions.length < 3) { + this.endDraw(); + this.clearDisEntity() + reject("航线不得少于三个航点!") + } + else { + this.lineEntity.polyline = { + positions: this.routeParams.isClose? this.positions.concat(this.positions[0]) : this.positions, + width: 2, + material: Cesium.Color.YELLOW, + depthFailMaterial: Cesium.Color.YELLOW + } + this.endDraw(); + resolve(this._Cartesian_degrees(this.positions)) + } + }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); + + this.handler.setInputAction(e => { + let position = this.viewer.scene.pickPosition(e.endPosition); + if (!position) { + position = this.viewer.scene.camera.pickEllipsoid(e.startPosition, this.viewer.scene.globe.ellipsoid); + } + if (this.positions.length >= 1){ + this.temPositions = this.positions.concat(position); + } + this.viewer.scene.requestRender() + }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); + }) + } + + //结束绘制 + endDraw() { + this.unRegisterEvents(); + this.viewer._element.style.cursor = 'default'; + this.viewer.enableCursorStyle = true; + this.lineEntitys.push(this.lineEntity) + + } + + //清空绘制 + clearDisEntity() { + //清除折线 + this.lineEntitys.forEach(item => { + this.viewer.entities.remove(item); + }); + //清除节点 + this.vertexEntities.forEach(item => { + this.viewer.entities.remove(item); + }); + + this.positions = [] + this.temPositions = [] // 鼠标移动时产生的临时点 + this.vertexEntities = [] // 节点元素 + this.lineEntity = undefined // 折线元素 + this.lineEntitys = [] // 折线元素数组,每完成一次航线绘制,将折线元素加入此数组 + } + + //创建线对象 + createLineEntity() { + this.lineEntity = this.viewer.entities.add({ + polyline: { + positions: new Cesium.CallbackProperty(e => { + return this.temPositions; + }, false), + width: 2, + material: Cesium.Color.YELLOW, + clampToGround: true, + heightReference:Cesium.HeightReference.CLAMP_TO_GROUND, + }, + }) + this.viewer.scene.requestRender() + } + //创建线节点 + createVertex() { + let vertexEntity = new Cesium.Entity({ + id: "Route" + this.positions[this.positions.length - 1], + position: this.positions[this.positions.length - 1], + point: { + color: Cesium.Color.FUCHSIA, + pixelSize: 8, + outlineColor: Cesium.Color.WHITE, + outlineWidth: 2, + disableDepthTestDistance:99000000, + heightReference:Cesium.HeightReference.CLAMP_TO_GROUND, + }, + }); + this.vertexEntities.push(vertexEntity) + this.viewer.entities.add(vertexEntity) + } + + //解除鼠标事件(测距) + unRegisterEvents() { + this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK); + this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); + this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE); + } + + /** + * 笛卡尔坐标数组转为经纬度坐标数组 + * @param cartesian3Arr 笛卡尔坐标数组 + * @returns {*[]} 经纬度坐标数组 + * @private + */ + _Cartesian_degrees(cartesian3Arr){ + let coords = [] + cartesian3Arr.forEach((item) => { + // 将 Cartesian3 转换为 Cartographic + let cartographic = Cesium.Cartographic.fromCartesian(item, Cesium.Ellipsoid.WGS84); + coords.push({ + lon: Cesium.Math.toDegrees(cartographic.longitude), + lat: Cesium.Math.toDegrees(cartographic.latitude), + alt: this.routeParams.height + }) + }) + return coords + } + +} \ No newline at end of file diff --git a/src/assets/js/measureDistance.js b/src/assets/js/measureViewer.js similarity index 97% rename from src/assets/js/measureDistance.js rename to src/assets/js/measureViewer.js index 7344228..a40afcd 100644 --- a/src/assets/js/measureDistance.js +++ b/src/assets/js/measureViewer.js @@ -2,9 +2,10 @@ import * as Cesium from 'cesium' import {Cartesian3} from 'cesium' import {useStaticStore} from "@/store/staticOptions.js"; import {useLayerStore} from "@/store/layerManagerStore.ts"; +import {ByDirectionAndLen} from "@/utils/map/geocomputation.ts"; const store = useStaticStore() let time = new Date() -export default class MeasureDistance { +export default class MeasureViewer { constructor(viewer) { this.viewer = viewer this.scene = viewer.scene @@ -33,6 +34,8 @@ export default class MeasureDistance { borderWidth: 1, material: Cesium.Color.GREEN.withAlpha(0.5), } + + this.barirsCollection = [] //机场障碍物 } profileAnalyse(){ @@ -219,7 +222,7 @@ export default class MeasureDistance { /** * Cesium实体导出Geojson,只导出实体中的 polyline polygon rectangle - * @param entity + * @param entity Cesium实体 * @return geojson Object */ entity2Geojson(entity){ @@ -603,7 +606,6 @@ export default class MeasureDistance { this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas) this.viewer.scene.globe.depthTestAgainstTerrain = true - this.registerEvents(); //注册鼠标事件 this.isMeasure = true; this.totalDistance = 0 // 总距离 //设置鼠标样式 @@ -611,6 +613,8 @@ export default class MeasureDistance { this.viewer.enableCursorStyle = true; this.temPositions = []; this.positions = []; + + this.registerEvents(); //注册鼠标事件 } //禁用测距 @@ -643,7 +647,6 @@ export default class MeasureDistance { this.totalDistance = 0 // 总距离 } - //创建线对象(测距) createLineEntity() { this.lineEntity = this.viewer.entities.add({ @@ -654,6 +657,7 @@ export default class MeasureDistance { width: 2, material: Cesium.Color.YELLOW, clampToGround: true, + heightReference:Cesium.HeightReference.CLAMP_TO_GROUND, }, }) this.viewer.scene.requestRender() @@ -1095,4 +1099,29 @@ export default class MeasureDistance { }, }) } + + addEntitiesByDirectionAndLen(DatumPt, markers){ + let datumCar = Cartesian3.fromDegrees(Number(DatumPt.lon),Number(DatumPt.lat)) + console.group() + console.log(markers) + markers.forEach(marker => { + let pt = ByDirectionAndLen(datumCar,Number(marker.direction),Number(marker.distance)) + + let entity = new Cesium.Entity({ + position: pt, + box: { + dimensions: new Cartesian3(20,30,Number(marker.alt)), + material: Cesium.Color.RED, + outline: true, + outlineColor: Cesium.Color.WHITE, + outlineWidth: 2, + disableDepthTestDistance:990000, + } + }) + this.barirsCollection.push(entity) + this.viewer.entities.add(entity) + }) + + return datumCar + } } diff --git a/src/assets/js/useBlockDialog.js b/src/assets/js/useBlockDialog.js new file mode 100644 index 0000000..e365bc2 --- /dev/null +++ b/src/assets/js/useBlockDialog.js @@ -0,0 +1,54 @@ +import { defineAsyncComponent, render, createVNode } from "vue"; +export class routeDialog { + constructor() { + this.component = defineAsyncComponent( + () => import("@/components/RouteOptions.vue")); + this.vnode = null; + this.node = null; + this.props = { + width: "40%", + height: "auto", + }; + } + installRouteDialog() { + if (!this.vnode) { + const dialog = createVNode(this.component, this.props); + const container = document.createElement('div'); + render(dialog, container); + this.vnode = dialog; + this.node = container.childNodes[0]; + document.body.appendChild(this.node); + } + } + /** + * + * @returns {Promise} + * @param points 经纬度坐标点数组 + */ + show(points) { + // 发送信号,显示窗口 + const event = new CustomEvent('route-dialog-show', { detail: + { show: true, pts: points } + }); + document.dispatchEvent(event); + return new Promise((resolve,reject) => { + document.addEventListener('route-dialog-confirm', event => { + if(event.detail==='cancel'){ + reject('cancel') + }else{ + resolve(event.detail); + } + }); + }) + } + } + const aDialog = new routeDialog(); + + /** + * 展示一个阻塞式对话框 + * @returns {Promise} + * @param point 经纬度坐标点数组 + */ + export async function showRouteDialog(point) { + return aDialog.show(point); + } \ No newline at end of file diff --git a/src/components/MarkerDialog.vue b/src/components/MarkerDialog.vue new file mode 100644 index 0000000..c6ad031 --- /dev/null +++ b/src/components/MarkerDialog.vue @@ -0,0 +1,131 @@ + + + + + \ No newline at end of file diff --git a/src/components/RouteOptions.vue b/src/components/RouteOptions.vue new file mode 100644 index 0000000..e4e06a5 --- /dev/null +++ b/src/components/RouteOptions.vue @@ -0,0 +1,79 @@ + + + + + + \ No newline at end of file diff --git a/src/components/map/LayerManager.vue b/src/components/map/LayerManager.vue index 71d7ec2..4e35c90 100644 --- a/src/components/map/LayerManager.vue +++ b/src/components/map/LayerManager.vue @@ -16,7 +16,8 @@ defineExpose({ let sidebar = document.querySelector('.LayerManagerSidebar'); if(sidebar?.classList.contains('open')){ //关闭侧边栏 sidebar?.classList.remove('open'); - }else { // 打开侧边栏 + } + else { // 打开侧边栏 sidebar?.classList.add('open'); } }, diff --git a/src/components/map/SceneViewer.vue b/src/components/map/SceneViewer.vue index f00d807..468e878 100644 --- a/src/components/map/SceneViewer.vue +++ b/src/components/map/SceneViewer.vue @@ -21,7 +21,7 @@ import { } from '@/utils/map/TDTProvider' import { initViewer, perfViewer, showNavigator } from '@/utils/map/sceneViewer' import { flyToChina } from '@/utils/map/camera' -import MeasureDistance from "@/assets/js/measureDistance.js"; +import MeasureViewer from "@/assets/js/measureViewer.js"; const viewerDivRef = ref() let viewer: Viewer @@ -57,7 +57,7 @@ onMounted(() => { // 挂载在window,供全局组件共享 window.viewer = viewer - window.measureViewer = new MeasureDistance(viewer); + window.measureViewer = new MeasureViewer(viewer); }) diff --git a/src/components/toolbar.vue b/src/components/toolbar.vue index c7d351a..4503985 100644 --- a/src/components/toolbar.vue +++ b/src/components/toolbar.vue @@ -18,12 +18,19 @@ 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, useRoute } from 'vue-router' +const router = useRouter() +const route = useRoute() 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(); @@ -33,6 +40,8 @@ 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; //飞机上一时点的坐标 @@ -45,7 +54,6 @@ function handleSceneSelect(key){ window.viewer.trackedEntity = window.viewer.entities.getById(store.uav.ModelIDinMap); } } -let layerValue = ref('layer1'); let barIsOpen = ref(true); function openCloseBar() { // 获取元素 @@ -58,12 +66,19 @@ function openCloseBar() { } barIsOpen.value = !barIsOpen.value } -function handleEditSelect(key) { - if(key === 'createLine') { +function handleEditSelect(key) { + if(key === 'createLine') { // 绘制航线 + showRouteModal.value = true; + } + else if(key === 'marker') { // 标记障碍物 + markerDialog.value?.openDlg() } else{ - // 航线管理页面 + // 转到航线管理页面 + router.push({ + name: 'Test', + }) } } function handleAnalyseSelect(key) { @@ -124,7 +139,6 @@ async function handleFile() { else if(file.name.toLowerCase().endsWith('geojson')||file.name.toLowerCase().endsWith('json')||file.name.toLowerCase().endsWith('topojson')) { window.measureViewer.addJson(file) } - } /** @@ -234,6 +248,15 @@ async function connectWebSocket() { }; } +/** + * 将航线信息发送给QT端(每次发送一条航线) + * @param route 航线,Airline类型 + */ +function sendRouteToQT(route){ + if(store.webskt.ws?.readyState == WebSocket.OPEN){ + store.webskt.ws.send(JSON.stringify(route)) + } +} //关闭动态碰撞检测功能 function shutDownDetec() { if(lStore.openDetect){ @@ -258,6 +281,33 @@ function manageLayer(){ layerManager.value?.open_closeSidebar() } +/** + * 绘制航线,目前只绘制任务航线 + * @param routeParams 航线属性 + */ +function startDrawRoute(routeParams /*{code: number,isOpen: bool,height: number,routePts: []}*/) { + showRouteModal.value = false + let drawLine = new RouteManageViewer(window.viewer, routeParams.isClose, routeParams.height) + drawLine.start().then(result => { + console.log(result) + // result是经纬度坐标数组,需转换为 AirlinePoint数组 + let AirlinePoints = [] + result.forEach((pt,index) => { + let AirlinePoint = {lon:pt.lon, lat:pt.lat, alt:pt.alt, ch1:routeParams.isClose? 2:0, ch2:0x03, speed:0, nPt:index} + AirlinePoints.push(AirlinePoint) + }) + AirlinePoints.at(-1).ch2 = 0x01; + // 航线加入store + let aRoute = { + code: routeParams.code, PtNum: result.length, isClose: routeParams.isClose, name: "", points: AirlinePoints, totalDistance: 0 + } + useRouteStore().route.push(aRoute) + // fixme: 航线加入图层管理 + + }).catch(reject => { + message.error(reject) + }) +}