06e05149ddd60f26b3aa3639feeb2b5ae45ddfe3..5893e3bd6108ec5847a077977a5dcc499e6b56c7
2025-09-22 jihongshun
视频贴地
dev
5893e3 对比 | 目录
2025-09-22 jihongshun
视频融合
4c7324 对比 | 目录
已添加1个文件
已修改3个文件
444 ■■■■ 文件已修改
public/images/videoMark.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/components/init-map.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/flightRouteSimulation/index.vue 370 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/shootPoint/components/shootPointDialog.vue 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/images/videoMark.jpg
src/utils/components/init-map.vue
@@ -275,7 +275,6 @@
  },
  mounted(){
     this.initCesium();
     console.log(this.deviceData)
  },
  computed: {
    boxWidth() {
src/views/system/flightRouteSimulation/index.vue
@@ -1,5 +1,7 @@
<template>
   <div class="app-container">
    <el-button @click="putVideo">播放视频</el-button>
    <el-button @click="videoMerge">视频融合</el-button>
    <!-- <a @click="showGZ">显示视锥体</a> -->
    <input @change="handleKMZUpload" type="file" id="kmzFile" accept=".kmz" class="block w-full text-sm text-gray-500
    file:mr-4 file:py-2 file:px-4
@@ -18,6 +20,7 @@
    ></div>
      <!-- 左下角倍率信息 -->
    </div>
    <div id="videoSource"></div>
   </div>
</template>
@@ -25,6 +28,31 @@
import xml2js from 'xml2js'
import CesiumMap from "../../../utils/components/cesium-map.vue";
import { getScopeTower} from "@/api/system/template"
let scratchSetViewMatrix3 = new Cesium.Matrix3();
let cmCollection = [];
let pmCollection = [];//视频
let camera_marks = [];
let cameraData = [];
let playersArr = [];
let fixedHandler;
let camMovehandler;
let mouseMovehandler;
let lastEntity;
let lockfunction;
let isCamLock = false; //超范围回弹标记,回弹坐标参数
let camLockId;
let pointLock;
let headingLock;
let pitchLock;
let preVideoScopeElement = null;
let preVideoScopePrimitiveArr = [];
let preVideoScopePrimitiveArrTie = [];
export default {
  name: "flightRouteSimulation",
  components: {
@@ -562,7 +590,7 @@
      // 获取当前时间
      const currentTime = viewer.clock.currentTime;
      const currentPosition = this.positionProperty.getValue(currentTime);
      console.log(currentPosition)
      // 如果获取到位置
      if (currentPosition) {
        // 将位置从 Cartesian 转换为 Cartographic
@@ -596,10 +624,62 @@
          this.zoomRatio = holderZoom ==1? 1 :(holderZoom  /23.76237623)
        }
        
        this.updateFrustum();
        // this.updateFrustum();
        console.log(pmCollection)
        console.log(cmCollection)
        console.log(preVideoScopePrimitiveArrTie)
        pmCollection.map((item)=>{
          if(item.id == "video111111"){
            let obj = this.updateSZScope(holderZoom ==1? 1 :(holderZoom  /23.76237623))
            item.primitive.inverseViewMatrix = obj.M
            item.primitive.frustum = obj.F
            // item.primitive.zoomRatio  = holderZoom == 1? 1 :(holderZoom  /23.76237623)
          }
        })
        cmCollection.map((item)=>{
          if(item.id == "szt111111"){
            let obj = this.updateSZScope(holderZoom ==1? 1 :(holderZoom  /23.76237623))
            item.primitive.inverseViewMatrix = obj.M
            item.primitive.frustum = obj.F
            // item.primitive.zoomRatio  = holderZoom == 1? 1 :(holderZoom  /23.76237623)
          }
        })
        let yyDestination = this.c3Position
        window['cesiumContainerYY'].viewer.camera.setView(
          {
            destination: yyDestination,
            orientation: {
              // 指向
              heading: Cesium.Math.toRadians(Number(this.heading)),
              // 视角
              pitch: Cesium.Math.toRadians(Number(this.pitch)),
              roll: Cesium.Math.toRadians(Number(this.roll))
            }
          }
        )
        this.updateFov()
      } else {
        console.log("无法获取当前位置");
      }
    },
     //更新视椎体位置
    // updateSZScope(lon, lat, alt, camHeading, camPitch, camRoll, LightView,distanceBetween) {
    updateSZScope( LightView,distanceBetween) {
      console.log('11111111111111111111')
      let frustum = new Cesium.PerspectiveFrustum({
        fov: Cesium.Math.toRadians(60),
        aspectRatio: Number(1.77778),
        near: Number(0.05),
        // far: Number(distanceBetween),
        far: Number(1000),
      });
      let inverseViewMatrixNew = this.hpr2m({
        position: this.c3Position,
        heading: Cesium.Math.toRadians(Number(this.heading)),
        pitch: Cesium.Math.toRadians(Number(this.pitch)),
        roll: Cesium.Math.toRadians(Number(this.roll)),
      });
      return {M: inverseViewMatrixNew, F: frustum};
    },
    updateFov(deg) {
      if (this.camera && this.camera.frustum) {
@@ -608,37 +688,271 @@
        this.camera.frustum.fov = newFov
      }
    },
     // 创建幕布
    createVideoElementOld(videoSrc, id) {
      var videoElement = document.createElement("div");
      videoElement.id = "video" + id;
      videoElement.style.position = "absolute";
      videoElement.style.zIndex = "-100";
      videoElement.style.background = "red";
      document.getElementById("videoSource").appendChild(videoElement);
      return videoElement;
    },
    createVideoElementwithMouse(videoSrc,id) {
        let videoElement = document.createElement('video');
        videoElement.id = 'video'+id;
        videoElement.src = videoSrc+'?t='+new Date().getTime();
        videoElement.style.position = 'absolute';
        videoElement.style.zIndex = '-100';
        videoElement.style.visibility = 'hidden';
        videoElement.crossOrigin = 'anonymous';
        videoElement.autoplay = true;
        videoElement.loop = true;
        videoElement.muted = true;
        document.getElementById("videoSource").appendChild(videoElement);
        return videoElement;
    },
    hpr2m(obj, result) {
      // const inverseViewMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(position, headingPitchRoll, undefined, undefined, result);
      const inverseViewMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
        obj.position,
        undefined,
        result
      );
      // const hpr = new Cesium.HeadingPitchRoll(heading - Cesium.Math.PI_OVER_TWO, pitch, roll);
      // var rotQuat = Cesium.Quaternion.fromHeadingPitchRoll(hpr, scratchSetViewQuaternion);
      // var rotMat = Cesium.Matrix3.fromQuaternion(rotQuat, scratchSetViewMatrix3);
      let rotMat = Cesium.Matrix3.fromRotationX(
        Cesium.Math.PI_OVER_TWO,
        scratchSetViewMatrix3
      );
      Cesium.Matrix4.multiplyByMatrix3(
        inverseViewMatrix,
        rotMat,
        inverseViewMatrix
      );
      rotMat = Cesium.Matrix3.fromRotationY(
        -obj.heading,
        scratchSetViewMatrix3
      );
      Cesium.Matrix4.multiplyByMatrix3(
        inverseViewMatrix,
        rotMat,
        inverseViewMatrix
      );
      rotMat = Cesium.Matrix3.fromRotationX(obj.pitch, scratchSetViewMatrix3);
      Cesium.Matrix4.multiplyByMatrix3(
        inverseViewMatrix,
        rotMat,
        inverseViewMatrix
      );
      rotMat = Cesium.Matrix3.fromRotationZ(-obj.roll, scratchSetViewMatrix3);
      Cesium.Matrix4.multiplyByMatrix3(
        inverseViewMatrix,
        rotMat,
        inverseViewMatrix
      );
      return inverseViewMatrix;
    },
    showGZ(){
      let viewer = window['cesiumContainer'].viewer;
      this.camera = new Cesium.Camera(viewer.scene)
      // 视锥体参数
      this.camera.frustum = new Cesium.PerspectiveFrustum({
        // fov: Cesium.Math.PI_OVER_THREE,
        fov:Cesium.Math.toRadians(60),
        aspectRatio: 1920 / 1080,
      });
      this.camera.setView({
        up: Cesium.Cartesian3.UNIT_Z,
        destination: this.c3Position,
        orientation: {
          heading: Cesium.Math.toRadians(this.heading),
          pitch: Cesium.Math.toRadians(this.pitch),
          roll: Cesium.Math.toRadians(this.roll),
        },
      })
      this.frustumOutline = new Cesium.DebugCameraPrimitive({
        id: 111111,
        camera: this.camera,
        frustumSplits: [0.01, 10000],
        color: Cesium.Color.fromCssColorString('#00ff00'),
        // updateOnChange: false,
        updateOnChange: true,
      })
      viewer.scene.primitives.add(this.frustumOutline);
      // this.camera = new Cesium.Camera(viewer.scene)
      // // 视锥体参数
      // this.camera.frustum = new Cesium.PerspectiveFrustum({
      //   // fov: Cesium.Math.PI_OVER_THREE,
      //   fov:Cesium.Math.toRadians(60),
      //   aspectRatio: 1920 / 1080,
      // });
      // this.camera.setView({
      //   up: Cesium.Cartesian3.UNIT_Z,
      //   destination: this.c3Position,
      //   orientation: {
      //     heading: Cesium.Math.toRadians(this.heading),
      //     pitch: Cesium.Math.toRadians(this.pitch),
      //     roll: Cesium.Math.toRadians(this.roll),
      //   },
      // })
      // this.frustumOutline = new Cesium.DebugCameraPrimitive({
      //   id: 111111,
      //   camera: this.camera,
      //   frustumSplits: [0.01, 10000],
      //   color: Cesium.Color.fromCssColorString('#00ff00'),
      //   // updateOnChange: false,
      //   updateOnChange: true,
      // })
      // viewer.scene.primitives.add(this.frustumOutline);
      
      this.listenChange()
    }
      // let videoElement = this.createVideoElementwithMouse("",'11111111');
      preVideoScopeElement = this.createVideoElementOld(
        "http://192.168.5.122:9998/live?port=1234&app=live&stream=mystream",
        '1111111',
        1.7778
      );
      let positionCt3 = this.c3Position
      // 2.2.2.2 准备inverseViewMatrix
        // 准备inverseViewMatrix是为了定义视频拍摄的相机的姿态(位置和方向)
        // 此处设定为当前相机的欧拉角(heading\pitch\roll)和位置信息
      let inverseViewMatrix = this.hpr2m({
            position: positionCt3,
            heading: Cesium.Math.toRadians(Number(this.heading)),
            pitch: Cesium.Math.toRadians(Number(this.pitch)),
            roll: Cesium.Math.toRadians(Number(this.roll)),
        });
      // 2.2.2.3 准备frustum,
        // frustum是为了定义投影体的形状
        // frustum选填,可以直接置为undefined
        let frustum = new Cesium.PerspectiveFrustum({
            fov: Cesium.Math.toRadians(60),
            aspectRatio: Number(1.77778), // 宽高比
            near: Number(0.05), // 设备焦距
            far : 1000.0
        });
        // 2.2.2.4 根据以上信息创建cameraVideo
        let cameraVideo = new Cesium.XbsjCameraVideo({
            inverseViewMatrix: inverseViewMatrix,
            frustum: frustum,
            videoElement: preVideoScopeElement,
            showHelperPrimitive: true
        });
        cameraVideo._primitive.classificationType = 2; // 同时投影地形和3dtiles数据
        cameraVideo.id='sz'+11111111
        let isHole = false;
        cameraVideo._primitive.appearance.material = new Cesium.Material({
            fabric : {
                type : 'Color',
                uniforms : {
                    color : new Cesium.Color(1.0, 0, 0, 0.2)
                }
            }
        });
        viewer.scene.primitives.add(cameraVideo);
        let pmObj = {};
      pmObj.id = 'szt'+111111;
      pmObj.primitive = cameraVideo;
      cmCollection.push(pmObj);
    },
    putVideo(){
      this.createVideoElement('/trailer.mp4','111111',2)
    },
    videoMerge(){
      let node = null
        this.createCameraVideo(node,document.getElementById("video" + 111111))
    },
    // 创建幕布flv技术
    createVideoElement(videoSrc, id, as) {
      let viewer = window['cesiumContainer'].viewer
      let videoElement = document.createElement("video");
      videoElement.id = "video" + id;
      videoElement.style.position = "absolute";
      videoElement.style.zIndex = "100";
      // videoElement.style.top = "0";
      videoElement.style.visibility = 'hidden';
      console.log(viewer.canvas)
      console.log(viewer.canvas.clientHeight)
      let veHeight = viewer.canvas.clientHeight / 2;
      videoElement.style.height = veHeight + "px";
      let veWidth = Number(veHeight) * Number(as);
      videoElement.style.width = veWidth / 2 + "px";
      videoElement.style.bottom = 0;
      videoElement.style.left =
        (Number(viewer.canvas.clientWidth) - veWidth) / 2 + "px";
      // videoElement.crossOrigin = "anonymous";
      videoElement.autoplay = 'autoplay';
      videoElement.loop = 'loop';
      videoElement.controls = 'controls';
      videoElement.muted = "muted";
      // 设置视频源
      // videoElement.src = 'https://media.w3.org/2010/05/sintel/trailer.mp4';
      videoElement.src =videoSrc ;
      // videoElement.type = "video/mp4"
      document.getElementById("videoSource").appendChild(videoElement);
      let playerObj = {};
      playerObj.id = "video" + id;
      playersArr.push(playerObj);
      return videoElement;
    },
     // 清除幕布的div元素
    destroyVideoElement(videoElement) {
      document.getElementById("videoSource").removeChild(videoElement);
    },
    // 创建相机姿势
    createCameraVideo(node, videoElement) {
      console.log(document.getElementById("video" + 111111))
      let viewer = window['cesiumContainer'].viewer;
      let positionCt3 = Cesium.Cartesian3.fromDegrees(125.04159166971326, 46.63755601510411,33.13806141449017)
      // 2.2.2.2 准备inverseViewMatrix
      // 准备inverseViewMatrix是为了定义视频拍摄的相机的姿态(位置和方向)
      // 此处设定为当前相机的欧拉角(heading\pitch\roll)和位置信息
      let inverseViewMatrix = this.hpr2m({
        position: positionCt3,
        heading: Cesium.Math.toRadians(Number(this.heading)),
        pitch: Cesium.Math.toRadians(Number(this.pitch)),
        roll: Cesium.Math.toRadians(Number(this.roll)),
      });
      // 2.2.2.3 准备frustum,
      // frustum是为了定义投影体的形状
      // frustum选填,可以直接置为undefined
      let frustum = new Cesium.PerspectiveFrustum({
        fov: Cesium.Math.toRadians(60),
        aspectRatio: Number(1.77778), // 宽高比
        near: Number(0.05), // 设备焦距
        far : 1000.0
      });
      // 2.2.2.4 根据以上信息创建cameraVideo
      let cameraVideo = new Cesium.XbsjCameraVideo({
        inverseViewMatrix: inverseViewMatrix,
        frustum: frustum,
        videoElement: videoElement,
        showHelperPrimitive: false,
      });
      cameraVideo._primitive.classificationType = 2;
      let isHole = false;
      let alphamaterial = new Cesium.Material({
        fabric: {
          type: 'XbsjCameraVideo',
          uniforms: {
            image: '', // Cesium有bug,此处不能直接赋值video
            alphaImage: '/images/videoMark.jpg',
          },
          components: {
            diffuse: 'texture2D(image, fract(materialInput.st)).rgb',
            // alpha : 'texture2D(alphaImage, fract(repeat * materialInput.st)).a * color.a'
            alpha: 'texture2D(alphaImage, fract(materialInput.st)).r',
          }
        }
      });
      alphamaterial.uniforms.image = videoElement;
      cameraVideo._primitive.appearance.material = alphamaterial;
      // 2.2.2.5 加入到场景中去
      viewer.scene.primitives.add(cameraVideo);
      let pmObj = {};
      pmObj.id = 'video' + '111111';
      pmObj.isHole = isHole;
      pmObj.primitive = cameraVideo;
      pmCollection.push(pmObj);
      // preVideoScopePrimitiveArrTie.push(pmObj);
    },
    //清除视频投影渲染基元
    destroyCameraVideo(vid) {
      if(pmCollection){
        for (let i = 0; i < pmCollection.length; i++) {
          if (vid === pmCollection[i].id) {
            viewer.scene.primitives.remove(pmCollection[i].primitive);
            pmCollection.splice(i, 1);
          }
        }
      }
    },
  }
}
</script>
src/views/system/shootPoint/components/shootPointDialog.vue
@@ -160,10 +160,8 @@
      //预览逻辑
      getPointInfo(this.templateId).then(res=>{
        if(res.code == 200 ) {
          console.log(res.data)
          this.getRowData(res.data)
          let drawArr = this.convertToTree(res.data.ardListWayPointsLS)
          console.log(drawArr)
          setTimeout(() => {
            //渲染时间问题 加个延时器
            this.drawLines(drawArr)
@@ -175,8 +173,6 @@
  methods:{
    toggleSelect(data,index) {
      this.chooseItem = data
      console.log(this.treeData)
      console.log(data)
      // 如果点击的是当前行,则取消选中
      if (this.selectedIndex === index) {
        this.selectedIndex = null
@@ -262,9 +258,9 @@
      this.$refs.initMap.DealVisualCone(data,node)
    },
    dealTreeData(arrList){
      console.log(arrList)
      console.log(this.treeData)
      console.log(this.lastTableArr)
      // console.log(arrList)
      // console.log(this.treeData)
      // console.log(this.lastTableArr)
      this.treeData = arrList
      const childrenArr = arrList
        .filter(item => Array.isArray(item.children))
@@ -275,8 +271,10 @@
        this.treeData = this.treeData.map(item2 => {
        const matchingItem1 = this.lastTableArr.find(item1 => item1.label === item2.label);
          if (matchingItem1) {
            // console.log(item2)
            item2.children = item2.children.map(child2 => {
              const matchingChild1 = matchingItem1.children.find(child1 => child1.label === child2.label);
              // console.log(matchingChild1)
              if (matchingChild1) {
                // 将 actions 从 array1 复制到 this.treeData
                child2.actions = [...matchingChild1.actions];
@@ -294,8 +292,8 @@
        }
      })
      this.waypoints = childrenArr
      console.log(childrenArr)
      console.log(this.treeData)
      // console.log(childrenArr)
      // console.log(this.treeData)
    },
    convertToTree(data) {
      return data.map((item, index) => {
@@ -319,9 +317,9 @@
      });
    },
    drawLines(treeData) {
      console.log(treeData)
      console.log(window)
      console.log(window.viewerM)
      // console.log(treeData)
      // console.log(window)
      // console.log(window.viewerM)
      let viewer = window.viewerM
      // 收集空中点位置
@@ -462,12 +460,39 @@
        return newGround;
      });
    },
    //合并逻辑  将附近n米范围内的点合并 并且处理成数据
    mergePoint(arr,mergeNumber,viewer){
      console.log(arr)
      console.log(this.treeData)
      console.log(towerHeight)
      console.log(this.lastTableArr)
      // this.treeData = arr
      this.treeData = this.lastTableArr
      // this.treeData = this.lastTableArr
      this.treeData = arr.map((groundPoint, index) => {
        // 查找对应的新地面点数据
        const newGroundPoint = this.treeData[index];
        if (newGroundPoint && newGroundPoint.children) {
          // 更新每个地面点的子点经纬度
          groundPoint.children = groundPoint.children.map((child, childIndex) => {
            const newChild = newGroundPoint.children[childIndex];
            if (newChild) {
              return {
                ...child,
                longitude: newChild.longitude,
                latitude: newChild.latitude,
                height: newChild.height,
                heading: newChild.heading,
                pitch: newChild.pitch,
                actions: newChild.actions
              };
            }
            return child;
          });
        }
        return groundPoint;
      });
      console.log(this.treeData)
      const result = this.replaceCloseChildrenWithHighestPoint(this.treeData,mergeNumber);
      // const updated = this.insertRaisedPoints(result, 0,0, 236, 130);
      const updated = this.insertRaisedPoints(result, globalon, globalat, towerHeight);
@@ -477,6 +502,7 @@
        const ground = item
        const air = item.children[0]
        console.log(air)
        // 地面点到7空中点 — 白色虚线
        viewer.entities.add({
          polyline: {
@@ -507,6 +533,7 @@
      console.log(this.treeData)
    },
    //数组 塔的精度 塔的纬度  塔的高度  半径
    // insertRaisedPoints(data, centerLon, centerLat, centerHeight, radius = 15) {
    insertRaisedPoints(data, centerLon, centerLat, centerHeight, radius = 15) {
      const center = Cesium.Cartesian3.fromDegrees(centerLon, centerLat, centerHeight);
      const result = JSON.parse(JSON.stringify(data)); // 深拷贝避免污染原数据
@@ -523,6 +550,7 @@
        console.log(cartA)
        console.log(cartB)
        const closest = this.closestPointOnSegment(cartA, cartB, center);
        console.log(result)
        const distance = Cesium.Cartesian3.distance(closest, center);
        console.log(distance)
        if (distance < radius) {
@@ -758,7 +786,6 @@
              //   actionValue: item.extra.value
              // }))
              actions:child.actions.map(action => {
                console.log(action)
                switch (action.type) {
                  case "悬停":
                    return { hoverTime: parseFloat(action.extra.value) };
@@ -823,20 +850,12 @@
     return Cesium.Matrix4.multiplyByPoint(transformationMatrix, new Cesium.Cartesian3(rotatedX, rotatedY, rotatedZ), new Cesium.Cartesian3());
    },
    dealAction(data){
      console.log(this.treeData)
      console.log(this.waypoints)
      console.log(data)
      console.log(this.lastTableArr)
      const byLabel = this.addOrUpdate(this.lastTableArr, data, 'label', this)
      this.waypoints  = this.lastTableArr
      console.log(byLabel)
      console.log(this.lastTableArr)
      console.log( this.waypoints)
      this.treeData = this.lastTableArr
    },
    addOrUpdate(list, newItem, key = 'label', vm = null) {
      let index = list.findIndex(item => item[key] === newItem[key]);
      console.log(index)
      if (index !== -1) {
        if (vm) {
          vm.$set(list, index, newItem); // Vue2 响应式替换
@@ -867,10 +886,10 @@
      // 清空原来的entities集合并重新添加过滤后的实体
      window.viewerM.entities.removeAll(); // 清空所有实体
      filteredEntities.forEach(entity => window.viewerM.entities.add(entity))
      console.log(item)
      console.log(this.treeData)
       console.log(this.lastTableArr)
      console.log(window.viewerM.entities.values)
      // console.log(item)
      // console.log(this.treeData)
      //  console.log(this.lastTableArr)
      // console.log(window.viewerM.entities.values)
      // console.log(window.viewerM.entities.values[0].id)
      // console.log(window.viewerM.entities.values[1].id)
      // console.log(window.viewerM.entities.values[2].id)