From bdb66cf55723eab8ce633a2babea25d315cab0d5 Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Wed, 09 Apr 2025 17:52:00 +0800
Subject: [PATCH] feat: 新建航线

---
 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