From fee18d19a1d961847cd09469a20fc8231c1df46c Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Thu, 10 Apr 2025 11:09:34 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
src/views/TaskManage/TaskIntermediateContent/TaskMap.vue | 542 ++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 488 insertions(+), 54 deletions(-)
diff --git a/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue b/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
index fe8cdf7..b360db9 100644
--- a/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
+++ b/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
@@ -1,5 +1,5 @@
<template>
- <div id="taskMap" />
+ <div id="taskMap"></div>
</template>
<script setup>
import * as Cesium from 'cesium'
@@ -12,9 +12,29 @@
import EndPointicon from '@/assets/images/EndPointicon.png';
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
import { getCenterPoint } from '@/utils/cesium/mapUtil'
+import { getWaylineByArea } from '@/api/home/task';
+import { useStore } from 'vuex';
-const props = defineProps(['wayLineFile'])
+const store = useStore();
+const userAreaPosition = computed(() => store.state.home.userAreaPosition);
+// 声明事件
+const emit = defineEmits(['clickPosition', 'saveWayline']);
+
+const props = defineProps({
+ wayLineFile: {
+ type: String,
+ default: ''
+ },
+ checkedTableData: {
+ type: Array,
+ default: () => []
+ },
+ waylineTypeTest: {
+ type: Number,
+ default: 3
+ }
+})
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
@@ -26,8 +46,18 @@
maximumLevel: 18,
tilingScheme: new AmapMercatorTilingScheme(),
credit: 'amap_SL',
-})
-let viewer = null
+});
+
+let viewer = null;
+let currentEntity = null;
+let connectLines = []; // 存储连接线实体
+let polygonPoints = []; // 存储多边形的点
+let polygonEntity = null; // 存储多边形实体
+let existingEntity = null; // 后端返回数据生成得面状线
+// 添加变量跟踪当前菜单
+let currentMenu = null;
+
+
const init = () => {
viewer = new Viewer('taskMap', {
terrain: Terrain.fromWorldTerrain(),
@@ -46,22 +76,214 @@
})
viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL)
viewer.scene.morphTo2D(0);
+ //设置默认点
+ const { longitude = 115.763819, latitude = 28.787374, height = 10 } = userAreaPosition.value || {};
+ viewer.camera.setView({
+ destination: Cartesian3.fromDegrees(longitude, latitude, height),
+ });
}
-// 渲染线和点
+// 单点左键点击事件
+const singlePointLeftClick = () => {
+ viewer.screenSpaceEventHandler.setInputAction((click) => {
+ if (props.waylineTypeTest !== 1) return
+ const cartesian = viewer.camera.pickEllipsoid(
+ click.position,
+ viewer.scene.globe.ellipsoid
+ );
+ if (cartesian) {
+ // 清除之前的实体
+ viewer.entities.removeAll();
+
+ // 添加新点
+ const point = viewer.entities.add({
+ position: cartesian,
+ point: {
+ pixelSize: 10,
+ color: Cesium.Color.WHITE
+ }
+ });
+
+ // 转换坐标
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
+ const longitude = Cesium.Math.toDegrees(cartographic.longitude);
+ const latitude = Cesium.Math.toDegrees(cartographic.latitude);
+
+ // 更新当前实体引用
+ currentEntity = point;
+
+ // 发送坐标
+ emit('clickPosition', { longitude, latitude });
+ }
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
+};
+
+// 智能规划航线
+const intelligentPlanning = () => {
+ // 添加点击事件监听
+ viewer.screenSpaceEventHandler.setInputAction((click) => {
+ if (props.waylineTypeTest !== 2) return
+ const cartesian = viewer.camera.pickEllipsoid(
+ click.position,
+ viewer.scene.globe.ellipsoid
+ );
+
+ if (cartesian) {
+ // 添加新点
+ const point = viewer.entities.add({
+ position: cartesian,
+ point: {
+ pixelSize: 10,
+ color: Cesium.Color.WHITE
+ }
+ });
+
+ // 存储点位
+ polygonPoints.push(cartesian);
+
+ // 当点击超过2个点时绘制多边形
+ if (polygonPoints.length > 2) {
+ // 移除旧的多边形
+ if (polygonEntity) {
+ viewer.entities.remove(polygonEntity);
+ }
+
+ // 创建新的多边形
+ polygonEntity = viewer.entities.add({
+ polygon: {
+ hierarchy: new Cesium.PolygonHierarchy(polygonPoints),
+ // material: new Cesium.Color.fromBytes(212, 46, 32, 100),
+ material: new Cesium.Color(0, 0.5, 1, 0.3), // 蓝色
+ outline: true,
+ // outlineColor: new Cesium.Color.fromBytes(212, 46, 32, 255),
+ outlineColor: new Cesium.Color(0, 0.5, 1, 1), // 蓝
+ outlineWidth: 2,
+ height: 0, // Set explicit height
+ heightReference: Cesium.HeightReference.NONE // Disable terrain clamping
+ }
+ });
+ }
+
+ // 转换坐标并发送
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
+ const longitude = Cesium.Math.toDegrees(cartographic.longitude);
+ const latitude = Cesium.Math.toDegrees(cartographic.latitude);
+ // emit('clickPosition', cartographic);
+
+ }
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
+ // 修改右键点击事件,添加菜单
+ viewer.screenSpaceEventHandler.setInputAction((movement) => {
+ if (props.waylineTypeTest !== 2) return
+ if (polygonPoints.length > 2) {
+ // 清除之前的菜单
+ if (currentMenu) {
+ document.body.querySelectorAll('.context-menu').forEach(menu => menu.remove());
+ }
+
+ const menuContainer = document.createElement('div');
+ menuContainer.className = 'context-menu';
+
+ // 获取地图容器
+ const mapContainer = document.getElementById('taskMap');
+ // 使用鼠标右键点击的实际位置
+ menuContainer.style.position = 'absolute';
+ menuContainer.style.left = `${movement.position.x}px`;
+ menuContainer.style.top = `${movement.position.y}px`;
+ menuContainer.style.zIndex = '1000';
+
+ menuContainer.innerHTML = `
+ <div class="menu-item" id="saveWayline">保存航线</div>
+ <div class="menu-item" id="cancelDraw">取消绘制</div>
+ `;
+
+ mapContainer.appendChild(menuContainer);
+ currentMenu = menuContainer;
+
+ // 添加全局点击事件监听
+ const handleClickOutside = (e) => {
+ if (!menuContainer) return;
+ const isClickInside = menuContainer.contains(e.target);
+ if (!isClickInside) {
+ menuContainer.remove();
+ document.removeEventListener('mousedown', handleClickOutside);
+ }
+ };
+
+ // 延迟添加事件监听,避免右键点击立即触发
+ setTimeout(() => {
+ document.addEventListener('mousedown', handleClickOutside);
+ }, 100);
+
+ // 菜单按钮点击事件
+ document.getElementById('saveWayline').onclick = () => {
+ const coordinates = polygonPoints.map(point => {
+ const cartographic = Cesium.Cartographic.fromCartesian(point);
+ return {
+ longitude: Cesium.Math.toDegrees(cartographic.longitude),
+ latitude: Cesium.Math.toDegrees(cartographic.latitude)
+ };
+ });
+ emit('saveWayline', coordinates);
+ saveWaylineByArea(coordinates);
+ mapContainer.removeChild(menuContainer);
+ };
+
+ document.getElementById('cancelDraw').onclick = () => {
+ polygonPoints = [];
+ viewer.entities.removeAll();
+ mapContainer.removeChild(menuContainer);
+ };
+ }
+ }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
+};
+
+// 保存航线并且获取线
+const saveWaylineByArea = (dataValue) => {
+ const polygonArray = dataValue.map(point => [point.longitude, point.latitude]);
+ getWaylineByArea({ type: 2, polygon: polygonArray }).then(res => {
+ if (res.data.code !== 0) retrun;
+ drawResultWayline(res.data.data);
+ });
+}
+
+// 绘制后端生成得面状线
+const drawResultWayline = (dataValue) => {
+ // 先检查并删除已存在的航线
+ existingEntity = viewer.entities.getById('result_wayline');
+ if (existingEntity) {
+ viewer.entities.remove(existingEntity);
+ }
+ const cartesian3List = ref([]);
+ dataValue.forEach((lnglat) => {
+ const cartesian3 = Cesium.Cartesian3.fromDegrees(
+ Number(lnglat.x),
+ Number(lnglat.y),
+ Number(100), // 默认100
+ )
+ cartesian3List.value.push(cartesian3)
+ });
+ const setting = {
+ id: 'result_wayline',
+ polyline: {
+ width: 2,
+ positions: cartesian3List.value,
+ material: Cesium.Color.CHARTREUSE,
+ },
+ }
+ existingEntity = viewer?.entities.add({
+ polyline: setting.polyline,
+ id: setting.id,
+ })
+};
+
+// 选中航线时调用 渲染线和点 type = 0
const renderingLine = lineObj => {
const positions = lineObj.Placemark.map(item => {
const [lon, lat] = item.Point.coordinates.split(',')
return Cartesian3.fromDegrees(Number(lon), Number(lat))
})
- // viewer.entities.add({
- // position: positions[0],
- // billboard: {
- // image: new Cesium.ConstantProperty(uavImg),
- // width: 24,
- // height: 24,
- // },
- // });
+
viewer.entities.add({
polyline: {
width: 4,
@@ -75,24 +297,26 @@
clampToGround: false,
},
})
- console.log('positions',positions)
+
positions.forEach((point, index) => {
let setting = {};
- if (index === 0) {
- //TODO
- } else if (index === 1) {
- setting = {
- position: point,
- id: `point_${index}`,
- billboard: {
- image: Startingpointicon,
- outlineWidth: 0,
- width: 20,
- height: 20,
- scale: 1.0,
- },
- }
- } else if (index === positions.length - 1) {
+ // if (index === 0) {
+ // //TODO
+ // }
+ // else if (index === 1) {
+ // setting = {
+ // position: point,
+ // id: `point_${index}`,
+ // billboard: {
+ // image: Startingpointicon,
+ // outlineWidth: 0,
+ // width: 20,
+ // height: 20,
+ // scale: 1.0,
+ // },
+ // }
+ // } else
+ if (index === positions.length - 1) {
setting = {
position: point,
id: `point_${index}`,
@@ -109,7 +333,7 @@
position: point,
id: `point_${index}`,
label: {
- text: `${index}`,
+ text: `${index+1}`,
font: 'bold 14px serif',
style: Cesium.LabelStyle.FILL,
verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中
@@ -144,25 +368,11 @@
})
}
-// const drawLine = () => {
-// console.log('尽量么');
-// // props.wayLineFile.way_lines.forEach(item => {
-// // let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile);
-// let prexUrl2 = ref('https://wrj.shuixiongit.com/minio/cloud-bucket/wayline/20241224/wayline_1735032173222.kmz');
-
-// analyzeKmzFile(`${prexUrl2.value}?_t=${new Date().getTime()}`).then(async res => {
-// const templateXML = await res.fileInfoObj['wpmz/template.kml']
-// const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
-// const templateXMLObj = removeTextKey(templateXMLJSON.Folder)
-// renderingLine(templateXMLObj)
-// })
-// // })
-// }
// 异步解析kmz文件
const analysis = async url => {
return new Promise(async resolve => {
- const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
+ const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`);
const templateXML = await res.fileInfoObj['wpmz/template.kml']
const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
const templateXMLObj = removeTextKey(templateXMLJSON.Folder)
@@ -174,37 +384,239 @@
const drawLine = async () => {
let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile);
const res = await analysis(prexUrl.value);
+ if (!res.Placemark.length) return
renderingLine(res);
const points = res.Placemark.map(item => item.Point.coordinates.split(','))
flyToPoints(points)
}
-watch(() => props.wayLineFile, async (newValue, oldValue) => {
- if(newValue){
- await removeMap();
- await init();
+// 选择航线时,根据选择机巢连线
+const selectLineFile = (newVal) => {
+ // 清除之前的实体
+ // viewer.entities.removeAll();
+ // 重新绘制航线
+ // if (props.wayLineFile) {
+ // drawLine();
+ // }
+ // 添加选中点和连接线
+ const positions = newVal.map(item => {
+ const position = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude));
+ // 添加机巢点
+ viewer.entities.add({
+ position: position,
+ billboard: {
+ image: new Cesium.ConstantProperty(uavImg),
+ width: 24,
+ height: 24,
+ },
+ });
+ return position;
+ });
+
+ // 添加连接线
+ if (positions.length > 1) {
+ viewer.entities.add({
+ polyline: {
+ positions: positions,
+ width: 2,
+ material: new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.RED,
+ dashLength: 8.0
+ })
+ }
+ });
+ }
+ // 飞到中心点
+ const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
+ flyToPoints(lngLatArr);
+};
+
+// 单个点生成选择多个机巢生成航线
+const singlePointLines = (newVal) => {
+ // 清除之前的连接线
+ connectLines.forEach(line => viewer.entities.remove(line));
+ connectLines = [];
+
+ if (currentEntity) {
+ // 获取当前点的位置
+ const currentPosition = currentEntity.position.getValue();
+
+ // 为每个选中的机巢创建点和连接线
+ newVal.forEach(item => {
+ // 创建机巢点
+ const nestPosition = Cartesian3.fromDegrees(
+ Number(item.longitude),
+ Number(item.latitude)
+ );
+ viewer.entities.add({
+ position: nestPosition,
+ billboard: {
+ image: new Cesium.ConstantProperty(uavImg),
+ width: 24,
+ height: 24,
+ },
+ });
+
+ // 创建连接线
+ const line = viewer.entities.add({
+ polyline: {
+ positions: [currentPosition, nestPosition],
+ width: 2,
+ material: new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.RED,
+ dashLength: 8.0
+ })
+ }
+ });
+ connectLines.push(line);
+ });
+
+ // 飞到所有点的中心位置
+ const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
+ flyToPoints(lngLatArr);
+ }
+};
+
+// 智慧规划航线-面状航线
+const planarPointsLines = (newVal) => {
+ // 如果存在面状航线
+ if (existingEntity) {
+ const waylinePositions = existingEntity.polyline.positions.getValue();
+
+ newVal.forEach(item => {
+ // 创建机巢点
+ const nestPosition = Cartesian3.fromDegrees(
+ Number(item.longitude),
+ Number(item.latitude)
+ );
+
+ // 添加机巢图标
+ viewer.entities.add({
+ position: nestPosition,
+ billboard: {
+ image: new Cesium.ConstantProperty(uavImg),
+ width: 24,
+ height: 24,
+ },
+ });
+
+ // 找到最近的航线点并连线
+ let minDistance = Number.MAX_VALUE;
+ let closestPosition = null;
+
+ waylinePositions.forEach(waylinePos => {
+ const distance = Cartesian3.distance(nestPosition, waylinePos);
+ if (distance < minDistance) {
+ minDistance = distance;
+ closestPosition = waylinePos;
+ }
+ });
+
+ // 创建连接线
+ if (closestPosition) {
+ const line = viewer.entities.add({
+ polyline: {
+ positions: [nestPosition, closestPosition],
+ width: 2,
+ material: new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.CHARTREUSE,
+ dashLength: 8.0
+ })
+ }
+ });
+ connectLines.push(line);
+ }
+ });
+
+ // 飞到所有点的中心位置
+ const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
+ flyToPoints(lngLatArr);
+ }
+};
+
+// 监听选择航线文件事件
+watch(() => props.wayLineFile, async (newVal, oldValue) => {
+ await removeMap();
+ if(newVal){
await drawLine();
}
-})
+}, { deep: true });
+
+// 监听表格选中数据变化
+watch(() => props.checkedTableData, (newVal) => {
+ if (newVal.length > 0 && props.waylineTypeTest === 0) {
+ selectLineFile(newVal);
+ } else if (newVal.length > 0 && props.waylineTypeTest === 1) {
+ singlePointLines(newVal);
+ } else if (newVal.length > 0 && props.waylineTypeTest === 2) {
+ planarPointsLines(newVal);
+ }
+}, { deep: true });
+
+// 监听航线类型
+watch(() => props.waylineTypeTest, async (newVal) => {
+ await removeMap();
+ if (newVal === 1) {
+ await singlePointLeftClick();
+ } else if (newVal === 2) {
+ await intelligentPlanning();
+ }
+}, { deep: true });
+
+const removeEvent = () => {
+ // 清除事件监听器
+ viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
+ viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
+};
const removeMap = () => {
- viewer.entities.removeAll()
- viewer.destroy()
+ // 清除所有实体
+ if (viewer) {
+ // 清除连接线
+ connectLines.forEach(line => viewer.entities.remove(line));
+ connectLines = [];
+
+ // 清除多边形点和实体
+ polygonPoints = [];
+ if (polygonEntity) {
+ viewer.entities.remove(polygonEntity);
+ }
+
+ // 清除当前实体和面状航线
+ if (currentEntity) {
+ viewer.entities.remove(currentEntity);
+ }
+ if (existingEntity) {
+ viewer.entities.remove(existingEntity);
+ }
+
+ viewer.entities.removeAll();
+
+ // 重置所有变量
+ currentEntity = null;
+ polygonEntity = null;
+ existingEntity = null;
+ }
}
onBeforeUnmount(() => {
- removeMap()
+ removeMap();
+ removeEvent();
+ // 移除所有实体并销毁viewer
+ viewer.destroy();
+ viewer = null;
})
onMounted(() => {
nextTick(() => {
init()
})
-})
+});
</script>
<style scoped lang="scss">
#taskMap {
+ position: relative;
height: 100%;
:deep() {
@@ -232,5 +644,27 @@
display: none;
}
}
+
+:deep(.context-menu) {
+ position: absolute;
+ background: rgba(0, 21, 41, 0.9);;
+ border-radius: 4px;
+ padding: 8px 0;
+ min-width: 120px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
+
+ .menu-item {
+ padding: 8px 16px;
+ color: #fff;
+ cursor: pointer;
+ transition: all 0.3s;
+ font-size: 14px;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.1);
+ }
+ }
+ }
}
+
</style>
--
Gitblit v1.9.3