jihongshun
2025-09-06 1acb4ff3e80f83bd4f0a46c2b5f852dbdac15ae0
航线模拟初始化
已添加2个文件
已修改5个文件
395 ■■■■■ 文件已修改
public/Model/M300.glb 补丁 | 查看 | 原始文档 | blame | 历史
public/index.html 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/role.js 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/template.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/components/cesium-map.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/flightRouteSimulation/index.vue 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/project/components/addPorjectDialog.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/Model/M300.glb
Binary files differ
public/index.html
@@ -218,3 +218,4 @@
</script>
<script src="CesiumPlus/CesiumTools.js"></script>
<script src="CesiumPlus/CesiumVideo/xbsjCameraVideoMixin.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>
src/api/system/role.js
@@ -1,4 +1,4 @@
import request from '@/utils/request'
import request from '@/utils/request';
// æŸ¥è¯¢è§’色列表
export function listRole(query) {
@@ -6,7 +6,7 @@
    url: '/system/role/list',
    method: 'get',
    params: query
  })
  });
}
// æŸ¥è¯¢è§’色详细
@@ -14,7 +14,7 @@
  return request({
    url: '/system/role/' + roleId,
    method: 'get'
  })
  });
}
// æ–°å¢žè§’色
@@ -23,7 +23,7 @@
    url: '/system/role',
    method: 'post',
    data: data
  })
  });
}
// ä¿®æ”¹è§’色
@@ -32,7 +32,7 @@
    url: '/system/role',
    method: 'put',
    data: data
  })
  });
}
// è§’色数据权限
@@ -41,7 +41,7 @@
    url: '/system/role/dataScope',
    method: 'put',
    data: data
  })
  });
}
// è§’色状态修改
@@ -49,12 +49,12 @@
  const data = {
    roleId,
    status
  }
  };
  return request({
    url: '/system/role/changeStatus',
    method: 'put',
    data: data
  })
  });
}
// åˆ é™¤è§’色
@@ -62,7 +62,7 @@
  return request({
    url: '/system/role/' + roleId,
    method: 'delete'
  })
  });
}
// æŸ¥è¯¢è§’色已授权用户列表
@@ -71,7 +71,7 @@
    url: '/system/role/authUser/allocatedList',
    method: 'get',
    params: query
  })
  });
}
// æŸ¥è¯¢è§’色未授权用户列表
@@ -80,7 +80,7 @@
    url: '/system/role/authUser/unallocatedList',
    method: 'get',
    params: query
  })
  });
}
// å–消用户授权角色
@@ -89,7 +89,7 @@
    url: '/system/role/authUser/cancel',
    method: 'put',
    data: data
  })
  });
}
// æ‰¹é‡å–消用户授权角色
@@ -98,7 +98,7 @@
    url: '/system/role/authUser/cancelAll',
    method: 'put',
    params: data
  })
  });
}
// æŽˆæƒç”¨æˆ·é€‰æ‹©
@@ -107,7 +107,7 @@
    url: '/system/role/authUser/selectAll',
    method: 'put',
    params: data
  })
  });
}
// æ ¹æ®è§’色ID查询部门树结构
@@ -115,5 +115,5 @@
  return request({
    url: '/system/role/deptTree/' + roleId,
    method: 'get'
  })
  });
}
src/api/system/template.js
@@ -40,3 +40,11 @@
    data: data
  });
}
// æ ¹æ®åæ ‡æŸ¥è¯¢é™„近的塔设备
export function getScopeTower(longitude, latitude, distance) {
  return request({
    url: `tower/device/deviceList?distance=${distance}&latitude=${latitude}&longitude=${longitude}`,
    method: 'get'
  });
}
src/utils/components/cesium-map.vue
@@ -134,9 +134,12 @@
        navigationInstructionsInitiallyVisible: false,
        imageryProvider: imageryProvider1,
        shadows: false,
        animation: false,
        timeline: false,
        fullscreenButton: false,
        // animation: false,
        // timeline: false,
        // fullscreenButton: false,
        animation: true,
        timeline: true,
        fullscreenButton: true,
        vrButton: false,
//         terrainProvider: new Cesium.CesiumTerrainProvider({
// Â Â Â Â Â Â Â Â Â Â Â Â url : Cesium.IonResource.fromAssetId(1),
src/views/system/flightRouteSimulation/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,345 @@
<template>
   <div class="app-container">
    <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
    file:rounded-md file:border-0
    file:text-sm file:font-semibold
    file:bg-blue-50 file:text-blue-700
    hover:file:bg-blue-100">
    <CesiumMap style="height: 790px;"></CesiumMap>
   </div>
</template>
<script>
import xml2js from 'xml2js'
import CesiumMap from "../../../utils/components/cesium-map.vue";
import { getScopeTower} from "@/api/system/template"
export default {
  name: "flightRouteSimulation",
  components: {
    CesiumMap
  },
  data(){
    return{
        trajectoryData: [
        {
          time: '2025-04-01T13:00:00Z', // ISO 8601格式时间
          position: {
            longitude: 125.14349012556997,  // ç»åº¦
            latitude:46.55684464616254,    // çº¬åº¦
            height:33.157873665937274      // é«˜åº¦(ç±³)
          },
          speed: 0           // é€Ÿåº¦(公里/小时)
        },
        {
          time: '2025-04-01T13:01:00Z', // ISO 8601格式时间
          position: {
            longitude: 125.14347355270496,  // ç»åº¦
            latitude: 46.55684011411894,    // çº¬åº¦
            height: 37.91924525928209      // é«˜åº¦(ç±³)
          },
          speed: 10           // é€Ÿåº¦(公里/小时)
        },
        {
          time: '2025-04-01T13:02:00Z', // ISO 8601格式时间
          position: {
            longitude: 125.14347355270496,  // ç»åº¦126.672952 , 45.744238
            latitude: 46.556840114118955,    // çº¬åº¦
            height:87.91924526022864       // é«˜åº¦(ç±³)
          },
          speed: 20           // é€Ÿåº¦(公里/小时)
        },
        {
          time: '2025-04-01T13:03:00Z', // ISO 8601格式时间
          position: {
            longitude: 125.14347246077013,  // ç»åº¦126.671715 , 45.739693
            latitude: 46.556326837883375,    // çº¬åº¦
            height: 83.15663931356488       // é«˜åº¦(ç±³)
          },
          speed: 30           // é€Ÿåº¦(公里/小时)
        },
        {
          time: '2025-04-01T13:04:00Z', // ISO 8601格式时间
          position: {
            longitude: 125.14347246077013,  // ç»åº¦
            latitude: 46.55632683788337,    // çº¬åº¦
            height: 33.15663931413056       // é«˜åº¦(ç±³)
          },
          speed: 80           // é€Ÿåº¦(公里/小时)
        },
        // æ›´å¤šç‚¹...
      ],
    }
  },
  mounted() {
    console.log(window.viewer)
    console.log(viewer)
  },
  methods: {
    async handleKMZUpload(event) {
      const file = event.target.files[0];
      if (!file) return;
      try {
        const arrayBuffer = await this.readFileAsArrayBuffer(file);
        this.waypoints = await this.parseKMZ(arrayBuffer);
        console.log(">> this.waypoints:", this.waypoints);
        this.fileInfo = `
          èˆªç‚¹:${this.waypoints.length}个 <br/>
          è·ç¦»:${this.calculateTotalDistance(this.waypoints).toFixed(2)}公里
        `;
        this.showFlightPath()
        this.getTowerData()
        this.loadTimeLine()
      } catch (error) {
        console.error(error);
        this.fileInfo = `<p style="color:red">解析失败: ${error.message}</p>`;
      }
    },
    // è¯»å–文件为ArrayBuffer
    readFileAsArrayBuffer(file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result);
        reader.onerror = () => reject(new Error("文件读取失败"));
        reader.readAsArrayBuffer(file);
      });
    },
    // è§£æžKMZ文件
    async parseKMZ(kmzData) {
      // âš ï¸ è¿™é‡Œç›´æŽ¥ä½¿ç”¨å…¨å±€ JSZip å’Œ xml2js(通过CDN引入)
      const zip = await JSZip.loadAsync(kmzData);
      const kmlFile = Object.keys(zip.files).find(name =>
        name.endsWith(".kml")
      );
      console.log(">> kmlFile:", kmlFile);
      if (!kmlFile) throw new Error("未找到KML文件");
      const kmlContent = await zip.file(kmlFile).async("text");
      // xml2js ä»Ž CDN å¼•入后是 window.xml2js
      const parser = new xml2js.Parser();
      const result = await parser.parseStringPromise(kmlContent);
      console.log(">> result:", result);
      const placemarks =
        result.kml.Document[0].Folder[0].Placemark || [];
      const points = [];
      placemarks.forEach(placemark => {
        const coords = placemark.Point?.[0]?.coordinates?.[0];
        const height = placemark["wpml:height"]?.[0];
        if (coords) {
          coords.trim().split(" ").forEach(coord => {
            const [lng, lat] = coord.split(",").map(Number);
            points.push({ lng, lat, alt: height });
          });
        }
      });
      if (points.length === 0) throw new Error("未找到有效航点");
      return points;
    },
    // è®¡ç®—总距离
    calculateTotalDistance(points) {
      let total = 0;
      for (let i = 1; i < points.length; i++) {
        const p1 = points[i - 1];
        const p2 = points[i];
        total += this.haversineDistance(
          p1.lat,
          p1.lng,
          p2.lat,
          p2.lng
        );
      }
      return total;
    },
    haversineDistance(lat1, lon1, lat2, lon2) {
      const R = 6371;
      const dLat = (lat2 - lat1) * Math.PI / 180;
      const dLon = (lon2 - lon1) * Math.PI / 180;
      const a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(lat1 * Math.PI / 180) *
          Math.cos(lat2 * Math.PI / 180) *
          Math.sin(dLon / 2) *
          Math.sin(dLon / 2);
      const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
      return R * c;
    },
    showFlightPath() {
      console.log(">> this.waypoints:", this.waypoints);
      if (this.waypoints.length === 0) return alert('请先上传KMZ文件');
      // viewer.entities.removeAll();
      // è½¬æ¢åæ ‡
      const positions = this.waypoints.map(wp =>
        Cesium.Cartesian3.fromDegrees(wp.lng, wp.lat, wp.alt)
      );
      // ç»˜åˆ¶èˆªçº¿
      viewer.entities.add({
        polyline: {
          positions: positions,
          width: 5,
          material: new Cesium.PolylineGlowMaterialProperty({
            glowPower: 0.2,
            color: Cesium.Color.BLUE
          }),
          arcType: Cesium.ArcType.GEODESIC
        }
      });
      // æ·»åŠ èˆªç‚¹æ ‡è®°
      this.waypoints.forEach((wp, i) => {
        viewer.entities.add({
          position: Cesium.Cartesian3.fromDegrees(wp.lng, wp.lat, wp.alt),
          point: {
            pixelSize: 10,
            color: Cesium.Color.RED,
            outlineColor: Cesium.Color.WHITE,
            outlineWidth: 2
          },
          label: {
            text: `${i + 1}`,
            font: '14pt sans-serif',
            style: Cesium.LabelStyle.FILL_AND_OUTLINE,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM
          }
        });
      });
      //viewer.zoomTo(viewer.entities);
    },
    getTowerData(){
      let lon = this.waypoints[0].lng
      let lat = this.waypoints[0].lat
      let distance = 5000
      //精度维度高度
      getScopeTower(lon,lat,distance).then(res=>{
        if(res.code == 200) {
          console.log(res.rows)
          res.rows.forEach((data) => {
            this.createModel(data);
          });
        }
      })
    },
    createModel(data){
      console.log(data)
      viewer.entities.add({
        id: data.id,
        position: Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, data.altitude),
        model: {
          uri: data?.ardTowerModel?.modelRoute,
          scale: 1,
        },
        label: {
          show: true,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          font: '28px Helvetica',
          outlineColor: Cesium.Color.RED,
          outlineWidth: 3,
          fillColor: Cesium.Color.fromCssColorString('#FFFFFF'), //44c3cc
          text: data.deviceName,
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          pixelOffset: new Cesium.Cartesian2(0.0, -56.0),
          scaleByDistance: new Cesium.NearFarScalar(1000, 0.6, 10000, 0.4),
          pixelOffsetScaleByDistance: new Cesium.NearFarScalar(1000, 0.4, 10000, 0.4),
          disableDepthTestDistance: 100000000
        }
      });
    },
    loadTimeLine(){
      // è®¾ç½®æ—¶é—´èŒƒå›´
      const allTimes = this.trajectoryData.map(item => Cesium.JulianDate.fromIso8601(item.time))
      console.log(allTimes)
      const startTime = allTimes[0]
      const stopTime = allTimes[allTimes.length - 1]
      // const startTime = Cesium.JulianDate.minimum(...allTimes)
      // const stopTime = Cesium.JulianDate.maximum(...allTimes)
      viewer.clock.startTime = startTime.clone()
      viewer.clock.stopTime = stopTime.clone()
      viewer.clock.currentTime = startTime.clone()
      viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP
      viewer.timeline.zoomTo(startTime, stopTime)
      // åˆ›å»ºä½ç½®å’Œé€Ÿåº¦å±žæ€§
      const positionProperty = new Cesium.SampledPositionProperty()
      const speedProperty = new Cesium.SampledProperty(Number)
console.log(">> this.trajectoryData:", this.trajectoryData);
      // æ·»åŠ é‡‡æ ·ç‚¹
      this.trajectoryData.forEach(data => {
        const time = Cesium.JulianDate.fromIso8601(data.time)
        const position = Cesium.Cartesian3.fromDegrees(
          data.position.longitude,
          data.position.latitude,
          data.position.height || 0
        )
        positionProperty.addSample(time, position)
        speedProperty.addSample(time, data.speed)
      })
console.log(">> positionProperty:",positionProperty );
      let entity = viewer.entities.add({
        show: true,
        position: positionProperty,
        label: {
          show: true,
          font: '18px å¾®è½¯é›…黑',
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          showBackground: false,
          backgroundColor: Cesium.Color.fromCssColorString('#123456').withAlpha(0.5),
          outlineColor: Cesium.Color.fromCssColorString('#00ff00'),
          outlineWidth: 3,
          fillColor: Cesium.Color.fromCssColorString("#000000"),
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          verticalOrigin: Cesium.VerticalOrigin.TOP,
          text: '测试文字',
          pixelOffset: new Cesium.Cartesian2(0, -1 * 100),
        },
        model: {
          uri: "/Model/UAV.glb", // æ³¨æ„entitits.add方式加载gltf文件时,这里是uri,不是url,并且这种方式只能加载.glb格式的文件
          scale:  1, // ç¼©æ”¾æ¯”例
          // minimumPixelSize: this.minSize, // æœ€å°åƒç´ å¤§å°ï¼Œå¯ä»¥é¿å…å¤ªå°çœ‹ä¸è§
          minimumPixelSize: 0, // æœ€å°åƒç´ å¤§å°ï¼Œå¯ä»¥é¿å…å¤ªå°çœ‹ä¸è§
          maximumScale: 20000, // æ¨¡åž‹çš„æœ€å¤§æ¯”例尺大小。minimumPixelSize的上限
          incrementallyLoadTextures: true, // åŠ è½½æ¨¡åž‹åŽçº¹ç†æ˜¯å¦å¯ä»¥ç»§ç»­æµå…¥
          runAnimations: true, // æ˜¯å¦å¯åŠ¨æ¨¡åž‹ä¸­åˆ¶å®šçš„gltf动画
          clampAnimations: true, // åˆ¶å®šgltf动画是否在没有关键帧的持续时间内保持最后一个姿势
          shadows: Cesium.ShadowMode.ENABLED,
          heightReference: Cesium.HeightReference.NONE,
          distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 20000.0),
          silhouetteColor: Cesium.Color.fromCssColorString('#00ff00'),
          silhouetteSize: 1,
        },
        path: {
          resolution: 1,
          material: new Cesium.PolylineGlowMaterialProperty({
            glowPower: 0.1,
            color: Cesium.Color.fromCssColorString('#00ffff').withAlpha(0.5),
          }),
          width: 10,
          leadTime: 60,
          trailTime: 60
        },
        orientation: new Cesium.VelocityOrientationProperty(positionProperty),
      })
    }
  }
}
</script>
src/views/system/project/components/addPorjectDialog.vue
@@ -263,7 +263,7 @@
      );
      // åŠ è½½ glTF æ¨¡åž‹
      const entity = viewer.entities.add({
      const entity = model({
        name: row.id,
        position: position,
        orientation: orientation,