import * as Cesium from 'cesium' import { Decimal } from 'decimal.js' import _, { cloneDeep, throttle } from 'lodash' import { v4 as uuidv4 } from 'uuid' import { render } from 'vue' import HistoryDronePopup from '@/components/DeviceJobDetails/HistoryDronePopup.vue' import { getNewPolygonData } from '@/views/RoutePlan/PlanarAirLine/planarRouteUtils' import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz' import { ArrowLineMaterialProperty, LineTrailMaterial, PolylineGlowMaterial, } from '@/utils/cesium/Material' import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum' import { ElMessage } from 'element-plus' import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png' import endingOnlineImg from '@/assets/images/aiNowFly/ending-online.png' import newEndPointImg from '@/assets/images/newEndPointicon.png' import endPointImg from '@/assets/images/EndPointicon.png' import aircraftGltf from '@/assets/gltf/aircraft.gltf' import newNumPoint from '@/assets/images/newStartPoint.png' import { getPolyLine, } from '@/views/RoutePlan/PointAirLine/pointWayLineUtils' import store from '@/store/index' 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 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) { let zoom_factor const irLevel = camerasInfo?.ir_zoom_factor const zoomLevel = camerasInfo?.zoom_factor if (type === 'wide') { zoom_factor = 1 } else if (type === 'ir') { zoom_factor = irLevel } else { zoom_factor = zoomLevel } zoom_factor = _.round(zoom_factor, 1) return zoom_factor } export default class CreateRouteLine { /** * * @param {*} options 参数 */ constructor(options) { this.type = options?.type || 'single' this.isShowVideoPlan = options?.isShowVideoPlan || false this.viewer = null this.currentWaypointIndex = null this.droneSpeedArr = [] this.multiNestData = [] this.detailsMapPopupData = [] this.labelBoxRenderHandler = this.labelBoxRender.bind(this) this.removeLabelHandler = this.removeLabel.bind(this) } /** * 初始化viewer * @param {*} viewer */ initCreateRoute (viewer) { this.viewer = viewer } /** * 设置当前所在点位 * @param {*} data flighttaskProgressInfo 信息 */ setCurrentWaypointIndex (data) { const output = data?.output const sn = data?.sn if (output?.ext) { const currentWaypointIndex = output?.ext['current_waypoint_index'] this.updataMultiNestData(sn, { currentWaypointIndex }) } } /** * 请求航线文件数据并解析处理 * @param {*} data 包含文件url等信息 * @param {*} dronePosition 无人机位置 * @returns */ async parsingFiles (data, dronePosition = null, isShowDock = false, isShowPointBillboard = true) { const { url, wayline_type, device_sn } = data if (!url) return dronePosition != null ? { dronePosition: [{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }] } : {} this.updataMultiNestData(device_sn) const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`) const templateXML = await res.fileInfoObj['wpmz/template.kml'] const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml'] const templateXmlJson = XMLToJSON(templateXML)?.['Document'] const waylinesXmlJson = XMLToJSON(waylinesXML)?.['Document'] const templateXMLObj = removeTextKey(templateXmlJson.Folder) const { takeOffRefPoint } = removeTextKey(templateXmlJson.missionConfig) const [latitude, longitude, height] = takeOffRefPoint.split(',').map(item => _.round(item, 6)) const startPoint = { latitude, longitude, height: Number.isNaN(height) ? 0 : height } const { templateType } = templateXMLObj const missionConfig = removeTextKey(waylinesXmlJson.missionConfig) const waylinesXMLObj = removeTextKey(waylinesXmlJson.Folder) if (templateType === "mapping3d") { if (!waylinesXMLObj[0].Placemark.length) return ElMessage.error('没有航线点位') } else { if (!waylinesXMLObj.Placemark.length) return ElMessage.error('没有航线点位') } if ([2, 4, 5].includes(Number(wayline_type))) { if (templateType === "mapping3d") { let morePolygonData = waylinesXMLObj.map(i => ({ data: i.Placemark })) let newPolygonData = getNewPolygonData(morePolygonData) let newPolygonDisposePositions = newPolygonData.map(i => Cesium.Cartesian3.fromDegrees(Number(i[0]), Number(i[1])) ) this.viewer.entities.add({ name: 'work-drone-route-planar-outer-polygon', polygon: { hierarchy: new Cesium.CallbackProperty(() => { return new Cesium.PolygonHierarchy(newPolygonDisposePositions) }, false), material: Cesium.Color.fromBytes(255, 228, 22, 44), outline: false, outlineWidth: 2, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, }, }) return this.drawPlanarRoute( waylinesXMLObj[0], missionConfig, templateXmlJson, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type ) } return this.drawPlanarRoute( waylinesXMLObj, missionConfig, templateXmlJson, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type ) } else { return this.drawPointRoute( waylinesXMLObj, missionConfig, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type ) } } /** * 绘制面状航线 * @param {*} lineObj 航线文件中的 * @param {*} missionConfig 航线文件中的 * @param {*} templateXmlJson 航线文件中的 * @param {*} dronePosition 无人机位置 * @param {*} data * @param {*} startPoint * @param {*} isShowPointBillboard 是否显示,航线起飞点,终点等标注 * @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) const { device_sn } = data const droneTransformPosition = Cesium.Cartesian3.fromDegrees( Number(dronePosition.longitude), Number(dronePosition.latitude), Number(dronePosition.height), ) let coordArr = null const placemark = templateXmlJson.Folder?.Placemark // 取出点位 const coordinates = placemark.Polygon?.outerBoundaryIs.LinearRing.coordinates?.[ '#text' ]?.split('\n') || [] // 数组转换 coordArr = coordinates.map((coordinate) => coordinate .replace(/\s+/g, '') .split(',') .map((v) => Number(v)), ) // 获取当前经纬度绝对高度 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, material: Cesium.Color.CHARTREUSE, zIndex: 1, }, customData: { 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) }) // 面状航线第一个点突出展示 isShowPointBillboard && this.startingIncreasePoint(positionArray) // 面状判断当前点位数量 const cloneCoordArr = [...newCoordArr] if (cloneCoordArr.length >= 3) { cloneCoordArr.push(newCoordArr[0]) } // 绘制面状边框线-------------------- this.viewer.entities.add({ name: 'work-drone-route-planar-border', polyline: { positions: Cesium.Cartesian3.fromDegreesArrayHeights( cloneCoordArr.flat(), ), width: 4, material: new Cesium.PolylineOutlineMaterialProperty({ color: new Cesium.Color.fromBytes(74, 138, 233), outlineWidth: 2, outlineColor: Cesium.Color.WHITE, }), zIndex: -1, clampToGround: true, }, }) // 绘制面状地块-------------------- this.viewer.entities.add({ name: 'work-drone-route-planar-polygon', polygon: { 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), zIndex: -1, }, }) if (this.type === 'clusterScheduling') { this.viewer.entities.add({ name: 'work-drone-route-point-start', position: Cesium.Cartesian3.fromDegrees(filePositions[0].lng, filePositions[0].lat, filePositions[0].alt), billboard: { image: rwqfdImg, outlineWidth: 0, width: 50, height: 50, disableDepthTestDistance: Number.POSITIVE_INFINITY, }, label: { text: data.job_name || '', font: 'bold 16px YouSheBiaoTiHei', fillColor: Cesium.Color.WHITE, pixelOffset: new Cesium.Cartesian2(0, -50), style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth: 2, outlineColor: Cesium.Color.BLACK, disableDepthTestDistance: Number.POSITIVE_INFINITY, }, }) } 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) ) // 起点 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 } }) } return { entity: planarRouteEntity, routeLinePositions: positionArray, filePositions: dronePosition != null ? [ { lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }, ...filePositions ] : filePositions } } planarBillboard (color) { const billboard = document.createElement('canvas') const ctx = billboard.getContext('2d') const radius = 4 ctx.beginPath() ctx.arc(radius, radius, radius, 0, 2 * Math.PI, false) ctx.fillStyle = color // 设置填充颜色 ctx.fill() return billboard } startingIncreasePoint (wayData) { let positions = wayData[3] let setting = { name: 'work-drone-route-planar-start', position: positions, billboard: { image: this.planarBillboard('#2D8CF0'), pixelOffset: new Cesium.Cartesian2(146, 72), zIndex: 2, }, } this.viewer.entities.add(setting) } /** * 绘制点航线 * @param {*} lineObj 航线文件中的 * @param {*} missionConfig 航线文件中的 * @param {*} dronePosition 无人机位置 * @param {*} data 任务信息,航线地址等 * @param {*} startPoint * @param {*} isShowDock 是否显示机巢位置 * @param {*} isShowPointBillboard 是否显示,航线起飞点,终点等标注 * @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), ) // 路径线 let polyline 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, width: 50, height: 50, disableDepthTestDistance: Number.POSITIVE_INFINITY, }, label: { text: data.job_name || '', font: 'bold 16px YouSheBiaoTiHei', fillColor: data.type === 2 ? runningTextColor : pendingTextColor, pixelOffset: data.type === 2 ? runningTextOffset : pendingTextOffset, style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth: 2, outlineColor: Cesium.Color.BLACK, disableDepthTestDistance: Number.POSITIVE_INFINITY, }, }) } else if (index === filePositions.length - 1) { this.viewer.entities.add({ name: 'work-drone-route-point-end', position, billboard: { image: newEndPointImg, outlineWidth: 0, width: 40, height: 40, scale: 1.0, verticalOrigin: Cesium.VerticalOrigin.TOP, // 添加这行确保图标正确显示 pixelOffset: new Cesium.Cartesian2(0, -20), // 根据需要调整偏移量 }, }) } else { this.viewer.entities.add({ name: 'work-drone-route-point-approach', position, billboard: { image: newNumPoint, width: 50, height: 50, }, label: { text: `${index}`, font: 'bold 14px serif', fillColor: Cesium.Color.WHITE, pixelOffset: new Cesium.Cartesian2(1, 0), // 根据需要调整偏移量 eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 使标签在点的上方 }, offset: new Cesium.Cartesian2(10, 30), }) } }) polyline = this.viewer.entities.add({ name: 'work-drone-route-point-polyline', polyline: { width: 4, positions: positionArray, material: data.type === 2 ? runningLineMaterial : pendingLineMaterial, 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) ) // 起点 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 && 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, // 底部对齐 }, }) polyline = this.viewer.entities.add({ name: 'work-drone-route-point-polyline', polyline: { width: 4, positions: positionArray, material: arrowLineMaterialProperty, clampToGround: false, }, customData: { 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-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, } } /** * 通用处理数据 * @param {*} lineObj * @param {*} missionConfig * @param {*} dronePosition * @param {*} startPoint * @returns {} {positionArray: 带拼接点位置, filePositions:光航线点位置} */ disposeData (lineObj, missionConfig, dronePosition, data, startPoint, wayline_type) { const { device_sn } = data const executeHeightMode = lineObj.executeHeightMode === "WGS84" let filePositions = lineObj.Placemark.map(item => { const [lng, lat] = item.Point.coordinates.split(',') const height = item.executeHeight return { lng: Number(lng), lat: Number(lat), alt: Number(height), } }) let safetyHeight if (executeHeightMode) { let startHeight = new Decimal(missionConfig.takeOffSecurityHeight) .plus(dronePosition.height).toNumber() safetyHeight = startHeight < filePositions[0].alt ? filePositions[0].alt : startHeight } else { safetyHeight = missionConfig.takeOffSecurityHeight < filePositions[0].alt ? filePositions[0].alt : missionConfig.takeOffSecurityHeight } let safetyPositions = [] if (dronePosition != null) { safetyPositions = [ { lng: Number(dronePosition.longitude), lat: Number(dronePosition.latitude), alt: Number(dronePosition.height), }, { lng: Number(dronePosition.longitude), lat: Number(dronePosition.latitude), alt: Number(safetyHeight), }, { lng: filePositions[0].lng, lat: filePositions[0].lat, alt: Number(safetyHeight), }, ] } let pointAll = [...safetyPositions, ...filePositions] const currentRoutePositions = pointAll.map((item, index) => { let height = executeHeightMode ? item.alt : item.alt + (dronePosition != null ? dronePosition.height : 0) if (index == 0) height = item.alt return Cesium.Cartesian3.fromDegrees(item.lng, item.lat, height) }) this.updataMultiNestData(device_sn, { 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) 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()) }) } 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) 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()) }) } return { positionArray: currentRoutePositions, filePositions: filePositions.map(item => { let height = executeHeightMode ? item.alt : item.alt + (dronePosition != null ? dronePosition.height : 0) return { ...item, alt: Number(height) } }) } } /** * 删除entity */ removeAllRouteEntity () { const entities = this.viewer?.entities.values.filter(i => { return i?.name && i?.name.includes('work-drone-route-') }) Array.isArray(entities) && entities.forEach(item => { this.viewer?.entities.remove(item) }) } // 当前无人机视椎体相关 setCreateFrustum (host, zoomFactor = {}, cameraParams = {}) { if (!host) return const parent_sn = host?.parent_sn if (parent_sn) { const curDroneData = this.getCurDroneData(parent_sn) curDroneData.viewInfoFrustum?.destroyed() const attitude_head = 180 + host?.attitude_head const gimbal_pitch = 90 - Number(host?.payloads[0]?.gimbal_pitch) || 0 let fov = 60 if ( zoomFactor?.value?.set === true && cameraParams?.value?.set === true && curDroneData?.camera_type && ['wide', 'ir', 'zoom'].includes(curDroneData?.camera_type) ) { if (curDroneData.camera_type === 'wide') { fov = 60 } 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) } } this.updataMultiNestData(parent_sn, { viewInfoFrustum: new CreateFrustum(this.viewer, { position: { longitude: host?.longitude, latitude: host?.latitude, altitude: host?.height, }, width: store.state.common.liveResolution.width || 960, height: store.state.common.liveResolution.height || 720, fov: fov, near: 0.01, far: 250, roll: gimbal_pitch, pitch: 0, heading: attitude_head, isShowVideoPlan: this.isShowVideoPlan }) }) } } setLiveStatusWs (data) { const { device_sn, live_status } = data if (device_sn && live_status) { this.updataMultiNestData(device_sn, { camera_type: live_status?.[0].video_type || 'wide' }) } } removeCreateFrustum (host) { const parent_sn = host?.parent_sn if (parent_sn) { const curDroneData = this.getCurDroneData(parent_sn) curDroneData.viewInfoFrustum?.clear() } } setFrustumInfoDetails (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 => { return i?.name && i?.name.includes(`aircraft-glf-${dock_sn}`) }) if (aircraftEntity) { // 预计到达下一个航点时间 let arrivalTime = estimated_time_to_next_point if (arrivalTime > 60) { const minute = Math.floor(arrivalTime / 60) const second = Math.round(arrivalTime % 60) arrivalTime = `${minute}m${second}s` } else { arrivalTime = Math.round(arrivalTime) + 's' } 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`, font: '13px monospace', showBackground: true, horizontalOrigin: Cesium.HorizontalOrigin.CENTER, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, disableDepthTestDistance: Number.POSITIVE_INFINITY, pixelOffset: new Cesium.Cartesian2(0, -40), show: true, }) return } } } removeFrustumInfoDetails (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) { aircraftEntity.label.show = false } } } setAircraftGltf (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}`) }) const position = Cesium.Cartesian3.fromDegrees(host?.longitude, host?.latitude, host?.height) if (aircraftEntity) { aircraftEntity.position = new Cesium.ConstantPositionProperty(position) return } if (parent_sn) { this.viewer?.entities.add({ name: `aircraft-glf-${parent_sn}`, position, label: {}, model: { uri: aircraftGltf, // 或 .glb scale: 1.0, // 缩放比例 minimumPixelSize: 64, // 最小像素尺寸(保证模型远处可见) maximumScale: 128, // 最大缩放(可选) }, }) } } } removeViewInfoFrustum () { this.multiNestData.forEach(item => { if ('viewInfoFrustum' in item && item.viewInfoFrustum) { item.viewInfoFrustum?.clear() delete item.viewInfoFrustum } }) } mapEntityRemove (host) { const parent_sn = host?.parent_sn if (parent_sn) { const curDroneData = this.getCurDroneData(parent_sn) curDroneData.viewInfoFrustum?.clear() delete curDroneData.viewInfoFrustum const entities = this.viewer?.entities.values.filter(i => { return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`) }) Array.isArray(entities) && entities.forEach(item => { this.viewer?.entities.remove(item) }) } } mapEntityRemoveAll () { const entities = this.viewer?.entities.values.filter(i => { return i?.name && i?.name.includes(`aircraft-glf-`) }) Array.isArray(entities) && entities.forEach(item => { this.viewer?.entities.remove(item) }) this.multiNestData.length > 0 && (this.multiNestData.forEach(item => { item.viewInfoFrustum?.clear() })) } /** * 更新多机场数据 * @param {*} device_sn 机巢SN * @param {*} data 机巢对应需绑定数据 */ updataMultiNestData (device_sn, data) { const isHave = this.multiNestData.find(item => item.device_sn === device_sn) const isHaveInd = this.multiNestData.findIndex(item => item.device_sn === device_sn) if (isHave) { this.multiNestData[isHaveInd] = { ...this.multiNestData[isHaveInd], ...data } } else { this.multiNestData.push({ device_sn, ...data }) } } /** * 根据机巢sn获取当前机巢对应的相关信息 * @param {*} device_sn */ getCurDroneData (device_sn) { const isHave = this.multiNestData.find(item => item.device_sn === device_sn) if (isHave) return this.multiNestData.find(item => item.device_sn === device_sn) return false } showDetailMapPopup (data) { const that = this this.detailsMapPopupData = data.map(item => { return { ...item, show: true, 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)) } getLabelDom (data) { const that = this const vNode = h(MapPopUpBox, { data, removeLabel: that.removeLabelHandler }) const tooltipContainer = document.createElement('div') tooltipContainer.id = data.uuid tooltipContainer.style.position = 'absolute' tooltipContainer.style.transform = 'translate(-50%,10%)' tooltipContainer.style.pointerEvents = 'none' 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) let dom = document.querySelector(`[id="${item.uuid}"]`) 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' } }) } // 移除弹框标签 removeLabel (data) { const that = this that.removeDom(data) } removeDom (data) { const that = this const dom = document.querySelector(`[id="${data.uuid}"]`) if (dom && dom.parentNode) { that.detailsMapPopupData.forEach(i => i.uuid === data.uuid && (i.show = false)) dom.parentNode.removeChild(dom) } } destroyedCustomDom () { const that = this that.viewer?.scene.postRender.removeEventListener(that.labelBoxRenderHandler) } }