From 3a3cbd22dac6945039ba44e4ca14b1adaaaaa7ed Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Wed, 07 Jan 2026 11:04:45 +0800
Subject: [PATCH] feat:反无大屏基建

---
 applications/drone-command/src/utils/cesium/createRouteLine.js |  923 +++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 572 insertions(+), 351 deletions(-)

diff --git a/applications/drone-command/src/utils/cesium/createRouteLine.js b/applications/drone-command/src/utils/cesium/createRouteLine.js
index 2c8195e..6ec1c1f 100644
--- a/applications/drone-command/src/utils/cesium/createRouteLine.js
+++ b/applications/drone-command/src/utils/cesium/createRouteLine.js
@@ -2,17 +2,16 @@
 import { Decimal } from 'decimal.js'
 import _, { cloneDeep, throttle } from 'lodash'
 import { v4 as uuidv4 } from 'uuid'
-
 import { render } from 'vue'
-
+import { analysisPointLineKmz, handlePointListForKmz } from '@/views/newRoutePlan/Waypoint/pointWayLineUtils'
 import HistoryDronePopup from '@/components/DeviceJobDetails/HistoryDronePopup.vue'
 
-import { getNewPolygonData } from '@/views/RoutePlan/PlanarAirLine/planarRouteUtils'
+import {
+	getNewPolygonData,
+} from '@/views/newRoutePlan/routeUtils'
 
 import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
-
-import { ArrowLineMaterialProperty, LineTrailMaterial, PolylineGlowMaterial, } from '@/utils/cesium/Material'
-
+import { ArrowLineMaterialProperty, LineTrailMaterial, PolylineGlowMaterial } from '@/utils/cesium/Material'
 import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum'
 import { ElMessage } from 'element-plus'
 
@@ -23,61 +22,21 @@
 import aircraftGltf from '@/assets/gltf/aircraft.gltf'
 import newNumPoint from '@/assets/images/newStartPoint.png'
 
-import {
-	getPolyLine,
-} from '@/views/RoutePlan/PointAirLine/pointWayLineUtils'
+import { getPolyLine } from '@/views/newRoutePlan/Waypoint/pointWayLineUtils'
 import store from '@/store/index'
+import newStartPoint from '@/assets/images/newStartPoint.png'
+import newStartPointMore from '@/assets/images/newStartPointMore.png'
+import {
+	arrowLineMaterialProperty, arrowLineMaterialPropertyGray, arrowLineMaterialPropertyGrayT,
+	arrowLineMaterialPropertyGreen,
+	arrowLineMaterialPropertyOrange,
+} from '@/hooks/useMorePointLine/useMorePointLine'
 
-let arrowLineMaterialProperty = new ArrowLineMaterialProperty({
-	color: new Cesium.Color(128 / 255, 215 / 255, 255 / 255, 1),
-	directionColor: new Cesium.Color(1, 1, 1, 1),
-	outlineColor: new Cesium.Color(1, 1, 1, 1),
-	outlineWidth: 0,
-	speed: 5
-})
 
-let arrowLineMaterialPropertyOrange = new ArrowLineMaterialProperty({
-	color: new Cesium.Color(255 / 255, 185 / 255, 58 / 255, 1),
-	directionColor: new Cesium.Color(1, 1, 1, 1),
-	outlineColor: new Cesium.Color(1, 1, 1, 1),
-	outlineWidth: 0,
-	speed: 5,
-})
-
-let arrowLineMaterialPropertyGreen = new ArrowLineMaterialProperty({
-	color: new Cesium.Color(6 / 255, 217 / 255, 87 / 255, 1),
-	directionColor: new Cesium.Color(1, 1, 1, 1),
-	outlineColor: new Cesium.Color(1, 1, 1, 1),
-	outlineWidth: 0,
-	speed: 5,
-})
-
-// 记录index
-let indexLog = 0
-// 颜色
-function getLineColor(lineNumber) {
-	// 颜色顺序: 蓝(0), 黄(1), 绿(2)
-	const colors = [arrowLineMaterialProperty, arrowLineMaterialPropertyOrange, arrowLineMaterialPropertyGreen]
-	// 计算颜色索引 (从0开始)
-	const colorIndex = (lineNumber - 1) % 3
-	// 返回对应颜色
-	return colors[colorIndex]
-}
-
-let runningLineMaterial = new LineTrailMaterial({
-	color: Cesium.Color.fromCssColorString('#1FFF69'),
-	opacity: 1,
-	speed: 10,
-})
 let runningTextColor = Cesium.Color.fromCssColorString('#1FFF69')
 let runningTextOffset = new Cesium.Cartesian2(0, -32)
-
-let pendingLineMaterial = new PolylineGlowMaterial({
-	color: Cesium.Color.fromCssColorString('#FFB81D'),
-})
 let pendingTextColor = Cesium.Color.fromCssColorString('#FFB81D')
 let pendingTextOffset = new Cesium.Cartesian2(0, 32)
-
 let MapPopUpBox = HistoryDronePopup
 
 function getZoomFactor (camerasInfo, type) {
@@ -95,10 +54,108 @@
 
 	return zoom_factor
 }
+// 记录index
+let indexLog = 0
+// 颜色
+function getPendingExecutionLineColor (lineNumber) {
+	// 颜色顺序: 蓝(灰), 更灰
+	const colors = [arrowLineMaterialPropertyGray, arrowLineMaterialPropertyGrayT]
+	// 计算颜色索引 (从0开始)
+	const colorIndex = (lineNumber - 1) % 2
+	// 返回对应颜色
+	return colors[colorIndex]
+}
+
+function getLineColor (lineNumber) {
+	// 颜色顺序: 蓝(0), 黄(1), 绿(2)
+	const colors = [arrowLineMaterialProperty, arrowLineMaterialPropertyOrange, arrowLineMaterialPropertyGreen]
+	// 计算颜色索引 (从0开始)
+	const colorIndex = (lineNumber - 1) % 3
+	// 返回对应颜色
+	return colors[colorIndex]
+}
+
+function getBase64Image (imgUrl) {
+	const img = new Image()
+	const canvas = document.createElement('canvas')
+	const ctx = canvas.getContext('2d')
+
+	return new Promise(resolve => {
+		img.onload = function () {
+			canvas.width = img.width
+			canvas.height = img.height
+			ctx.drawImage(img, 0, 0)
+			const base64 = canvas.toDataURL('image/png')
+			resolve(base64)
+		}
+		img.src = imgUrl
+	})
+}
+async function createCombinedSVG (index, billboardImageUrl) {
+	const base64Image = await getBase64Image(billboardImageUrl)
+	const svg = `
+	  <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
+		<image href="${base64Image}" x="10" y="10" width="80" height="80"/>
+		<text x="50" y="54" font-size="26" fill="white" text-anchor="middle" dominant-baseline="middle">
+		  ${index + 1}
+		</text>
+	  </svg>
+	`
+	return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
+}
+
+function findHeightByCoordinates (data, targetLng, targetLat) {
+	// 展平二维数组,然后查找第一个匹配的点
+	const allPoints = data.flat()
+	const point = allPoints.find(point =>
+		point.longitude === targetLng && point.latitude === targetLat
+	)
+
+	return point ? point.height : null
+}
+
+function renderingMachineNest (options) {
+	const { viewer, dockTransformPosition, dockPosition, data } = options
+	viewer.entities.add({
+		name: 'work-drone-route-point-dock',
+		position: dockTransformPosition,
+		billboard: {
+			image: endingOnlineImg,
+			width: 50,
+			height: 50,
+			disableDepthTestDistance: Number.POSITIVE_INFINITY,
+		},
+		customData: {
+			data,
+		},
+	})
+	viewer.entities.add({
+		name: 'work-drone-route--point-polyline',
+		position: dockTransformPosition,
+		polyline: getPolyLine(viewer, { value: [dockPosition] }, 0),
+	})
+}
+
+function renderingStartingPoint (options) {
+	const { viewer, startPosition, data } = options
+	viewer.entities.add({
+		name: 'work-drone-route-point-start',
+		position: startPosition,
+		billboard: {
+			image: rwqfdImg,
+			width: 50,
+			height: 50,
+			disableDepthTestDistance: Number.POSITIVE_INFINITY,
+		},
+		customData: {
+			data,
+		},
+	})
+}
 
 export default class CreateRouteLine {
 	/**
-	 * 
+	 *
 	 * @param {*} options 参数
 	 */
 	constructor(options) {
@@ -119,10 +176,9 @@
 
 	/**
 	 * 初始化viewer
-	 * @param {*} viewer 
+	 * @param {*} viewer
 	 */
 	initCreateRoute (viewer) {
-		indexLog = 0
 		this.viewer = viewer
 	}
 
@@ -138,24 +194,27 @@
 			const currentWaypointIndex = output?.ext['current_waypoint_index']
 
 			this.updataMultiNestData(sn, {
-				currentWaypointIndex
+				currentWaypointIndex,
 			})
 		}
 	}
 
 	/**
-	 * 请求航线文件数据并解析处理
+	 * 请求航线文件数据并绘制解析处理
 	 * @param {*} data 包含文件url等信息
-	 * @param {*} dronePosition 无人机位置
-	 * @returns  
+	 * @param {*} dockPosition 无人机位置
+	 * @returns
 	 */
-	async parsingFiles (data, dronePosition = null, isShowDock = false, isShowPointBillboard = true) {
+	async parsingFiles (data, dockPosition = null, isShowDock = false, isShowPointBillboard = true) {
+		console.log(data,'888')
 		const { url, wayline_type, device_sn } = data
-
-		if (!url) return dronePosition != null ? {
-			dronePosition: [{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }]
-		} : {}
-
+		indexLog = 0
+		if (!url)
+			return dockPosition != null
+				? {
+					dronePosition: [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }],
+				}
+				: {}
 		this.updataMultiNestData(device_sn)
 
 		const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
@@ -178,16 +237,16 @@
 		const missionConfig = removeTextKey(waylinesXmlJson.missionConfig)
 		const waylinesXMLObj = removeTextKey(waylinesXmlJson.Folder)
 
-		if (templateType === "mapping3d") {
+		if (templateType === 'mapping3d') {
 			if (!waylinesXMLObj[0].Placemark.length) return ElMessage.error('没有航线点位')
 		} else {
 			if (!waylinesXMLObj.Placemark.length) return ElMessage.error('没有航线点位')
 		}
 
-		if ([2, 4, 5, 6, 7, 10].includes(Number(wayline_type))) {
-			if (templateType === "mapping3d") {
+		if ([2, 4, 5, 7, 10].includes(Number(wayline_type))) {
+			if (templateType === 'mapping3d') {
 				let morePolygonData = waylinesXMLObj.map(i => ({
-					data: i.Placemark
+					data: i.Placemark,
 				}))
 
 				let newPolygonData = getNewPolygonData(morePolygonData)
@@ -214,7 +273,7 @@
 					waylinesXMLObj[0],
 					missionConfig,
 					templateXmlJson,
-					dronePosition,
+					dockPosition,
 					data,
 					startPoint,
 					isShowDock,
@@ -227,7 +286,7 @@
 				waylinesXMLObj,
 				missionConfig,
 				templateXmlJson,
-				dronePosition,
+				dockPosition,
 				data,
 				startPoint,
 				isShowDock,
@@ -238,7 +297,7 @@
 			return this.drawPointRoute(
 				waylinesXMLObj,
 				missionConfig,
-				dronePosition,
+				dockPosition,
 				data,
 				startPoint,
 				isShowDock,
@@ -248,50 +307,231 @@
 		}
 	}
 
+	// 拆分航线
+	async splitWayLine (data, dronePosition = null, isShowDock = false, isShowPointBillboard = true) {
+		const {
+			pointPlacemark,
+			polygonList,
+			pointList,
+			templateType,
+			startPoint,
+			execute_height_mode,
+			auto_flight_speed,
+			take_off_security_height,
+			buffer_distance_meters,
+			missionConfig,
+			waylinesXMLObjR,
+		} = await analysisPointLineKmz(`${data.domain_url}${data.object_key}`)
+		const { positionArray, filePositions } = this.disposeData(
+			waylinesXMLObjR,
+			missionConfig,
+			null,
+			data,
+			startPoint,
+			0
+		)
+		let bigPointList = handlePointListForKmz({
+			placemark: pointPlacemark,
+			startPoint: startPoint,
+			execute_height_mode: execute_height_mode,
+			auto_flight_speed: auto_flight_speed,
+		})
+		// 路径线
+		let entityConfig
+		bigPointList.shift()
+		let flyPositions = bigPointList.map(i => [Number(i.longitude), Number(i.latitude), Number(i.height)])
+		// 渲染多个点
+		let firstTopPosition = null
+		let resultPosotions = []
+		await Promise.all(data.wayline_file_list.map(async (item, index) => {
+			// 小航线渲染线
+			const kmzUrl = import.meta.env.VITE_APP_AIRLINE_URL + item.object_key
+			const { pointList } = await analysisPointLineKmz(kmzUrl)
+			resultPosotions.push(pointList)
+		})).then(res => {
+			// 目的是区分大航线点属于哪条小航线
+			bigPointList.forEach(bigObj => {
+				let foundIndex = -1
+				// 遍历 resultPositions 查找匹配项
+				resultPosotions.some((subArray, subIndex) => {
+					const found = subArray.some(obj =>
+						obj.latitude === bigObj.latitude &&
+						obj.longitude === bigObj.longitude
+					)
+					if (found) {
+						foundIndex = subIndex
+						return true // 找到就停止搜索
+					}
+					return false
+				})
+				// 如果找到了,给 bigPointList 的对象添加标记
+				if (foundIndex !== -1) {
+					bigObj.log = foundIndex // 或 bigObj.index = foundIndex
+				}
+			})
+			console.log(bigPointList, '查看结果值') // 现在 bigPointList 会有 log 属性
+			bigPointList.map(async (i, index) => {
+				firstTopPosition = i
+				const position = Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(findHeightByCoordinates(resultPosotions, i.longitude, i.latitude)))
+				const combinedImage = await createCombinedSVG(index, i.log % 2 ? newStartPoint : newStartPointMore)
+
+				entityConfig = {
+					name: 'work-drone-route-point-split',
+					position: position,
+					billboard: {
+						disableDepthTestDistance: Number.POSITIVE_INFINITY,
+						image: combinedImage,
+						width: 50,
+						height: 50,
+						verticalOrigin: Cesium.VerticalOrigin.CENTER,
+						horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
+					}
+				}
+
+				// 只在第一个点添加 label
+				if (index === 0) {
+					entityConfig.label = {
+						text: data.task_name || '',
+						font: 'bold 16px sans-serif',
+						fillColor: data.status === 2 ? runningTextColor : pendingTextColor,
+						pixelOffset: data.status === 2 ? runningTextOffset : pendingTextOffset,
+						style: Cesium.LabelStyle.FILL_AND_OUTLINE,
+						outlineWidth: 2,
+						outlineColor: Cesium.Color.BLACK,
+						disableDepthTestDistance: Number.POSITIVE_INFINITY,
+					}
+				}
+
+				this.viewer.entities.add(entityConfig)
+			})
+		})
+
+		// 渲染航线
+		data.wayline_file_list.map(async (item, index) => {
+			// 小航线渲染线
+			const kmzUrl = import.meta.env.VITE_APP_AIRLINE_URL + item.object_key
+			const { pointList } = await analysisPointLineKmz(kmzUrl)
+			// if (curRouteLineData.value.droneData && splitLines.length === 1) {
+			// 	// 把pointList的第一个点替换为无人机位置
+			// 	pointList[0] = curRouteLineData.value.droneData[0]
+			// }
+			if (take_off_security_height + pointList[0].height > firstTopPosition.height) {
+				// 安全高度+机巢高度 > 第一个点高度
+				const positionTest = {
+					longitude: pointList[0].longitude,
+					latitude: pointList[0].latitude,
+					height: take_off_security_height + pointList[0].height,
+				}
+
+				const positionTest1 = {
+					longitude: pointList[1].longitude,
+					latitude: pointList[1].latitude,
+					height: take_off_security_height + pointList[0].height,
+				}
+				pointList.splice(1, 0, positionTest)
+				// 往数组中插入元素
+				pointList.splice(2, 0, positionTest1)
+				// console.log(pointList, 'pointList')
+			} else if (take_off_security_height + pointList[0].height < firstTopPosition.height) {
+				const positionTest = {
+					longitude: pointList[0].longitude,
+					latitude: pointList[0].latitude,
+					height: firstTopPosition.height,
+				}
+				pointList.splice(1, 0, positionTest)
+			}
+			const routePositions = pointList.map(i =>
+				Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(i.height))
+			)
+
+			this.viewer.entities.add({
+				name: 'work-drone-route-line-split',
+				polyline: {
+					width: 4,
+					positions: routePositions,
+					material: data.status === 1 ? arrowLineMaterialPropertyGray : getLineColor(index + 1),
+					clampToGround: false,
+				},
+			})
+		})
+			// 落点线
+			; (bigPointList || [])?.forEach((item, index) => {
+				const topPosition = Cesium.Cartesian3.fromDegrees(
+					Number(item.longitude),
+					Number(item.latitude),
+					Number(item.height)
+				)
+				let setting = {
+					name: 'work-drone-route--planar-polyline-split',
+					position: topPosition,
+					polyline: getPolyLine(this.viewer, { value: bigPointList }, index),
+				}
+				this.viewer.entities.add(setting)
+			})
+		return {
+			entity: entityConfig,
+			routeLinePositions: positionArray,
+			filePositions:
+				dronePosition != null
+					? [{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }, ...filePositions]
+					: filePositions,
+		}
+	}
+
 	/**
 	 * 绘制面状航线
 	 * @param {*} lineObj 航线文件中的
 	 * @param {*} missionConfig 航线文件中的
 	 * @param {*} templateXmlJson 航线文件中的
-	 * @param {*} dronePosition 无人机位置
-	 * @param {*} data 
-	 * @param {*} startPoint 
-	 * @param {*} isShowPointBillboard 是否显示,航线起飞点,终点等标注
-	 * @returns 
+	 * @param {*} dockPosition 无人机位置
+	 * @param {*} data
+	 * @param {*} startPoint
+	 * @returns
 	 */
-	async drawPlanarRoute (lineObj, missionConfig, templateXmlJson, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type) {
-		const { positionArray, filePositions } = this.disposeData(lineObj, missionConfig, dronePosition, data, startPoint, wayline_type)
+	async drawPlanarRoute (
+		lineObj,
+		missionConfig,
+		templateXmlJson,
+		dockPosition,
+		data,
+		startPoint,
+		isShowDock,
+		isShowPointBillboard,
+		wayline_type
+	) {
+		const { positionArray, filePositions } = this.disposeData(
+			lineObj,
+			missionConfig,
+			dockPosition,
+			data,
+			startPoint,
+			wayline_type
+		)
 		const { device_sn } = data
 
-		const droneTransformPosition = Cesium.Cartesian3.fromDegrees(
-			Number(dronePosition.longitude),
-			Number(dronePosition.latitude),
-			Number(dronePosition.height),
+		const dockTransformPosition = Cesium.Cartesian3.fromDegrees(
+			Number(dockPosition.longitude),
+			Number(dockPosition.latitude),
+			Number(dockPosition.height)
 		)
 
 		let coordArr = null
 
 		const placemark = templateXmlJson.Folder?.Placemark
 		// 取出点位
-		const coordinates =
-			placemark.Polygon?.outerBoundaryIs.LinearRing.coordinates?.[
-				'#text'
-			]?.split('\n') || []
-
+		const coordinates = placemark.Polygon?.outerBoundaryIs.LinearRing.coordinates?.['#text']?.split('\n') || []
 		// 数组转换
-		coordArr = coordinates.map((coordinate) =>
+		coordArr = coordinates.map(coordinate =>
 			coordinate
 				.replace(/\s+/g, '')
 				.split(',')
-				.map((v) => Number(v)),
+				.map(v => Number(v))
 		)
 
 		// 获取当前经纬度绝对高度
-		let newCoordArr = coordArr.map(item => ([item[0], item[1], 0]))
-
+		let newCoordArr = coordArr.map(item => [item[0], item[1], 0])
 		let planarRouteEntity = this.viewer.entities.add({
 			name: 'work-drone-route-planar-polyline',
-
 			polyline: {
 				width: 3,
 				positions: positionArray,
@@ -300,30 +540,30 @@
 			},
 
 			customData: {
-				data
-			}
+				data,
+			},
 		})
 
 		let droppointPositions = (filePositions || [])?.map(i => ({
 			longitude: Number(i.lng),
 			latitude: Number(i.lat),
 			height: Number(i.alt),
-		}));
+		}))
 
-		// 落点线
-		(droppointPositions || [])?.forEach((item, index) => {
-			const topPosition = Cesium.Cartesian3.fromDegrees(
-				Number(item.longitude),
-				Number(item.latitude),
-				Number(item.height)
-			)
-			let setting = {
-				name: 'work-drone-route-planar-polyline',
-				position: topPosition,
-				polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
-			}
-			this.viewer.entities.add(setting)
-		})
+			// 落点线
+			; (droppointPositions || [])?.forEach((item, index) => {
+				const topPosition = Cesium.Cartesian3.fromDegrees(
+					Number(item.longitude),
+					Number(item.latitude),
+					Number(item.height)
+				)
+				let setting = {
+					name: 'work-drone-route--planar-polyline',
+					position: topPosition,
+					polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
+				}
+				this.viewer.entities.add(setting)
+			})
 
 		// 面状航线第一个点突出展示
 		isShowPointBillboard && this.startingIncreasePoint(positionArray)
@@ -340,9 +580,7 @@
 			name: 'work-drone-route-planar-border',
 
 			polyline: {
-				positions: Cesium.Cartesian3.fromDegreesArrayHeights(
-					cloneCoordArr.flat(),
-				),
+				positions: Cesium.Cartesian3.fromDegreesArrayHeights(cloneCoordArr.flat()),
 				width: 4,
 				material: new Cesium.PolylineOutlineMaterialProperty({
 					color: new Cesium.Color.fromBytes(74, 138, 233),
@@ -357,11 +595,8 @@
 		// 绘制面状地块--------------------
 		this.viewer.entities.add({
 			name: 'work-drone-route-planar-polygon',
-
 			polygon: {
-				hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(
-					newCoordArr.flat(),
-				),
+				hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(newCoordArr.flat()),
 				material: new Cesium.Color.fromBytes(75, 159, 221, 100),
 				outline: true,
 				outlineColor: new Cesium.Color.fromBytes(35, 85, 216, 255),
@@ -384,7 +619,7 @@
 
 				label: {
 					text: data.job_name || '',
-					font: 'bold 16px YouSheBiaoTiHei',
+					font: 'bold 16px sans-serif',
 					fillColor: Cesium.Color.WHITE,
 					pixelOffset: new Cesium.Cartesian2(0, -50),
 					style: Cesium.LabelStyle.FILL_AND_OUTLINE,
@@ -395,64 +630,31 @@
 			})
 		} else {
 			// 显示机巢位置
-			if (isShowDock) {
-				// 起点
-				this.viewer.entities.add({
-					name: 'work-drone-route-point-dock',
-
-					position: droneTransformPosition,
-
-					billboard: {
-						image: endingOnlineImg,
-						width: 50,
-						height: 50,
-						disableDepthTestDistance: Number.POSITIVE_INFINITY,
-					},
-
-					customData: {
-						data
-					}
-				})
-
-				this.viewer.entities.add({
-					name: 'work-drone-route--point-polyline',
-					position: droneTransformPosition,
-					polyline: getPolyLine(this.viewer, { value: [dronePosition] }, 0),
-				})
-			}
-
-			const startPosition = Cesium.Cartesian3.fromDegrees(
-				Number(filePositions[0].lng),
-				Number(filePositions[0].lat),
-				Number(filePositions[0].alt)
-			)
-
+			isShowDock && renderingMachineNest({
+				viewer: this.viewer,
+				dockTransformPosition,
+				dockPosition,
+				data
+			})
 			// 起点
-			isShowPointBillboard && this.viewer.entities.add({
-				name: 'work-drone-route-point-start',
-
-				position: startPosition,
-
-				billboard: {
-					image: rwqfdImg,
-					width: 50,
-					height: 50,
-					disableDepthTestDistance: Number.POSITIVE_INFINITY,
-				},
-
-				customData: {
-					data
-				}
+			isShowPointBillboard && renderingStartingPoint({
+				viewer: this.viewer,
+				startPosition: Cesium.Cartesian3.fromDegrees(
+					Number(filePositions[0].lng),
+					Number(filePositions[0].lat),
+					Number(filePositions[0].alt)
+				),
+				data
 			})
 		}
 
 		return {
 			entity: planarRouteEntity,
 			routeLinePositions: positionArray,
-			filePositions: dronePosition != null ? [
-				{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height },
-				...filePositions
-			] : filePositions
+			filePositions:
+				dockPosition != null
+					? [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }, ...filePositions]
+					: filePositions,
 		}
 	}
 
@@ -472,9 +674,7 @@
 
 		let setting = {
 			name: 'work-drone-route-planar-start',
-
 			position: positions,
-
 			billboard: {
 				image: this.planarBillboard('#2D8CF0'),
 				pixelOffset: new Cesium.Cartesian2(146, 72),
@@ -489,35 +689,48 @@
 	 * 绘制点航线
 	 * @param {*} lineObj 航线文件中的
 	 * @param {*} missionConfig 航线文件中的
-	 * @param {*} dronePosition 无人机位置
+	 * @param {*} dockPosition 无人机位置
 	 * @param {*} data 任务信息,航线地址等
-	 * @param {*} startPoint 
+	 * @param {*} startPoint
 	 * @param {*} isShowDock 是否显示机巢位置
-	 * @param {*} isShowPointBillboard 是否显示,航线起飞点,终点等标注
-	 * @returns 
+	 * @returns
 	 */
-	async drawPointRoute (lineObj, missionConfig, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type) {
-		const { positionArray, filePositions } = this.disposeData(lineObj, missionConfig, dronePosition, data, startPoint, wayline_type)
-		
-		const droneTransformPosition = Cesium.Cartesian3.fromDegrees(
-			Number(dronePosition.longitude),
-			Number(dronePosition.latitude),
-			Number(dronePosition.height),
+	async drawPointRoute (
+		lineObj,
+		missionConfig,
+		dockPosition,
+		data,
+		startPoint,
+		isShowDock,
+		isShowPointBillboard,
+		wayline_type
+	) {
+		const { positionArray, filePositions } = this.disposeData(
+			lineObj,
+			missionConfig,
+			dockPosition,
+			data,
+			startPoint,
+			wayline_type
 		)
 
+		const dockTransformPosition = Cesium.Cartesian3.fromDegrees(
+			Number(dockPosition.longitude),
+			Number(dockPosition.latitude),
+			Number(dockPosition.height)
+		)
 		// 路径线
 		let polyline
-		
+
+		indexLog = indexLog + 1
 
 		if (this.type === 'clusterScheduling') {
 			filePositions.forEach((item, index) => {
 				let position = Cesium.Cartesian3.fromDegrees(item.lng, item.lat, item.alt)
-
 				if (index === 0) {
 					this.viewer.entities.add({
 						name: 'work-drone-route-point-start',
 						position,
-
 						billboard: {
 							image: rwqfdImg,
 							outlineWidth: 0,
@@ -525,10 +738,9 @@
 							height: 50,
 							disableDepthTestDistance: Number.POSITIVE_INFINITY,
 						},
-
 						label: {
 							text: data.job_name || '',
-							font: 'bold 16px YouSheBiaoTiHei',
+							font: 'bold 16px sans-serif',
 							fillColor: data.type === 2 ? runningTextColor : pendingTextColor,
 							pixelOffset: data.type === 2 ? runningTextOffset : pendingTextOffset,
 							style: Cesium.LabelStyle.FILL_AND_OUTLINE,
@@ -540,9 +752,7 @@
 				} else if (index === filePositions.length - 1) {
 					this.viewer.entities.add({
 						name: 'work-drone-route-point-end',
-
 						position,
-
 						billboard: {
 							image: newEndPointImg,
 							outlineWidth: 0,
@@ -556,9 +766,7 @@
 				} else {
 					this.viewer.entities.add({
 						name: 'work-drone-route-point-approach',
-
 						position,
-
 						billboard: {
 							image: newNumPoint,
 							width: 50,
@@ -577,96 +785,56 @@
 					})
 				}
 			})
-
 			polyline = this.viewer.entities.add({
 				name: 'work-drone-route-point-polyline',
-
 				polyline: {
 					width: 4,
 					positions: positionArray,
-					material: data.type === 2 ? runningLineMaterial : pendingLineMaterial,
+					material: data.type === 2 ? getLineColor(indexLog) : arrowLineMaterialPropertyGray,
 					clampToGround: false,
-				}
+				},
 			})
 		} else {
 			// 显示机巢位置
-			if (isShowDock) {
-				// 起点
-				this.viewer.entities.add({
-					name: 'work-drone-route-point-dock',
-
-					position: droneTransformPosition,
-
-					billboard: {
-						image: endingOnlineImg,
-						width: 50,
-						height: 50,
-						disableDepthTestDistance: Number.POSITIVE_INFINITY,
-					},
-
-					customData: {
-						data
-					}
-				})
-
-				this.viewer.entities.add({
-					name: 'work-drone-route--point-polyline',
-					position: droneTransformPosition,
-					polyline: getPolyLine(this.viewer, { value: [dronePosition] }, 0),
-				})
-			}
-
-			const startPosition = Cesium.Cartesian3.fromDegrees(
-				Number(filePositions[0].lng),
-				Number(filePositions[0].lat),
-				Number(filePositions[0].alt)
-			)
-
+			isShowDock && renderingMachineNest({
+				viewer: this.viewer,
+				dockTransformPosition,
+				dockPosition
+			})
 			// 起点
-			isShowPointBillboard && this.viewer.entities.add({
-				name: 'work-drone-route-point-start',
-
-				position: startPosition,
-
-				billboard: {
-					image: rwqfdImg,
-					width: 50,
-					height: 50,
-					disableDepthTestDistance: Number.POSITIVE_INFINITY,
-				},
-
-				customData: {
-					data
-				}
+			isShowPointBillboard && renderingStartingPoint({
+				viewer: this.viewer,
+				startPosition: Cesium.Cartesian3.fromDegrees(
+					Number(filePositions[0].lng),
+					Number(filePositions[0].lat),
+					Number(filePositions[0].alt)
+				),
+				data
 			})
-
 			// 终点
-			isShowPointBillboard && this.viewer.entities.add({
-				name: 'work-drone-route-point-end',
-
-				position: positionArray[positionArray.length - 1],
-
-				billboard: {
-					image: new Cesium.ConstantProperty(endPointImg),
-					width: 30,
-					height: 30,
-					verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
-				},
-			})
-
+			isShowPointBillboard &&
+				this.viewer.entities.add({
+					name: 'work-drone-route-point-end',
+					position: positionArray[positionArray.length - 1],
+					billboard: {
+						image: new Cesium.ConstantProperty(endPointImg),
+						width: 30,
+						height: 30,
+						verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
+					},
+				})
+			console.log(data.status, '失败')
 			polyline = this.viewer.entities.add({
 				name: 'work-drone-route-point-polyline',
-
 				polyline: {
 					width: 4,
 					positions: positionArray,
-					material: getLineColor(indexLog),
+					material: data.status === 5 ? arrowLineMaterialPropertyGray : getLineColor(indexLog),
 					clampToGround: false,
 				},
-
 				customData: {
-					data
-				}
+					data,
+				},
 			})
 		}
 
@@ -674,47 +842,45 @@
 			longitude: Number(i.lng),
 			latitude: Number(i.lat),
 			height: Number(i.alt),
-		}));
+		}))
 
-		// 落点线
-		(droppointPositions || [])?.forEach((item, index) => {
-			const topPosition = Cesium.Cartesian3.fromDegrees(
-				Number(item.longitude),
-				Number(item.latitude),
-				Number(item.height)
-			)
+			// 落点线
+			; (droppointPositions || [])?.forEach((item, index) => {
+				const topPosition = Cesium.Cartesian3.fromDegrees(
+					Number(item.longitude),
+					Number(item.latitude),
+					Number(item.height)
+				)
 
-			let setting = {
-				name: 'work-drone-route-point-polyline',
-				position: topPosition,
-				polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
-			}
-			this.viewer.entities.add(setting)
-		})
+				let setting = {
+					name: 'work-drone-route--point-polyline',
+					position: topPosition,
+					polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
+				}
+				this.viewer.entities.add(setting)
+			})
 
 		return {
 			entity: polyline,
 			routeLinePositions: positionArray,
-			filePositions: dronePosition != null ? [
-				{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height },
-				...filePositions
-			] : filePositions,
+			filePositions:
+				dockPosition !== null
+					? [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }, ...filePositions]
+					: filePositions,
 		}
 	}
 
 	/**
 	 * 通用处理数据
-	 * @param {*} lineObj 
-	 * @param {*} missionConfig 
-	 * @param {*} dronePosition 
-	 * @param {*} startPoint 
+	 * @param {*} lineObj
+	 * @param {*} missionConfig
+	 * @param {*} dronePosition
+	 * @param {*} startPoint
 	 * @returns {} {positionArray: 带拼接点位置, filePositions:光航线点位置}
 	 */
 	disposeData (lineObj, missionConfig, dronePosition, data, startPoint, wayline_type) {
-		indexLog = indexLog + 1
 		const { device_sn } = data
-
-		const executeHeightMode = lineObj.executeHeightMode === "WGS84"
+		const executeHeightMode = lineObj.executeHeightMode === 'WGS84'
 
 		let filePositions = lineObj.Placemark.map(item => {
 			const [lng, lat] = item.Point.coordinates.split(',')
@@ -730,12 +896,14 @@
 		let safetyHeight
 
 		if (executeHeightMode) {
-			let startHeight = new Decimal(missionConfig.takeOffSecurityHeight)
-				.plus(dronePosition.height).toNumber()
+			let startHeight = new Decimal(missionConfig.takeOffSecurityHeight).plus(dronePosition?.height || 0).toNumber()
 
 			safetyHeight = startHeight < filePositions[0].alt ? filePositions[0].alt : startHeight
 		} else {
-			safetyHeight = missionConfig.takeOffSecurityHeight < filePositions[0].alt ? filePositions[0].alt : missionConfig.takeOffSecurityHeight
+			safetyHeight =
+				missionConfig.takeOffSecurityHeight < filePositions[0].alt
+					? filePositions[0].alt
+					: missionConfig.takeOffSecurityHeight
 		}
 
 		let safetyPositions = []
@@ -773,28 +941,38 @@
 		})
 
 		this.updataMultiNestData(device_sn, {
-			currentRoutePositions
+			currentRoutePositions,
 		})
 
 		if ([0].includes(wayline_type)) {
-			let startHeight = executeHeightMode ? filePositions[0].alt : filePositions[0].alt + (dronePosition != null ? dronePosition.height : 0)
-			let endHeight = executeHeightMode ? filePositions[1].alt : filePositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
+			let startHeight = executeHeightMode
+				? filePositions[0].alt
+				: filePositions[0].alt + (dronePosition != null ? dronePosition.height : 0)
+			let endHeight = executeHeightMode
+				? filePositions[1].alt
+				: filePositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
 
 			this.updataMultiNestData(device_sn, {
 				showPopupPosition: Cesium.Cartesian3.midpoint(
 					Cesium.Cartesian3.fromDegrees(filePositions[0].lng, filePositions[0].lat, startHeight),
 					Cesium.Cartesian3.fromDegrees(filePositions[1].lng, filePositions[1].lat, endHeight),
-					new Cesium.Cartesian3())
+					new Cesium.Cartesian3()
+				),
 			})
 		} else {
-			let startHeight = executeHeightMode ? safetyPositions[1].alt : safetyPositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
-			let endHeight = executeHeightMode ? safetyPositions[2].alt : safetyPositions[2].alt + (dronePosition != null ? dronePosition.height : 0)
+			let startHeight = executeHeightMode
+				? safetyPositions[1].alt
+				: safetyPositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
+			let endHeight = executeHeightMode
+				? safetyPositions[2].alt
+				: safetyPositions[2].alt + (dronePosition != null ? dronePosition.height : 0)
 
 			this.updataMultiNestData(device_sn, {
 				showPopupPosition: Cesium.Cartesian3.midpoint(
 					Cesium.Cartesian3.fromDegrees(safetyPositions[1].lng, safetyPositions[1].lat, startHeight),
 					Cesium.Cartesian3.fromDegrees(safetyPositions[2].lng, safetyPositions[2].lat, endHeight),
-					new Cesium.Cartesian3())
+					new Cesium.Cartesian3()
+				),
 			})
 		}
 
@@ -805,9 +983,9 @@
 
 				return {
 					...item,
-					alt: Number(height)
+					alt: Number(height),
 				}
-			})
+			}),
 		}
 	}
 
@@ -819,9 +997,10 @@
 			return i?.name && i?.name.includes('work-drone-route-')
 		})
 
-		Array.isArray(entities) && entities.forEach(item => {
-			this.viewer?.entities.remove(item)
-		})
+		Array.isArray(entities) &&
+			entities.forEach(item => {
+				this.viewer?.entities.remove(item)
+			})
 	}
 
 	// 当前无人机视椎体相关
@@ -847,11 +1026,22 @@
 				['wide', 'ir', 'zoom'].includes(curDroneData?.camera_type)
 			) {
 				if (curDroneData.camera_type === 'wide') {
-					fov = 60
+					fov = 65
 				} else {
-					const diffFocal = zoomFactor.value[curDroneData.camera_type].max - getZoomFactor(host?.cameras?.[0], curDroneData.camera_type)
-
-					fov = (60 / zoomFactor.value[curDroneData.camera_type].max) * (diffFocal === 0 ? 1 : diffFocal)
+					const cameraMultiplier = getZoomFactor(host?.cameras?.[0], curDroneData.camera_type)
+					switch (cameraMultiplier) {
+						case 2: fov = 30; break
+						case 3: fov = 19; break
+						case 4: fov = 15; break
+						case 5: fov = 13; break
+						case 6: fov = 11; break
+						case 7: fov = 9; break
+						case 8: fov = 8.34; break
+						case 9: fov = 7.68; break
+						case 10: fov = 6.9; break
+						case 14: fov = 5; break
+						default: fov = 55.946 * Math.exp(-0.4504 * cameraMultiplier) + 6.323
+					}
 				}
 			}
 
@@ -871,8 +1061,8 @@
 					pitch: 0,
 					heading: attitude_head,
 
-					isShowVideoPlan: this.isShowVideoPlan
-				})
+					isShowVideoPlan: this.isShowVideoPlan,
+				}),
 			})
 		}
 	}
@@ -882,7 +1072,7 @@
 
 		if (device_sn && live_status) {
 			this.updataMultiNestData(device_sn, {
-				camera_type: live_status?.[0].video_type || 'wide'
+				camera_type: live_status?.[0].video_type || 'wide',
 			})
 		}
 	}
@@ -897,14 +1087,9 @@
 		}
 	}
 
+	// 设置当前无人机视椎体信息面板
 	setFrustumInfoDetails (data) {
-		const {
-			dock_sn,
-			distance_to_airport,
-			distance_to_next_point,
-			estimated_time_to_next_point,
-			plane_mileage
-		} = data
+		const { dock_sn, distance_to_airport, distance_to_next_point, estimated_time_to_next_point, plane_mileage } = data
 
 		if (dock_sn) {
 			const aircraftEntity = this.viewer?.entities.values.find(i => {
@@ -924,7 +1109,9 @@
 				}
 
 				aircraftEntity.label = new Cesium.LabelGraphics({
-					text: `离机场平面距离:${distance_to_airport}m\n离下个航点平面距离:${Math.round(distance_to_next_point)}m\n到达下个航点剩余时间:${arrivalTime}\n航线总平面里程:${Math.round(plane_mileage)}m`,
+					text: `离机场平面距离:${distance_to_airport}m\n离下个航点平面距离:${Math.round(
+						distance_to_next_point
+					)}m\n到达下个航点剩余时间:${arrivalTime}\n航线总平面里程:${Math.round(plane_mileage)}m`,
 					font: '13px monospace',
 					showBackground: true,
 					horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
@@ -936,7 +1123,6 @@
 
 				return
 			}
-
 		}
 	}
 
@@ -954,8 +1140,10 @@
 		}
 	}
 
+	// 设置无人机模型
 	setAircraftGltf (host) {
 		const parent_sn = host?.parent_sn
+		const attitude_head = Number(host?.payloads?.[0]?.gimbal_yaw) - 90 || 0
 
 		if (parent_sn) {
 			const aircraftEntity = this.viewer?.entities.values.find(i => {
@@ -963,9 +1151,18 @@
 			})
 
 			const position = Cesium.Cartesian3.fromDegrees(host?.longitude, host?.latitude, host?.height)
+			// ✅ 新增:Heading / Pitch / Roll(单位:度 -> 弧度)
+			const heading = Cesium.Math.toRadians(attitude_head) // 航向角
+			const pitch = Cesium.Math.toRadians(0)     // 俯仰角
+			const roll = Cesium.Math.toRadians(0)       // 横滚角
+
+			// ✅ 新增:构造 HeadingPitchRoll 和 四元数
+			const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
+			const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
 
 			if (aircraftEntity) {
 				aircraftEntity.position = new Cesium.ConstantPositionProperty(position)
+				aircraftEntity.orientation = new Cesium.ConstantProperty(orientation)
 
 				return
 			}
@@ -974,18 +1171,35 @@
 				this.viewer?.entities.add({
 					name: `aircraft-glf-${parent_sn}`,
 					position,
+					orientation,
 					label: {},
 					model: {
 						uri: aircraftGltf, // 或 .glb
-						scale: 1.0, // 缩放比例
+						scale: 64, // 缩放比例
 						minimumPixelSize: 64, // 最小像素尺寸(保证模型远处可见)
-						maximumScale: 128, // 最大缩放(可选)
+						maximumScale: 64, // 最大缩放(可选)
 					},
 				})
 			}
 		}
 	}
 
+	// 移除无人机模型
+	removeAircraftGltf (host) {
+		const parent_sn = host?.parent_sn
+
+		if (parent_sn) {
+			const aircraftEntity = this.viewer?.entities.values.find(i => {
+				return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
+			})
+
+			if (aircraftEntity) {
+				this.viewer?.entities.remove(aircraftEntity)
+			}
+		}
+	}
+
+	// 移除所有无人机视椎体
 	removeViewInfoFrustum () {
 		this.multiNestData.forEach(item => {
 			if ('viewInfoFrustum' in item && item.viewInfoFrustum) {
@@ -1010,9 +1224,10 @@
 				return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
 			})
 
-			Array.isArray(entities) && entities.forEach(item => {
-				this.viewer?.entities.remove(item)
-			})
+			Array.isArray(entities) &&
+				entities.forEach(item => {
+					this.viewer?.entities.remove(item)
+				})
 		}
 	}
 
@@ -1021,13 +1236,15 @@
 			return i?.name && i?.name.includes(`aircraft-glf-`)
 		})
 
-		Array.isArray(entities) && entities.forEach(item => {
-			this.viewer?.entities.remove(item)
-		})
+		Array.isArray(entities) &&
+			entities.forEach(item => {
+				this.viewer?.entities.remove(item)
+			})
 
-		this.multiNestData.length > 0 && (this.multiNestData.forEach(item => {
-			item.viewInfoFrustum?.clear()
-		}))
+		this.multiNestData.length > 0 &&
+			this.multiNestData.forEach(item => {
+				item.viewInfoFrustum?.clear()
+			})
 	}
 
 	/**
@@ -1043,19 +1260,19 @@
 			this.multiNestData[isHaveInd] = {
 				...this.multiNestData[isHaveInd],
 
-				...data
+				...data,
 			}
 		} else {
 			this.multiNestData.push({
 				device_sn,
-				...data
+				...data,
 			})
 		}
 	}
 
 	/**
 	 * 根据机巢sn获取当前机巢对应的相关信息
-	 * @param {*} device_sn 
+	 * @param {*} device_sn
 	 */
 	getCurDroneData (device_sn) {
 		const isHave = this.multiNestData.find(item => item.device_sn === device_sn)
@@ -1071,14 +1288,16 @@
 			return {
 				...item,
 				show: true,
-				uuid: uuidv4()
+				uuid: uuidv4(),
 			}
 		})
 		this.viewer.scene.postRender.addEventListener(that.labelBoxRenderHandler)
 	}
 
 	showSingleDetailMapPopup (data) {
-		this.detailsMapPopupData.forEach(i => i.job_id === data.job_id && i.device_sn === data.device_sn && (i.show = true))
+		this.detailsMapPopupData.forEach(
+			i => i?.job_id === data?.job_id && i?.device_sn === data?.device_sn && (i.show = true)
+		)
 	}
 
 	getLabelDom (data) {
@@ -1090,28 +1309,30 @@
 		tooltipContainer.style.position = 'absolute'
 		tooltipContainer.style.transform = 'translate(-50%,10%)'
 		tooltipContainer.style.pointerEvents = 'none'
-		document.querySelector('.content-map-popups')?.append(tooltipContainer)
+		document.querySelector('.content-map-popups').append(tooltipContainer)
 		render(vNode, tooltipContainer)
 		return tooltipContainer
 	}
 
 	labelBoxRender () {
-		this.detailsMapPopupData.filter(i => i.show === true).forEach(item => {
-			const { showPopupPosition } = this.getCurDroneData(item.device_sn)
+		this.detailsMapPopupData
+			.filter(i => i.show === true)
+			.forEach(item => {
+				const { showPopupPosition } = this.getCurDroneData(item.device_sn)
 
-			let dom = document.querySelector(`[id="${item.uuid}"]`)
+				let dom = document.querySelector(`[id="${item.uuid}"]`)
 
-			if (!dom) {
-				dom = this.getLabelDom(item)
-			}
+				if (!dom) {
+					dom = this.getLabelDom(item)
+				}
 
-			const screenPosition = this.viewer?.scene.cartesianToCanvasCoordinates(showPopupPosition)
-			if (screenPosition) {
-				dom.style.left = `${screenPosition.x}px`
-				dom.style.top = `${screenPosition.y}px`
-				dom.style.display = 'block'
-			}
-		})
+				const screenPosition = this.viewer?.scene.cartesianToCanvasCoordinates(showPopupPosition)
+				if (screenPosition) {
+					dom.style.left = `${screenPosition.x}px`
+					dom.style.top = `${screenPosition.y}px`
+					dom.style.display = 'block'
+				}
+			})
 	}
 
 	// 移除弹框标签
@@ -1137,4 +1358,4 @@
 		const that = this
 		that.viewer?.scene.postRender.removeEventListener(that.labelBoxRenderHandler)
 	}
-}
\ No newline at end of file
+}

--
Gitblit v1.9.3