From 41b8a8d0ecea96ace27b5afb9ce63b4193420c49 Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Thu, 14 Aug 2025 08:47:21 +0800
Subject: [PATCH] Merge branch 'refs/heads/历史任务详情' into feature/v5.0/5.0.3

---
 src/utils/cesium/mapUtil.js |  135 +++++++++++++++++++++++++++++++++++++-------
 1 files changed, 112 insertions(+), 23 deletions(-)

diff --git a/src/utils/cesium/mapUtil.js b/src/utils/cesium/mapUtil.js
index 7d484cb..a47dcdf 100644
--- a/src/utils/cesium/mapUtil.js
+++ b/src/utils/cesium/mapUtil.js
@@ -1,5 +1,6 @@
 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
 
 export const getLngLatDistance = (lat1, lng1, lat2, lng2) => {
 	const radLat1 = (lat1 * Math.PI) / 180.0
@@ -9,10 +10,7 @@
 	let s =
 		2 *
 		Math.asin(
-			Math.sqrt(
-				Math.pow(Math.sin(a / 2), 2) +
-				Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)
-			)
+			Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))
 		)
 	s = s * 6378.137 // EARTH_RADIUS;
 	s = Math.round(s * 10000) / 10000
@@ -216,7 +214,7 @@
 					longitude,
 					latitude,
 					height,
-					cartesian3
+					cartesian3,
 				})
 				// 在这里,你可以使用 height 值进行后续操作
 			})
@@ -257,8 +255,7 @@
 					const longitude = Cesium.Math.toDegrees(item.longitude)
 					const latitude = Cesium.Math.toDegrees(item.latitude)
 
-					let FinalHeight =
-						Number(data[index]?.['ellipsoidHeight']?.['#text']) + Number(item.height)
+					let FinalHeight = Number(data[index]?.['ellipsoidHeight']?.['#text']) + Number(item.height)
 
 					if (droneHeight) {
 						FinalHeight = Number(data[index]?.['ellipsoidHeight']?.['#text']) + droneHeight
@@ -404,30 +401,60 @@
 
 /**
  * 飞到中心点并且所有点在可视范围
- * @param lngLatArr
- * @param viewer
- * @param rangeMultiple 范围倍数越大飞的越高默认3
+ *  @param positionsData 二维数组,每项为 [lon, lat] 或 三维数组 [lon, lat, height]
+ *  @param viewer Cesium.Viewer 实例
+ *  @param multiple 缩放倍数-默认为4
+ *  @param pitch 俯仰角-默认为-90
  */
-export function flyVisual (lngLatArr, viewer, rangeMultiple = 3) {
-	if (!Array.isArray(lngLatArr) || lngLatArr.length === 0) return
-	const positions = lngLatArr.map(([lon, lat]) =>
-		Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat))
+export function flyVisual({ positionsData, viewer, multiple = 4, pitch = -90 }) {
+	if (!Array.isArray(positionsData) || positionsData.length === 0) return
+
+	const positions = positionsData.map(([lon, lat, height]) =>
+		Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat), Number(height || 0))
 	)
-	// 计算包围盒 BoundingSphere(所有点的外接球)
+
+	// 计算最高高度和中心点经纬度
+	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)
-	viewer.camera.flyToBoundingSphere(boundingSphere, {
+	// 暂时飞向 BoundingSphere
+	viewer.camera.flyToBoundingSphere(Cesium.BoundingSphere.fromPoints(positions), {
 		duration: 0,
-		offset: new Cesium.HeadingPitchRange(0, -90, boundingSphere.radius * rangeMultiple),
+		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,
+					},
+				})
+			}
+		},
 	})
 }
 
 // 获取视口地图中心点
-export function getMapCenterPoint (viewer) {
+export function getMapCenterPoint(viewer) {
 	const centerResult = viewer.camera.pickEllipsoid(
-		new Cesium.Cartesian2(
-			viewer.canvas.clientWidth / 2,
-			viewer.canvas.clientHeight / 2
-		)
+		new Cesium.Cartesian2(viewer.canvas.clientWidth / 2, viewer.canvas.clientHeight / 2)
 	)
 	const curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(centerResult)
 	const longitude = (curPosition.longitude * 180) / Math.PI
@@ -436,7 +463,7 @@
 }
 
 // 获取屏幕四个角经纬度
-export const getScreenCorner = (viewer) => {
+export const getScreenCorner = viewer => {
 	const camera = viewer.scene.camera
 	const canvas = viewer.canvas
 
@@ -470,3 +497,65 @@
 		lt: [leftBottomLonLat[0], rightTopLonLat[1]], // 左上角
 	}
 }
+
+// areaCode转换为目录结构, 返回数组,
+export function areaCodeToArr(code) {
+	const codeStr = code.toString()
+	const provinceCode = codeStr.slice(0, 2) + '0000'
+	const cityCode = codeStr.slice(0, 4) + '00'
+	if (codeStr.slice(2, 4) === '00' && codeStr.slice(4, 6) === '00') {
+		return [provinceCode]
+	} else if (codeStr.slice(4, 6) === '00') {
+		return [provinceCode, cityCode]
+	}
+	return [provinceCode, cityCode, codeStr]
+}
+
+// 根据区域code获取轮廓
+export const getContourByCode = async areaCode => {
+	const defaultDir = `${VITE_APP_REGION_URL}/100000/`
+	const hierarchy = areaCodeToArr(areaCode.slice(0, 6))
+	const jsonPathPre = hierarchy.slice(0, hierarchy.length - 1).join('/')
+	const res = await fetch(`${defaultDir}${jsonPathPre}/index.json`)
+	const parentGJson = await res.json()
+	let features = parentGJson.features.find(item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1]))
+	return { type: 'FeatureCollection', features: [features] }
+}
+
+// 辅助函数:创建旋转文字的画布
+export function createRotatedTextCanvas(time) {
+	const prefix = '预计'
+	const suffix = '分钟'
+	const canvas = document.createElement('canvas')
+	const context = canvas.getContext('2d')
+	// 设置字体
+	context.font = 'bold 16px Source Han Sans CN'
+	// 测量各部分文本宽度
+	const prefixWidth = context.measureText(prefix).width
+	const timeWidth = context.measureText(time).width
+	const suffixWidth = context.measureText(suffix).width
+	const totalWidth = prefixWidth + timeWidth + suffixWidth
+	const textHeight = 20 // 估算高度
+	// 设置画布大小(增加边距)
+	canvas.width = totalWidth + 30
+	canvas.height = textHeight * 3
+	// 重置上下文
+	context.font = 'bold 16px Source Han Sans CN'
+	context.textBaseline = 'middle'
+	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.fillStyle = '#1EE7E7' // 亮蓝色
+	context.fillText(time, 10 + prefixWidth, 12)
+	// 绘制“分钟”(白色)
+	context.font = 'bold 16px Source Han Sans CN'
+	context.fillStyle = '#FFFFFF'
+	context.fillText(suffix, 20 + prefixWidth + timeWidth, 15)
+
+	return canvas
+}

--
Gitblit v1.9.3