From 89380e6260a75d1d3b94de687ebcc2f50d50659d Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Tue, 03 Feb 2026 15:44:33 +0800
Subject: [PATCH] feat:环境变量配置调整

---
 applications/drone-command/src/utils/cesium/mapUtil.js |  250 ++++++++++++++++++++++++++++++++++---------------
 1 files changed, 172 insertions(+), 78 deletions(-)

diff --git a/applications/drone-command/src/utils/cesium/mapUtil.js b/applications/drone-command/src/utils/cesium/mapUtil.js
index eabf803..767f5b0 100644
--- a/applications/drone-command/src/utils/cesium/mapUtil.js
+++ b/applications/drone-command/src/utils/cesium/mapUtil.js
@@ -1,7 +1,8 @@
 import * as Cesium from 'cesium'
 import * as turf from '@turf/turf'
-const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
+import store from '@/store'
 
+const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
 export const getLngLatDistance = (lat1, lng1, lat2, lng2) => {
 	const radLat1 = (lat1 * Math.PI) / 180.0
 	const radLat2 = (lat2 * Math.PI) / 180.0
@@ -190,6 +191,66 @@
 	const newArr = arr.sort((a, b) => a.distance - b.distance)
 	return newArr[0]
 }
+// 获取多边形内部数据
+export const samplePolygonInterior = (positions, samplingDistance) => {
+	if (!positions.length) return []
+	let minLng = Infinity,
+		maxLng = -Infinity
+	let minLat = Infinity,
+		maxLat = -Infinity
+	positions.forEach(pos => {
+		minLng = Math.min(minLng, pos.lng)
+		maxLng = Math.max(maxLng, pos.lng)
+		minLat = Math.min(minLat, pos.lat)
+		maxLat = Math.max(maxLat, pos.lat)
+	})
+
+	//  将 Cesium 多边形格式转为 Turf 格式
+	const turfPolygonCoords = positions.map(pos => [pos.lng, pos.lat])
+	turfPolygonCoords.push([positions[0].lng, positions[0].lat]) // 闭合多边形
+	const turfPolygon = turf.polygon([turfPolygonCoords])
+
+	//  生成网格点并判断是否在多边形内
+	const sampledPoints = []
+	for (let lng = minLng; lng <= maxLng; lng += samplingDistance) {
+		for (let lat = minLat; lat <= maxLat; lat += samplingDistance) {
+			// 创建 Turf 点
+			const turfPoint = turf.point([lng, lat])
+			// Turf 原生方法判断点是否在多边形内
+			if (turf.booleanPointInPolygon(turfPoint, turfPolygon)) {
+				sampledPoints.push({
+					lng: Number(lng.toFixed(6)),
+					lat: Number(lat.toFixed(6)),
+					height: 0,
+				})
+			}
+		}
+	}
+
+	return sampledPoints
+}
+// 获取多边形面内的最高点(相对地形高度)
+export const getPolygonHighestPoint = async (positions, viewer, samplingDistance = 0.0002) => {
+	try {
+		const interiorPoints = samplePolygonInterior(positions, samplingDistance, viewer)
+		const allPoints = [...positions, ...interiorPoints]
+		// 批量获取所有点的地形高度
+		const pointsWithTerrain = await getPointPositionsHeight(allPoints, viewer)
+		// console.log('所有点的地形高度', pointsWithTerrain)
+
+		// 地形高度(ASL)最高的点
+		const highestTerrainPoint = pointsWithTerrain.reduce(
+			(max, curr) => (curr.ASL > max.ASL ? curr : max),
+			pointsWithTerrain[0]
+		)
+
+		// console.log('地形最高点信息:', highestTerrainPoint)
+		return highestTerrainPoint
+	} catch (error) {
+		console.error('获取地形最高点失败:', error)
+		return null
+	}
+}
 
 // 获取当前经纬度地形数据
 export const getLnglatAltitude = (longitude, latitude, viewer) => {
@@ -285,31 +346,24 @@
 	return new Promise((resolve, reject) => {
 		if (!data || !data.length) {
 			resolve([])
-
 			return
 		}
-
 		// 假设 viewer 已经初始化并且 terrainProvider 是有效的
 		const terrainProvider = viewer?.terrainProvider
-
 		// 创建 Cartographic 对象
 		const cartographics = data.map(item => {
 			const { lng, lat } = item
-
 			return Cesium.Cartographic.fromDegrees(Number(lng), Number(lat))
 		})
-
 		// 获取地形数据的Promise
 		const promise = Cesium.sampleTerrainMostDetailed(terrainProvider, cartographics)
-
 		// 使用 Cesium.when 处理 Promise
 		promise
 			.then(function (updatedPositions) {
-				// updatedPositions 是一个数组,包含更新后的 Cartographic 对象
+				//  是一个数组,包含更新后的 Cartographic 对象
 				const newPosition = updatedPositions.map((item, index) => {
 					const longitude = Cesium.Math.toDegrees(item.longitude)
 					const latitude = Cesium.Math.toDegrees(item.latitude)
-
 					let pointData = {
 						...data[index],
 						longitude,
@@ -317,18 +371,14 @@
 						ASL: Number(item.height),
 						customHeight: Number(item.height),
 					}
-
 					if (droneHeight) pointData.TH = Number(item.height) + Number(droneHeight)
-
 					return pointData
 				})
-
 				resolve(newPosition)
-				// 在这里,你可以使用 height 值进行后续操作
 			})
 			.catch(function (error) {
-				// console.error('获取高程时发生错误:', error);
-				reject(error)
+				console.log('获取高程时发生错误:', error)
+				resolve(data.map(item => ({ ...item, ASL: 0, customHeight: 0, longitude: item.lng, latitude: item.lat })))
 			})
 	})
 }
@@ -397,63 +447,6 @@
 		//有相交
 		return true
 	}
-}
-
-/**
- * 飞到中心点并且所有点在可视范围
- *  @param positionsData 二维数组,每项为 [lon, lat] 或 三维数组 [lon, lat, height]
- *  @param viewer Cesium.Viewer 实例
- *  @param multiple 缩放倍数-默认为4
- *  @param pitch 俯仰角-默认为-90
- */
-export function flyVisual({ positionsData, viewer, multiple = 4, pitch = -90 }) {
-	if (!Array.isArray(positionsData) || positionsData.length === 0) return
-
-	// 如果是一个点,加两个点方便后续生成外包围盒
-	if (positionsData.length === 1){
-		const [lon,lat,height] = positionsData[0]
-		positionsData = [[lon+0.001,lat+0.001,height],[lon-0.001,lat-0.001,height],[lon,lat,height]]
-	}
-	const positions = positionsData.map(([lon, lat, height]) =>
-		Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat), Number(height || 0))
-	)
-
-	// 计算最高高度和中心点经纬度
-	let maxHeight = -Infinity
-	let sumLon = 0,
-		sumLat = 0
-	for (const [lon, lat, height] of positionsData) {
-		sumLon += Number(lon)
-		sumLat += Number(lat)
-		maxHeight = Math.max(maxHeight, Number(height || 0))
-	}
-	const centerLon = sumLon / positionsData.length
-	const centerLat = sumLat / positionsData.length
-	const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
-	// 暂时飞向 BoundingSphere
-	viewer.camera.flyToBoundingSphere(Cesium.BoundingSphere.fromPoints(positions), {
-		duration: 0,
-		offset: {
-			heading: Cesium.Math.toRadians(0),
-			pitch: Cesium.Math.toRadians(pitch),
-			range: boundingSphere.radius * multiple,
-		},
-		complete: () => {
-			// 飞行完成后检查相机高度是否小于最高点,如果小于直接飞到最高点
-			const cameraHeight = Cesium.Cartographic.fromCartesian(viewer.camera.position).height
-			if (cameraHeight < maxHeight) {
-				viewer.camera.flyTo({
-					destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, maxHeight + 100), // 加100米缓冲
-					duration: 1.5,
-					orientation: {
-						heading: viewer.camera.heading,
-						pitch: viewer.camera.pitch,
-						roll: viewer.camera.roll,
-					},
-				})
-			}
-		},
-	})
 }
 
 // 获取视口地图中心点
@@ -529,7 +522,7 @@
 
 // 辅助函数:创建旋转文字的画布
 export function createRotatedTextCanvas(time) {
-	const prefix = '预计'
+	const prefix = '预计到达'
 	const suffix = '分钟'
 	const canvas = document.createElement('canvas')
 	const context = canvas.getContext('2d')
@@ -537,7 +530,9 @@
 	context.font = 'bold 16px Source Han Sans CN'
 	// 测量各部分文本宽度
 	const prefixWidth = context.measureText(prefix).width
-	const timeWidth = context.measureText(time).width
+	let timeWidth = context.measureText(time).width
+	const timeWidthMore = `${time >= 10 ? 6 : 0}`
+	timeWidth = timeWidth + Number(timeWidthMore)
 	const suffixWidth = context.measureText(suffix).width
 	const totalWidth = prefixWidth + timeWidth + suffixWidth
 	const textHeight = 20 // 估算高度
@@ -550,17 +545,116 @@
 	context.textAlign = 'left' // 改为左对齐,方便分段绘制
 	// 清除画布
 	context.clearRect(0, 0, canvas.width, canvas.height)
-	// 绘制“预计”(白色)
+	// 绘制“预计到达”(白色)
 	context.fillStyle = '#FFFFFF'
 	context.fillText(prefix, 10, 15)
 	// 绘制时间(蓝色)
-	context.font = 'bold 30px Source Han Sans CN'
+	context.font = 'bold 24px Source Han Sans CN'
 	context.fillStyle = '#1EE7E7' // 亮蓝色
-	context.fillText(time, 10 + prefixWidth, 12)
+	// 设置宽度
+	context.textBaseline = 'middle'
+	context.fillText(time, 10 + prefixWidth, 14)
 	// 绘制“分钟”(白色)
 	context.font = 'bold 16px Source Han Sans CN'
 	context.fillStyle = '#FFFFFF'
-	context.fillText(suffix, 20 + prefixWidth + timeWidth, 15)
+	context.fillText(suffix, 14 + prefixWidth + timeWidth, 15)
 
 	return canvas
 }
+
+
+export const saveCurrentCameraPosition = viewer => {
+	const position = viewer?.camera.position.clone() // 相机位置(Cartesian3)
+	const heading = viewer?.camera.heading // 航向角(弧度)
+	const pitch = viewer?.camera.pitch // 俯仰角(弧度)
+	const roll = viewer?.camera.roll // 翻滚角(弧度)
+	store.commit('setCameraPosition', {
+		duration: 0,
+		destination: position,
+		orientation: { heading, pitch, roll },
+	})
+}
+
+// 连接地面线
+export function getDockPolyLine(data, viewer) {
+	if (!data.longitude || !data.latitude) return {}
+	return {
+		positions: new Cesium.CallbackProperty(() => {
+			const pointPosition = Cesium.Cartesian3.fromDegrees(+data.longitude, +data.latitude, +data.height)
+			if (!pointPosition) return []
+			// 获取点的位置
+			const cartographic = Cesium.Cartographic.fromCartesian(pointPosition)
+			// 获取地形高度
+			const terrainHeight = viewer.scene.globe.getHeight(cartographic) || 0
+			// 创建地面点位置
+			const groundPosition = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, terrainHeight)
+			return [pointPosition, groundPosition]
+		}, false),
+		width: 0.5,
+		material: Cesium.Color.WHITE,
+	}
+}
+
+// 飞向一个机巢中心
+export const flyDockCenter = (viewer, info) => {
+	const { longitude = 115, latitude = 28, height = 0 } = info
+	const position = Cesium.Cartesian3.fromDegrees(+longitude, +latitude, +height)
+	const droneEntity = viewer?.entities.add({ position: position, point: { pixelSize: 0 } })
+	viewer?.flyTo(droneEntity, {
+		offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 22000),
+		duration: 0.5,
+		complete: () => {
+			viewer?.entities.remove(droneEntity)
+		},
+	})
+}
+
+// 在固定的经度上获得高度
+export function getHeightOnFixedLonLat(ray, pointParams, ellipsoid = Cesium.Ellipsoid.WGS84) {
+	const { longitude: lon, latitude: lat } = pointParams
+	// 1. 地表点(高度=0)
+	const p0 = Cesium.Cartesian3.fromDegrees(lon, lat, 0, ellipsoid)
+
+	// 2. 该点处的法线方向(up 向量,单位向量)
+	const up = ellipsoid.geodeticSurfaceNormal(p0, new Cesium.Cartesian3())
+
+	// 3. 射线起点和方向
+	const r0 = ray.origin // 相机位置
+	const rd = ray.direction // 射线方向(已归一化?不一定,但不影响)
+
+	// 4. 解:求直线 L(t) = p0 + t * up 与射线 R(s) = r0 + s * rd 的最近点
+	// 使用向量公式求两条直线的最近点参数 t
+	const w0 = Cesium.Cartesian3.subtract(p0, r0, new Cesium.Cartesian3())
+
+	const a = Cesium.Cartesian3.dot(up, up) // = 1(因为 up 是单位向量)
+	const b = Cesium.Cartesian3.dot(up, rd)
+	const c = Cesium.Cartesian3.dot(rd, rd) // = |rd|^2
+	const d = Cesium.Cartesian3.dot(up, w0)
+	const e = Cesium.Cartesian3.dot(rd, w0)
+
+	const denom = a * c - b * b
+	let t
+
+	if (Math.abs(denom) < Cesium.Math.EPSILON10) {
+		// 两直线平行,取 t = 0
+		t = 0
+	} else {
+		t = (b * e - c * d) / denom
+	}
+
+	// 5. 得到高度线上对应的点
+	const resultPoint = Cesium.Cartesian3.add(
+		p0,
+		Cesium.Cartesian3.multiplyByScalar(up, t, new Cesium.Cartesian3()),
+		new Cesium.Cartesian3()
+	)
+
+	// 6. 转回 Cartographic 获取精确高度
+	const carto = Cesium.Cartographic.fromCartesian(resultPoint, ellipsoid)
+	return {
+		cartesian: resultPoint,
+		height: carto.height,
+		longitude: Cesium.Math.toDegrees(carto.longitude),
+		latitude: Cesium.Math.toDegrees(carto.latitude),
+	}
+}

--
Gitblit v1.9.3