| | |
| | | <div class="chooseModel"> |
| | | <el-button type="primary" @click='chooseModel'>选择模型</el-button> |
| | | <div class="modelType"> |
| | | 模型名称:模型塔1 |
| | | 模型名称:{{ modelName }} |
| | | </div> |
| | | <div> |
| | | 模型类型:电塔 |
| | | 模型类型:{{ templateType }} |
| | | </div> |
| | | <el-form label-width="70px" :model="form"> |
| | | <el-form-item label="模板名称"> |
| | | <el-input v-model="form.templateName"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="modelTree"> |
| | | <div class='modelTreeTitle'>巡检点目录</div> |
| | |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="20"> |
| | | <!-- <el-card class="noScroll"> --> |
| | | <InitMap v-if="showMap" @mergePoint="mergePoint"></InitMap> |
| | | <!-- </el-card> --> |
| | | <InitMap v-if="showMap" @mergePoint="mergePoint" :towerUrl="towerUrl"></InitMap> |
| | | </el-col> |
| | | <!-- <el-col :span="8"> |
| | | <div> |
| | | <div class="cameraView"> |
| | | <a>相机视角</a> |
| | | </div> |
| | | <div class="uavView"> |
| | | <a>无人机视角</a> |
| | | </div> |
| | | <div class="operationControl"> |
| | | <a>操控相关(使用以下键控制相机视角)</a> |
| | | <div> |
| | | <el-button type="primary">地面点</el-button> |
| | | <el-button type="primary">空中点</el-button> |
| | | <el-button type="primary">悬停时间</el-button> |
| | | </div> |
| | | <div> |
| | | <div class="container"> |
| | | <div class="keypad"> |
| | | <div class="row"> |
| | | <div class="key">Q-上升</div> |
| | | <div class="key">W-向前</div> |
| | | <div class="key">E-下降</div> |
| | | </div> |
| | | <div class="row"> |
| | | <div class="key">A- 向左</div> |
| | | <div class="key">S- 向后</div> |
| | | <div class="key">D- 向右</div> |
| | | </div> |
| | | </div> |
| | | <div class="arrow-pad"> |
| | | <div class="arrow-row"> |
| | | <div class="arrow empty"></div> |
| | | <div class="arrow">↑ - 向上看</div> |
| | | <div class="arrow empty"></div> |
| | | </div> |
| | | <div class="arrow-row"> |
| | | <div class="arrow">← - 向左转</div> |
| | | <div class="arrow">↓ - 向下看</div> |
| | | <div class="arrow">→ - 向右转</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-col> --> |
| | | </el-row> |
| | | <span slot="footer" class="dialog-footer"> |
| | | <el-button @click="closeModal">取 消</el-button> |
| | | <el-button type="primary" @click="dialogVisible = false">确 定</el-button> |
| | | <el-button type="primary" @click="submit()" >确 定</el-button> |
| | | </span> |
| | | </el-dialog> |
| | | <ChooseModelDialog v-if="showModel" @cancel ='cancel' @getRowData="getRowData" ></ChooseModelDialog> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import { compileToFunctions } from "vue-template-compiler"; |
| | | import CesiumMap from "../../../../utils/components/cesium-map.vue"; |
| | | import InitMap from "../../../../utils/components/init-map.vue"; |
| | | import ChooseModelDialog from './chooseModelDialog.vue'; |
| | | |
| | | import { addPoint , getPointInfo} from "@/api/system/template" |
| | | let globalon = 0 |
| | | let globalat = 0 |
| | | let towerHeight = 47.47 |
| | | //塔的朝向算法所用到的 旋转度数 |
| | | let rotationAngle = Cesium.Math.toRadians(0) |
| | | export default{ |
| | | name:'shootPointDialog', |
| | | components: { |
| | |
| | | ChooseModelDialog, |
| | | InitMap |
| | | }, |
| | | props: { |
| | | templateId: { |
| | | type: String, |
| | | defaule: null |
| | | } |
| | | }, |
| | | data(){ |
| | | return{ |
| | | dialogVisible :true, |
| | | showModel:false, |
| | | // treeData: [{ |
| | | // id: 1, |
| | | // label: '一级 1', |
| | | // children: [{ |
| | | // id: 4, |
| | | // label: '二级 1-1', |
| | | // }] |
| | | // }, { |
| | | // id: 2, |
| | | // label: '一级 2', |
| | | // children: [{ |
| | | // id: 5, |
| | | // label: '二级 2-1' |
| | | // }] |
| | | // }, { |
| | | // id: 3, |
| | | // label: '一级 3', |
| | | // children: [{ |
| | | // id: 7, |
| | | // label: '二级 3-1' |
| | | // }] |
| | | // }], |
| | | treeData:[], |
| | | defaultProps: { |
| | | children: 'children', |
| | |
| | | initialCameraPosition: null, |
| | | initialCameraOrientation: null, |
| | | animationFrameId: null, |
| | | showMap:false |
| | | showMap:false, |
| | | towerUrl:null, |
| | | chooseModelId:null, |
| | | modelName:null, |
| | | templateType:null, |
| | | form:{} |
| | | } |
| | | }, |
| | | mounted(){ |
| | | console.log(this.templateId) |
| | | if(this.templateId) { |
| | | //预览逻辑 |
| | | getPointInfo(this.templateId).then(res=>{ |
| | | if(res.code == 200 ) { |
| | | console.log(res.data) |
| | | this.getRowData(res.data) |
| | | let drawArr = this.convertToTree(res.data.ardListWayPointsLS) |
| | | setTimeout(() => { |
| | | //渲染时间问题 加个延时器 |
| | | this.drawLines(drawArr) |
| | | }, 500); |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | methods:{ |
| | | convertToTree(data) { |
| | | return data.map((item, index) => { |
| | | const parentId = 'air_' + index; |
| | | return { |
| | | id: parentId, |
| | | label: item.targetName, |
| | | longitude: item.longitude, |
| | | latitude: item.latitude, |
| | | height: item.altitude, |
| | | children: (item.ardGroundPoint || []).map((g, i) => { |
| | | return { |
| | | id: parentId + '_ground_' + i, |
| | | label: g.targetName, |
| | | longitude: g.longitude, |
| | | latitude: g.latitude, |
| | | height: g.height |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }, |
| | | drawLines(treeData) { |
| | | console.log(treeData) |
| | | console.log(window) |
| | | console.log(window.viewerM) |
| | | let viewer = window.viewerM |
| | | |
| | | // 收集空中点位置 |
| | | const airPositions = treeData.map(point => { |
| | | return Cesium.Cartesian3.fromDegrees( |
| | | point.longitude, |
| | | point.latitude, |
| | | point.height |
| | | ); |
| | | }); |
| | | |
| | | // 1. 空中点之间用黄色实线连接 |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | positions: airPositions, |
| | | width: 2, |
| | | material: Cesium.Color.YELLOW |
| | | } |
| | | }); |
| | | |
| | | // 2. 每个空中点与地面点用蓝色虚线连接 |
| | | treeData.forEach(point => { |
| | | const airPos = Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.height); |
| | | point.children.forEach(child => { |
| | | const groundPos = Cesium.Cartesian3.fromDegrees(child.longitude, child.latitude, child.height); |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | positions: [airPos, groundPos], |
| | | width: 1, |
| | | material: new Cesium.PolylineDashMaterialProperty({ |
| | | color: Cesium.Color.BLUE, |
| | | dashLength: 8 |
| | | }) |
| | | } |
| | | }); |
| | | }); |
| | | }); |
| | | }, |
| | | addPoint(viewer, position, color, label) { |
| | | viewer.entities.add({ |
| | | name: label, |
| | | position, |
| | | point: { |
| | | pixelSize: 10, |
| | | color: color, |
| | | heightReference: Cesium.HeightReference.NONE |
| | | }, |
| | | label: { |
| | | text: label, |
| | | font: '12px sans-serif', |
| | | pixelOffset: new Cesium.Cartesian2(10, -10), |
| | | style: Cesium.LabelStyle.FILL, |
| | | fillColor: color, |
| | | showBackground: true, |
| | | } |
| | | }); |
| | | }, |
| | | drawLine(viewer, positions, color, dashed = false) { |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | positions, |
| | | width: 2, |
| | | material: dashed |
| | | ? new Cesium.PolylineDashMaterialProperty({ |
| | | color: color, |
| | | dashLength: 8, |
| | | }) |
| | | : color, |
| | | } |
| | | }); |
| | | }, |
| | | handleClose(){ |
| | | this.dialogVisible = false |
| | | }, |
| | |
| | | cancel(){ |
| | | this.showModel = false |
| | | }, |
| | | rotateAllPoints(dataList) { |
| | | return dataList.map(item => { |
| | | console.log(item) |
| | | const groundPoint = Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, item.height); |
| | | const towerPoint = Cesium.Cartesian3.fromDegrees(0, 0, 0); |
| | | |
| | | // 旋转地面点本身(会变,但绕自身旋转不会移动) |
| | | const newGroundPoint = this.rotateAroundPoint( groundPoint,towerPoint, rotationAngle); |
| | | const newGroundCarto = Cesium.Cartographic.fromCartesian(newGroundPoint); |
| | | const newGround = { |
| | | ...item, |
| | | longitude: Cesium.Math.toDegrees(newGroundCarto.longitude), |
| | | latitude: Cesium.Math.toDegrees(newGroundCarto.latitude), |
| | | height: newGroundCarto.height |
| | | }; |
| | | console.log(newGround) |
| | | // 处理子空中点 |
| | | newGround.children = item.children.map(child => { |
| | | const airPoint = Cesium.Cartesian3.fromDegrees(child.longitude, child.latitude, child.height); |
| | | const rotatedAirPoint = this.rotateAroundPoint( airPoint,towerPoint, rotationAngle); |
| | | const rotatedAirCarto = Cesium.Cartographic.fromCartesian(rotatedAirPoint); |
| | | |
| | | return { |
| | | ...child, |
| | | longitude: Cesium.Math.toDegrees(rotatedAirCarto.longitude), |
| | | latitude: Cesium.Math.toDegrees(rotatedAirCarto.latitude), |
| | | height: rotatedAirCarto.height |
| | | }; |
| | | }); |
| | | |
| | | return newGround; |
| | | }); |
| | | }, |
| | | //合并逻辑 将附近n米范围内的点合并 并且处理成数据 |
| | | mergePoint(arr,mergeNumber,viewer){ |
| | | this.treeData = arr |
| | | console.log(mergeNumber) |
| | | const result = this.replaceCloseChildrenWithHighestPoint(this.treeData,mergeNumber); |
| | | console.log(result) |
| | | console.log(viewer) |
| | | console.log(window.viewer) |
| | | const updated = this.insertRaisedPoints(result, 125.1541, 46.5542, 236, 130); |
| | | // const updated = this.insertRaisedPoints(result, 0,0, 236, 130); |
| | | const updated = this.insertRaisedPoints(result, globalon, globalat, towerHeight); |
| | | this.treeData = updated |
| | | |
| | | console.log(updated) |
| | | const airPoints = [] |
| | | this.treeData.forEach(item => { |
| | | const ground = item |
| | |
| | | ground.longitude, ground.latitude, ground.height, |
| | | air.longitude, air.latitude, air.height |
| | | ]), |
| | | width: 2, |
| | | width: 5, |
| | | material: new Cesium.PolylineDashMaterialProperty({ |
| | | color: Cesium.Color.WHITE, |
| | | color: Cesium.Color.RED, |
| | | dashLength: 16.0 |
| | | }) |
| | | } |
| | |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | positions: Cesium.Cartesian3.fromDegreesArrayHeights(positions), |
| | | width: 2, |
| | | material: Cesium.Color.YELLOW |
| | | width: 5, |
| | | material: Cesium.Color.RED |
| | | } |
| | | }) |
| | | } |
| | | console.log(this.treeData) |
| | | }, |
| | | insertRaisedPoints(data, centerLon, centerLat, centerHeight, radius = 130) { |
| | | //数组 塔的精度 塔的纬度 塔的高度 半径 |
| | | insertRaisedPoints(data, centerLon, centerLat, centerHeight, radius = 10) { |
| | | const center = Cesium.Cartesian3.fromDegrees(centerLon, centerLat, centerHeight); |
| | | const result = JSON.parse(JSON.stringify(data)); // 深拷贝避免污染原数据 |
| | | const inserts = []; // 用于存储插入项及其目标位置 |
| | |
| | | }, |
| | | getRowData(row){ |
| | | console.log(row) |
| | | // //选择好模型 并且自动飞到模型位置 |
| | | // const position = Cesium.Cartesian3.fromDegrees(0, 0, 0); |
| | | // // 设置模型方向(可选) |
| | | // // const heading = Cesium.Math.toRadians(135); // 朝东南方向 |
| | | // let model = viewer.entities.getById("modelList"); |
| | | // const heading = Cesium.Math.toRadians(120); // 朝东南方向 |
| | | // const pitch = 0; |
| | | // const roll = 0; |
| | | // const orientation = Cesium.Transforms.headingPitchRollQuaternion( |
| | | // position, |
| | | // new Cesium.HeadingPitchRoll(heading, pitch, roll) |
| | | // ); |
| | | // if(!model) { |
| | | // // 加载 glTF 模型 |
| | | // const entity = viewer.entities.add({ |
| | | // id: "modelList", |
| | | // name: "modelList", |
| | | // position: position, |
| | | // orientation: orientation, |
| | | // model: { |
| | | // uri: "/Model/tower.glb", // 替换成你的模型路径 |
| | | // scale: 1000, |
| | | // }, |
| | | // }); |
| | | // viewer.flyTo(entity) |
| | | // }else { |
| | | // model.orientation = orientation |
| | | // } |
| | | this.showMap = true |
| | | this.modelName = row.modelName |
| | | this.templateType = row.modelType |
| | | if(this.templateId){ |
| | | this.form.templateName = row.templateName || '' |
| | | } |
| | | this.chooseModelId = row.id |
| | | towerHeight = row.towerHeight || 47.47 |
| | | this.showMap =false |
| | | this.$nextTick(()=>{ |
| | | // this.towerUrl = row.modelRoute |
| | | |
| | | if(this.templateId){ |
| | | this.towerUrl = row.ardTowerModel.modelRoute |
| | | }else { |
| | | this.towerUrl = row.modelRoute |
| | | } |
| | | this.showMap = true |
| | | }) |
| | | }, |
| | | beforeDestroy() { |
| | | // 清理 RAF 和事件 |
| | |
| | | }, |
| | | handleDrop (draggingNode, dropNode, dropType, ev) { |
| | | console.log('拖拽完成', { draggingNode, dropNode, dropType }) |
| | | // Element-UI 已经帮你把 treeData 的顺序调好了, |
| | | // 如果要保存顺序到后端,遍历 treeData 发请求即可 |
| | | }, |
| | | submit(){ |
| | | if(!this.form.templateName){ |
| | | return this.$message({ |
| | | message: '请先输入模板名称再保存', |
| | | type: 'warning' |
| | | }) |
| | | } |
| | | // this.dialogVisible = false |
| | | // const dealArr = this.submitDealData() |
| | | const transformed = this.treeData.map((ground, index) => { |
| | | const pointNumber = index + 1; |
| | | const child = ground.children?.[0] || {}; |
| | | |
| | | return { |
| | | altitude: child.height, |
| | | ardGroundPoint: [ |
| | | { |
| | | height: ground.height, |
| | | latitude: ground.latitude, |
| | | longitude: ground.longitude, |
| | | pointNumber: 1, |
| | | targetName: ground.label |
| | | } |
| | | ], |
| | | latitude: child.latitude, |
| | | longitude: child.longitude, |
| | | pointNumber: pointNumber, |
| | | targetName: child.label |
| | | }; |
| | | }) |
| | | console.log(transformed) |
| | | let parmas = { |
| | | templateName:this.form.templateName, |
| | | modelId:this.chooseModelId, |
| | | ardListWayPointsLS:transformed |
| | | } |
| | | addPoint(parmas).then(res=>{ |
| | | console.log(res) |
| | | if(res.code == 200) { |
| | | this.$message({ |
| | | message: '新增模板成功', |
| | | type: 'success' |
| | | }) |
| | | this.$emit('on-submit') |
| | | this.$emit('close') |
| | | } |
| | | }) |
| | | console.log(parmas) |
| | | }, |
| | | // 计算点A绕点B逆时针旋转指定角度后的新位置 |
| | | // 空中点或者地面点笛卡尔坐标 塔的笛卡尔坐标 塔的朝向值 |
| | | rotateAroundPoint(startPoint,pivotPoint,rotationAngle) { |
| | | // 创建一个从B点到本地坐标系的转换矩阵(东方向为X轴,北方向为Y轴,垂直方向为Z轴) |
| | | const transformationMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(pivotPoint); |
| | | // 获取世界坐标系到本地坐标系的转换矩阵 |
| | | const inverseTransformationMatrix = Cesium.Matrix4.inverse(transformationMatrix,new Cesium.Matrix4()); |
| | | // 将A点转换到局部坐标系中 |
| | | const localStartPoint = Cesium.Matrix4.multiplyByPoint(inverseTransformationMatrix, startPoint,new Cesium.Cartesian3()); |
| | | // 计算A点在局部坐标系中逆时针旋转指定角度后的新位置 |
| | | const rotatedX = localStartPoint.x * Math.cos(rotationAngle) + localStartPoint.y * Math.sin(rotationAngle); |
| | | const rotatedY = localStartPoint.y * Math.cos(rotationAngle) - localStartPoint.x * Math.sin(rotationAngle); |
| | | const rotatedZ = localStartPoint.z; // Z轴坐标保持不变 |
| | | // 将旋转后的局部坐标转换回世界坐标系 |
| | | return Cesium.Matrix4.multiplyByPoint(transformationMatrix, new Cesium.Cartesian3(rotatedX, rotatedY, rotatedZ), new Cesium.Cartesian3()); |
| | | } |
| | | } |
| | | } |
| | |
| | | text-align: center; |
| | | } |
| | | .chooseModel{ |
| | | height: 100px; |
| | | height: 120px; |
| | | /* border: 1px solid #dddddd; */ |
| | | } |
| | | .cameraView{ |