From 2ca94de8ede18ac07ccfd8dec7b6f6a707adde9b Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Mon, 01 Sep 2025 11:20:24 +0800
Subject: [PATCH] Merge branch 'refs/heads/feature/v5.0/5.0.5' into patch_management

---
 src/views/job/components/DeviceJobDetailsMap.vue |  358 ++++++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 281 insertions(+), 77 deletions(-)

diff --git a/src/views/job/components/DeviceJobDetailsMap.vue b/src/views/job/components/DeviceJobDetailsMap.vue
index 22ab5a0..ffbc284 100644
--- a/src/views/job/components/DeviceJobDetailsMap.vue
+++ b/src/views/job/components/DeviceJobDetailsMap.vue
@@ -1,95 +1,123 @@
 <template>
-	<div id="deviceJobDetailsMap" class="ztzf-cesium" />
+	<div id="deviceJobDetailsMap" class="ztzf-cesium">
+	</div>
+
 </template>
 <script setup>
 import * as Cesium from 'cesium'
-import { Cartesian3, Terrain, Viewer } from 'cesium'
-import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
-import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
-import lineImg from '@/assets/images/task/arrow-right-blue.png'
-// import rwqfdImg from '@/assets/images/task/rwqfd.png'
-import rwqfdImg from '@/assets/map_images/Startingpointicon.png'
-import endPointImg from '@/assets/images/task/EndPointicon.png'
-import { flyVisual } from '@/utils/cesium/mapUtil'
 import { PublicCesium } from '@/utils/cesium/publicCesium'
-
-const props = defineProps(['detailsData'])
-
+import { flyVisual } from '@/utils/cesium/mapUtil'
+import _ from 'lodash'
+import photoDirection from '@/assets/images/historyMapPopup/photoDirection-new.png'
+import panoramaPoint from '@/assets/images/panorama/panorama-point.png' //全景图标
+import { getShowImg } from '@/utils/util'
+import { getEventImage } from '@/utils/stateToImageMap/event'
+import { getPanoramaList } from '@/api/panorama'
+import CreateRouteLine from '@/utils/cesium/createRouteLine'
+import { useStore } from 'vuex'
+import { getOdmToken } from '@/api/model'
+const createRouteLineExample = new CreateRouteLine()
+const store = useStore()
+const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
 let viewer = null
+let publicCesiumInstance = null
+const viewInstance = shallowRef(null)
+import { cloneDeep } from 'lodash'
+let handler = null
+let curMoveEntity = null
+const tokenStr = ref('')
+const emit = defineEmits(['showImageeclick'])
+const props = defineProps(['detailsData', 'yuanImages', 'jobId'])
+
 const initMap = () => {
-	viewer = new PublicCesium({ dom: 'deviceJobDetailsMap' }).getViewer()
-}
+	publicCesiumInstance = new PublicCesium({
+		dom: 'deviceJobDetailsMap',
+		terrain: true,
+		flatMode: false,
+		layerMode: 4,
+	})
 
-// 渲染线和点
-const renderingLine = (lineObj,startPoint) => {
-	const positions = lineObj.Placemark.map(item => {
-		const [lon, lat] = item.Point.coordinates.split(',')
-		return Cartesian3.fromDegrees(Number(lon), Number(lat))
-	})
-	// 向前插入起飞点
-	positions.unshift(Cartesian3.fromDegrees(startPoint[0], startPoint[1]))
-	// 起点
-	viewer.entities.add({
-		position: positions[0],
-		billboard: {
-			image: new Cesium.ConstantProperty(rwqfdImg),
-			width: 30,
-			height: 30,
-		},
-	})
-	// 终点
-	viewer.entities.add({
-		position: positions[positions.length - 1],
-		billboard: {
-			image: new Cesium.ConstantProperty(endPointImg),
-			width: 30,
-			height: 30,
-			verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
-		},
-	})
-	// 路径线
-	viewer.entities.add({
-		polyline: {
-			width: 4,
-			positions: positions,
-			material: new ImageTrailMaterial({
-				color: { alpha: 1, blue: 1, green: 1, red: 1 },
-				speed: 20,
-				image: lineImg,
-				repeat: { x: Math.floor(40), y: 1 },
-			}),
-			clampToGround: false,
-		},
-	})
-}
+	viewer = publicCesiumInstance.getViewer()
+	viewInstance.value = publicCesiumInstance
+	viewer.scene.globe.depthTestAgainstTerrain = true
+	createRouteLineExample.initCreateRoute(viewer)
+	handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
+	handler.setInputAction(click => {
+		const pickedObjects = viewer.scene.drillPick(click.position)
+		LeftClickEvent(click, pickedObjects, viewer)
+	}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
 
-// 异步解析kmz文件
-const analysis = async item => {
-	return new Promise(async resolve => {
-		const res = await analyzeKmzFile(`${item.url}?_t=${new Date().getTime()}`)
-		const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml']
-		const waylinesXMLJSON = XMLToJSON(waylinesXML)?.['Document']
-		const waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder)
-		resolve({ waylinesXMLObj, takeOffRefPoint:[item.longitude,item.latitude] })
-	})
+	handler.setInputAction(e => {
+		let pick = viewer.scene.drillPick(e.endPosition)
+	}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
 }
-
 // 绘制线和飞行
 const drawLine = async () => {
-	const res = await Promise.all(props.detailsData.way_lines.map(item => analysis(item)))
-	// 渲染线
-	res.forEach(({ waylinesXMLObj,takeOffRefPoint }) => renderingLine(waylinesXMLObj,takeOffRefPoint))
-	// 收集所有起飞点
-	const allTakeOffRefPoint = props.detailsData.way_lines.map(item => ([item.longitude,item.latitude]))
-	// 收集所有点包括起飞点
-	const allPoint = res.flatMap(({ waylinesXMLObj }) => waylinesXMLObj.Placemark)
-		.map(item => item.Point.coordinates.split(',')).concat(allTakeOffRefPoint)
-	flyVisual(allPoint, viewer,4)
+	Promise.all(
+		props.detailsData.way_lines.map(
+			async item =>
+				await createRouteLineExample.parsingFiles(
+					{
+						...item,
+						url: `${item.url}?_t=${new Date().getTime()}`,
+					},
+					{
+						longitude: item.longitude,
+						latitude: item.latitude,
+						height: item.height,
+					},
+					true,
+					false
+				)
+		)
+	).then(res => {
+		createRouteLineExample.showDetailMapPopup(props.detailsData.way_lines)
+		let arr = res.map(i => i.filePositions)
+		let newArr = _.flatten(arr)
+		flyVisual({
+			positionsData: newArr.map(i => [i.lng, i.lat, i.alt]),
+			viewer,
+			pitch: -60,
+		})
+	})
 }
-
+const lastSelectedId = ref(null)
+// 鼠标左键点击事件
+const LeftClickEvent = (click, pick, viewer) => {
+	// pick是一个数组
+	if (pick && pick.length > 0) {
+		let startEntity = pick.find(
+			i => i.id.name === 'work-drone-route-planar-polyline' || i.id.name === 'work-drone-route-point-polyline'
+		)?.id
+		if (startEntity) {
+			createRouteLineExample.showSingleDetailMapPopup(startEntity?.customData?.data)
+			return
+		}
+		let imagePath = []
+		let categories = ''
+		let panoramic = ''
+		pick.forEach(item => {
+			const index = item.id.index
+			if (item.id.customType == 'spotImages') {
+				imagePath.push(item.id.customInfo.link)
+			} else if (item.id.customType == 'aggregationSplashed') {
+				imagePath.push(item.id.customInfo.link)
+			} else if (item.id.customType == 'panorama') {
+				//全景
+				imagePath.push(item.id.customInfo.link)
+				categories = item.id.customInfo.resultType
+			}
+		})
+		if (imagePath.length === 0) return
+		const finalImagePaths = categories === 5 ? imagePath : imagePath.map(i => getShowImg(i))
+		emit('showImageeclick', finalImagePaths, 0, categories)
+	}
+}
 const removeMap = () => {
 	viewer.entities.removeAll()
-	viewer.destroy()
+	publicCesiumInstance?.viewerDestroy()
+	publicCesiumInstance = null
+	viewer = null
 }
 
 watch(
@@ -100,12 +128,187 @@
 		}
 	}
 )
+const zsList = ref([])
+// 获取odmtoken
+function getOdmTokenStr() {
+	getOdmToken().then(res => {
+		if (res.data.code !== 200) return
+		tokenStr.value = res.data.data
+	})
+}
+// 获取正射列表
+function getZSList(newVal) {
+	let list = []
+	newVal.map((item, index) => {
+		item.childrens.map(zschilditem => {
+			if (zschilditem?.data) {
+				zschilditem?.data.map(dataitem => {
+					list.push(dataitem)
+				})
+			}
+		})
+	})
+	return list
+}
+// 获取正射请求地图聚合数据
+const zsMapList = ref([])
+function getZSMapList(valSn) {
+	const params = {
+		areaCode: userAreaCode.value,
+		deviceSn: valSn,
+		resultType: 4,
+		wayLineJobId: props.jobId,
+	}
+	getPanoramaList(params).then(res => {
+		const resData = res?.data?.data
+		zsMapList.value = resData?.childrens || []
+		zsList.value = getZSList(zsMapList.value)
 
+		renderOrthophoto()
+	})
+}
+// 解析经纬度
+function getLonLat(str) {
+	if (!str) return []
+	return str
+		.replace('POLYGON((', '')
+		.replace('))', '')
+		.split(',')
+		.map(pair => {
+			const [lon, lat] = pair.trim().split(' ').map(Number)
+			return [lon, lat]
+		})
+}
+// 渲染正射
+function renderOrthophoto() {
+	zsList.value.forEach((item, index) => {
+		const cur = viewInstance.value.addCustomImageryProviderDataSource(
+			new Cesium.UrlTemplateImageryProvider({
+				url: `${import.meta.env.VITE_APP_AREA_NAME}/webodm${item.url}?jwt=${tokenStr.value.split(' ')[1]}`,
+			})
+		)
+	})
+}
+watch(
+	() => props.yuanImages,
+	(newValue, oldValue) => {
+
+		// 增加箭头
+		  if (!viewer || !newValue?.length) return;
+		   // 清除旧实体
+    const oldEntities = viewer.entities.values.filter(e => 
+      e.id?.startsWith('spotImages') || 
+      e.id?.startsWith('aggregationSplashed') || 
+      e.id?.startsWith('panorama')
+    );
+    oldEntities.forEach(e => viewer.entities.remove(e));
+	// 添加新实体
+		let eventArr = newValue.filter(i => i.resultType === 2)
+		let originalArr = newValue.filter(i => i.resultType === 0)
+		let panoramicArr = newValue.filter(i => i.resultType === 5)
+		let orthographicArr = newValue.filter(i => i.resultType === 4)
+		if (orthographicArr.length >0) {
+			getZSMapList(orthographicArr[0]?.deviceSn)
+		}
+		originalArr.forEach((item_in, index) => {
+			// 根据偏航角度展示图片拍摄的方向
+			let yawAngleDegrees =
+				item_in.metadata.gimbalYawDegree < 0
+					? 360 + Number(item_in.metadata.gimbalYawDegree)
+					: Number(item_in.metadata.gimbalYawDegree)
+
+			viewer.entities.add({
+				position: Cesium.Cartesian3.fromDegrees(
+					+item_in.metadata.shootPosition.lng,
+					+item_in.metadata.shootPosition.lat,
+					+item_in.metadata.absoluteAltitude
+				),
+				id: 'spotImages' + item_in.id,
+				customType: 'spotImages',
+				customInfo: {
+					...item_in,
+				},
+				index: index,
+				billboard: {
+					image: photoDirection,
+					outlineWidth: 0,
+					width: 14,
+					height: 21,
+					disableDepthTestDistance: Number.POSITIVE_INFINITY,
+					verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
+				},
+			})
+		})
+
+		eventArr.forEach((item, index) => {
+			viewer.entities.add({
+				id: `aggregationSplashed${item.id}`,
+				customType: 'aggregationSplashed',
+				customInfo: {
+					...item,
+				},
+				position: Cesium.Cartesian3.fromDegrees(
+					+item.metadata.shootPosition.lng,
+					+item.metadata.shootPosition.lat,
+					+item.metadata.absoluteAltitude
+				),
+				billboard: {
+					image: getEventImage(item.status),
+					width: 30,
+					height: 30,
+					disableDepthTestDistance: Number.POSITIVE_INFINITY,
+				},
+				index: index,
+				properties: {
+					customData: {
+						data: item,
+					},
+				},
+			})
+		})
+		// 全景
+		panoramicArr.forEach((item, index) => {
+			viewer.entities.add({
+				id: `panorama${item.id}`,
+				customType: 'panorama',
+				customInfo: {
+					...item,
+				},
+				position: Cesium.Cartesian3.fromDegrees(
+					+item.metadata.shootPosition.lng,
+					+item.metadata.shootPosition.lat,
+					+item.metadata.absoluteAltitude
+				),
+				billboard: {
+					image: panoramaPoint,
+					width: 30,
+					height: 30,
+					disableDepthTestDistance: Number.POSITIVE_INFINITY,
+				},
+				index: index,
+				properties: {
+					customData: {
+						data: item,
+					},
+				},
+			})
+		})
+	},
+	{ immediate: true, deep: true }
+)
 onBeforeUnmount(() => {
+	// 移除事件监听
+	handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
+	handler?.destroy()
+	handler = null
+
+	createRouteLineExample.destroyedCustomDom()
+
 	removeMap()
 })
 
 onMounted(() => {
+	getOdmTokenStr()
 	nextTick(() => {
 		initMap()
 	})
@@ -113,6 +316,7 @@
 </script>
 <style scoped lang="scss">
 #deviceJobDetailsMap {
+	position: relative;
 	height: 100%;
 }
 </style>

--
Gitblit v1.9.3