| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <a @click="showGZ">显示视锥体</a> |
| | | <!-- <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 |
| | | file:rounded-md file:border-0 |
| | |
| | | file:bg-blue-50 file:text-blue-700 |
| | | hover:file:bg-blue-100"> |
| | | <CesiumMap style="height: 790px;" :showAnimation = true :showCesiumCompass = true :cesiumId="'cesiumContainer'"></CesiumMap> |
| | | <CesiumMap style="height: 324px;width: 576px;" :showAnimation = false :showCesiumCompass = false :cesiumId="'cesiumContainerYY'" class="eagleye"></CesiumMap> |
| | | <CesiumMap style="height: 324px;width: 576px;" :showAnimation = false :showCesiumCompass = false :showCrosshair = true :cesiumId="'cesiumContainerYY'" class="eagleye"></CesiumMap> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | holderHeadingProperty : new Cesium.SampledProperty(Number),//云台朝向heading |
| | | holderPitchProperty : new Cesium.SampledProperty(Number),//云台朝向pitch |
| | | holderRollProperty : new Cesium.SampledProperty(Number),//云台朝向roll |
| | | // flyHeadingProperty : new Cesium.SampledProperty(Number),//无人机heading |
| | | holderZoomProperty : new Cesium.SampledProperty(Number),//变倍 |
| | | currentZoom:1, |
| | | filename :'' |
| | | } |
| | | }, |
| | | mounted() { |
| | |
| | | this.waypoints = await this.parseKMZ(arrayBuffer); |
| | | //加点逻辑 比如向第2-n-1中插入元素 经纬度为当前的 heading pitch roll 下一个的 |
| | | const dealArr = []; |
| | | console.log(">> this.waypoints:", this.waypoints); |
| | | // console.log(">> this.waypoints:", this.waypoints); |
| | | for (let i = 0; i < this.waypoints.length - 1; i++) { |
| | | dealArr.push(this.waypoints[i]); // Push the current element |
| | | const current = this.waypoints[i]; |
| | |
| | | 'flyHeading':next.flyHeading, |
| | | "heading": current.heading, |
| | | "pitch": current.pitch, |
| | | "roll": current.roll |
| | | "roll": current.roll, |
| | | "zoom":next.zoom || 1 |
| | | }); |
| | | } |
| | | |
| | | // Push the last element without any change |
| | | dealArr.push(this.waypoints[this.waypoints.length - 1]); |
| | | |
| | | console.log(dealArr) |
| | | this.waypoints = dealArr |
| | | console.log(">> this.waypoints:", this.waypoints); |
| | | |
| | | this.fileInfo = ` |
| | | 航点:${this.waypoints.length}个 <br/> |
| | | 距离:${this.calculateTotalDistance(this.waypoints).toFixed(2)}公里 |
| | | `; |
| | | this.showFlightPath() |
| | | this.getTowerData() |
| | | const startDate = new Date("2025-04-01T13:00:00Z"); |
| | | const speeds = [10, 10, 20, 30, 80, 100, 60, 50, 40, 30, 20, 10]; // 自定义速度 |
| | | // const startDate = new Date("2025-04-01T13:00:00Z"); |
| | | const match = this.filename.match(/(\d{4}-\d{2}-\d{2})_(\d{2}-\d{2}-\d{2})/); |
| | | |
| | | // const result = this.waypoints.map((item, index) => { |
| | | console.log(result) |
| | | if (match) { |
| | | const datePart = match[1]; // 2025-09-08 |
| | | const timePart = match[2]; // 16-36-01 |
| | | |
| | | // 替换时间部分的 '-' 为 ':' |
| | | const isoString = `${datePart}T${timePart.replace(/-/g, ':')}Z`; |
| | | |
| | | var date = new Date(isoString); |
| | | } |
| | | // const startDate = new Date("2025-04-01T13:00:00Z"); |
| | | const startDate = new Date(date.toISOString()); |
| | | const result = dealArr.map((item, index) => { |
| | | // const result = this.waypoints.map((item, index) => { |
| | | // 每次加一天 |
| | |
| | | heading:item.heading|| 0, |
| | | pitch:item.pitch|| 0, |
| | | roll:item.roll || 0, |
| | | // speed: speeds[index] || 0, |
| | | speed: 80, |
| | | zoom:item.zoom, |
| | | flyHeading:item.flyHeading |
| | | }; |
| | | }); |
| | | this.trajectoryData = result |
| | | console.log(this.trajectoryData) |
| | | // console.log(result); |
| | | // console.log(this.trajectoryData) |
| | | this.loadTimeLine() |
| | | } catch (error) { |
| | | console.error(error); |
| | | this.fileInfo = `<p style="color:red">解析失败: ${error.message}</p>`; |
| | | } |
| | | setTimeout(()=>{ |
| | | this.showGZ() |
| | | },1000) |
| | | }, |
| | | // 读取文件为ArrayBuffer |
| | | readFileAsArrayBuffer(file) { |
| | | this.filename = file.name |
| | | return new Promise((resolve, reject) => { |
| | | const reader = new FileReader(); |
| | | reader.onload = () => resolve(reader.result); |
| | |
| | | // xml2js 从 CDN 引入后是 window.xml2js |
| | | const parser = new xml2js.Parser(); |
| | | const result = await parser.parseStringPromise(kmlContent); |
| | | console.log(">> result:", result); |
| | | // console.log(">> result:", result); |
| | | |
| | | const placemarks = |
| | | result.kml.Document[0].Folder[0].Placemark || []; |
| | |
| | | const height = placemark["wpml:height"]?.[0]; |
| | | // 提取 aircraftHeading |
| | | // 提取 action 里的参数 |
| | | let heading = null; |
| | | let gimbalPitch = null, gimbalRoll = null, gimbalYaw = null; |
| | | // 初始化 Pitch, Roll 和 Heading |
| | | let totalPitch = 0; |
| | | let totalRoll = 0; |
| | | let totalHeading = 0; |
| | | let flyHeadingData = 0; |
| | | let currentzoom = 1 |
| | | if (placemark["wpml:actionGroup"]?.[0]?.["wpml:action"]) { |
| | | placemark["wpml:actionGroup"][0]["wpml:action"].forEach(action => { |
| | | // 处理 gimbalRotate 和 rotateYaw 函数 |
| | |
| | | totalPitch += gimbalPitch; |
| | | totalRoll += gimbalRoll; |
| | | totalHeading += gimbalYaw; |
| | | console.log('totalHeading里面循环' + totalHeading) |
| | | } |
| | | // 处理 rotateYaw 函数 |
| | | if (action['wpml:actionActuatorFunc'].includes("rotateYaw")) { |
| | | // 获取 aircraftHeading |
| | | const aircraftHeading = parseFloat(action['wpml:actionActuatorFuncParam'][0]["wpml:aircraftHeading"][0]); |
| | | console.log(aircraftHeading) |
| | | flyHeadingData = aircraftHeading |
| | | // 将 aircraftHeading 累加到 totalHeading |
| | | totalHeading += aircraftHeading; |
| | | console.log('totalHeading外层外层外层外层外层循环' + totalHeading) |
| | | } |
| | | // 处理 rotateYaw 函数 |
| | | if (action['wpml:actionActuatorFunc'].includes("zoom")) { |
| | | const aircraftHeading = parseFloat(action['wpml:actionActuatorFuncParam'][0]["wpml:focalLength"][0]); |
| | | currentzoom = aircraftHeading |
| | | } |
| | | }); |
| | | } |
| | | // 输出结果 |
| | | console.log("Total Pitch:", totalPitch); |
| | | console.log("Total Roll:", totalRoll); |
| | | console.log("Total Heading:", totalHeading); |
| | | if (coords) { |
| | | coords.trim().split(" ").forEach(coord => { |
| | | console.log(coord) |
| | | const [lng, lat] = coord.split(",").map(Number); |
| | | points.push({ lng, lat, alt: height,heading:totalHeading || 0,pitch:totalPitch || 0,roll:totalRoll|| 0 ,flyHeading:flyHeadingData}); |
| | | points.push({ lng, lat, alt: height,heading:totalHeading || 0,pitch:totalPitch || 0,roll:totalRoll|| 0 ,flyHeading:flyHeadingData,zoom : currentzoom || 1}); |
| | | }); |
| | | } |
| | | }); |
| | |
| | | }, |
| | | showFlightPath() { |
| | | let viewer = window['cesiumContainer'].viewer |
| | | console.log(">> this.waypoints:", this.waypoints); |
| | | // console.log(">> this.waypoints:", this.waypoints); |
| | | if (this.waypoints.length === 0) return alert('请先上传KMZ文件'); |
| | | // 转换坐标 |
| | | const positions = this.waypoints.map(wp => |
| | |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | //viewer.zoomTo(viewer.entities); |
| | | }, |
| | | getTowerData(){ |
| | | let lon = this.waypoints[0].lng |
| | |
| | | loadTimeLine(){ |
| | | let viewer = window['cesiumContainer'].viewer |
| | | // 设置时间范围 |
| | | console.log(this.trajectoryData) |
| | | const allTimes = this.trajectoryData.map(item => Cesium.JulianDate.fromIso8601(item.time)) |
| | | // 获取所有时间,并减去8小时 |
| | | // const allTimes = this.trajectoryData.map(item => { |
| | | // const time = Cesium.JulianDate.fromIso8601(item.time) |
| | | // return Cesium.JulianDate.addHours(time, +8, new Cesium.JulianDate()) |
| | | // }) |
| | | console.log(allTimes) |
| | | const startTime = allTimes[0] |
| | | const stopTime = allTimes[allTimes.length - 1] |
| | | console.log(startTime) |
| | | console.log(stopTime) |
| | | // 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.CLAMPED |
| | | viewer.timeline.zoomTo(startTime, stopTime) |
| | | const duration = Cesium.JulianDate.secondsDifference(stopTime, startTime); |
| | | console.log(`时间轴总时长: ${duration} 秒`) |
| | | |
| | | // 创建位置和速度属性 |
| | | const speedProperty = new Cesium.SampledProperty(Number) |
| | | |
| | | console.log(">> this.trajectoryData:", this.trajectoryData); |
| | | // 添加采样点 |
| | | this.trajectoryData.forEach(data => { |
| | | const time = Cesium.JulianDate.fromIso8601(data.time) |
| | | console.log("存储的时间:", Cesium.JulianDate.toDate(time)) |
| | | const position = Cesium.Cartesian3.fromDegrees( |
| | | data.position.longitude, |
| | | data.position.latitude, |
| | |
| | | this.positionProperty.addSample(time, position) |
| | | speedProperty.addSample(time, data.speed) |
| | | // 计算朝向四元数 |
| | | // const hpr = new Cesium.HeadingPitchRoll( |
| | | // Cesium.Math.toRadians(data.flyHeading), |
| | | // Cesium.Math.toRadians(data.pitch), |
| | | // Cesium.Math.toRadians(data.roll) |
| | | // ); |
| | | const hpr = new Cesium.HeadingPitchRoll( |
| | | Cesium.Math.toRadians(data.flyHeading), |
| | | Cesium.Math.toRadians(0), |
| | |
| | | |
| | | // // 添加飞机朝向采样 |
| | | this.orientation.addSample(time, quaternion); |
| | | //无人机heading |
| | | // this.flyHeadingProperty.addSample(time, data.flyHeading) |
| | | //云台 |
| | | const szthpr = new Cesium.HeadingPitchRoll( |
| | | Cesium.Math.toRadians(data.heading), |
| | | Cesium.Math.toRadians(data.pitch), |
| | | Cesium.Math.toRadians(data.roll) |
| | | ); |
| | | console.log(szthpr) |
| | | // console.log( Cesium.Math.toRadians(data.heading)) |
| | | const sztquaternion = Cesium.Transforms.headingPitchRollQuaternion(position, szthpr); |
| | | console.log(sztquaternion) |
| | | // this.sztOrientation.addSample(time, sztquaternion); |
| | | this.holderHeadingProperty.addSample(time, data.heading) |
| | | this.holderPitchProperty.addSample(time, data.pitch) |
| | | this.holderRollProperty.addSample(time, data.roll) |
| | | // console.log(this.sztOrientation) |
| | | // console.log(this.orientation) |
| | | }) |
| | | // console.log(">> positionProperty:",this.positionProperty ); |
| | | |
| | | //变倍 |
| | | this.holderZoomProperty.addSample(time,data.zoom) |
| | | }) |
| | | let entity = viewer.entities.add({ |
| | | show: true, |
| | | position: this.positionProperty, |
| | |
| | | trailTime: 60 |
| | | }, |
| | | orientation:this.orientation |
| | | // orientation: { |
| | | // heading: Cesium.Math.toRadians(this.flyHeadingProperty), |
| | | // pitch: Cesium.Math.toRadians(0), |
| | | // roll: Cesium.Math.toRadians(0), |
| | | // } |
| | | }) |
| | | }, |
| | | // 更新视锥体位置和朝向 |
| | |
| | | } |
| | | } |
| | | ) |
| | | this.updateFov() |
| | | }, |
| | | listenChange() { |
| | | let viewer = window['cesiumContainer'].viewer; |
| | |
| | | if (viewer.clock.shouldAnimate) { |
| | | this.getCurrentPosition() |
| | | // 执行动作 |
| | | // console.log("Clock is running, performing action..."); |
| | | // 你的代码逻 |
| | | } else { |
| | | // 时钟暂停时不执行 |
| | | // console.log("Clock is paused, no action performed."); |
| | | } |
| | | }); |
| | | }, |
| | | getCurrentPosition() { |
| | | let viewer = window['cesiumContainer'].viewer; |
| | | // console.log(this.sztOrientation) |
| | | // 获取当前时间 |
| | | const currentTime = viewer.clock.currentTime; |
| | | console.log("当前时间:", Cesium.JulianDate.toDate(currentTime)); |
| | | // console.log(Cesium.JulianDate.toDate(currentTime)) |
| | | // console.log('start:', Cesium.JulianDate.toDate(this.positionProperty._property._times[0])); |
| | | // console.log('stop:', Cesium.JulianDate.toDate(this.positionProperty._property._times.slice(-1)[0])); |
| | | // const currentTime = Cesium.JulianDate.addHours(currentTime, -8, new Cesium.JulianDate()); |
| | | |
| | | // console.log(currentTime) |
| | | // 从 positionProperty 中获取当前位置 |
| | | const currentPosition = this.positionProperty.getValue(currentTime); |
| | | |
| | | // console.log('currentPosition>>>>>>>>>>>>>>>>>>>>>>>' + currentPosition) |
| | | // 如果获取到位置 |
| | | if (currentPosition) { |
| | | // 将位置从 Cartesian 转换为 Cartographic |
| | |
| | | this.heading = holderUavHeading |
| | | } |
| | | //云台变化 |
| | | // const sztquaternion = this.sztOrientation.getValue(currentTime) |
| | | // console.log(sztquaternion) |
| | | // if (sztquaternion) { |
| | | // const hpr = Cesium.HeadingPitchRoll.fromQuaternion(sztquaternion) |
| | | // console.log(hpr) |
| | | // this.heading = (Cesium.Math.toDegrees(hpr.heading).toFixed(2)-143.32384000000002 ) |
| | | // // this.heading = 180 |
| | | // this.pitch = (Cesium.Math.toDegrees(hpr.pitch).toFixed(2) - 14.33) |
| | | // // this.roll = Cesium.Math.toDegrees(hpr.roll).toFixed(2) |
| | | // this.roll = 0 |
| | | // // console.log('当前位置 - heading ' + this.heading) |
| | | // console.log(`当前位置 - heading: ${this.heading}, pitch: ${this.pitch}, roll: ${this.roll}`); |
| | | // } |
| | | const holderYtHeading = this.holderHeadingProperty.getValue(currentTime) |
| | | const holderYtPitch = this.holderPitchProperty.getValue(currentTime) |
| | | const holderYtRoll = this.holderRollProperty.getValue(currentTime) |
| | | if(holderYtHeading) { |
| | | // this.heading = Cesium.Math.toDegrees(holderYtHeading).toFixed(2) |
| | | // this.pitch = Cesium.Math.toDegrees(holderYtPitch).toFixed(2) |
| | | // this.roll = Cesium.Math.toDegrees(holderYtRoll).toFixed(2) |
| | | this.heading = holderYtHeading |
| | | this.pitch = holderYtPitch |
| | | this.roll = holderYtRoll |
| | | console.log('当前位置 - heading ' + this.heading) |
| | | console.log(`当前位置 - heading: ${this.heading}, pitch: ${this.pitch}, roll: ${this.roll}`); |
| | | // console.log('当前位置 - heading ' + this.heading) |
| | | // console.log(`当前位置 - heading: ${this.heading}, pitch: ${this.pitch}, roll: ${this.roll}`); |
| | | } |
| | | // console.log(sztquaternion) |
| | | // if (sztquaternion) { |
| | | // const hpr = Cesium.HeadingPitchRoll.fromQuaternion(sztquaternion) |
| | | // console.log(hpr) |
| | | // this.heading = (Cesium.Math.toDegrees(hpr.heading).toFixed(2)-143.32384000000002 ) |
| | | // // this.heading = 180 |
| | | // this.pitch = (Cesium.Math.toDegrees(hpr.pitch).toFixed(2) - 14.33) |
| | | // // this.roll = Cesium.Math.toDegrees(hpr.roll).toFixed(2) |
| | | // this.roll = 0 |
| | | // // console.log('当前位置 - heading ' + this.heading) |
| | | // console.log(`当前位置 - heading: ${this.heading}, pitch: ${this.pitch}, roll: ${this.roll}`); |
| | | // } |
| | | //变倍 |
| | | const holderZoom = this.holderZoomProperty.getValue(currentTime) |
| | | if(holderZoom) { |
| | | this.currentZoom = holderZoom ==1? 1 :(holderZoom /23.76237623) |
| | | } |
| | | |
| | | this.updateFrustum(); |
| | | } else { |
| | | console.log("无法获取当前位置"); |
| | | } |
| | | }, |
| | | updateFov(deg) { |
| | | if (this.camera && this.camera.frustum) { |
| | | let originalFov = Cesium.Math.toRadians(60); // 1.047 rad |
| | | let newFov = originalFov / this.currentZoom; // 约 0.1047 rad |
| | | this.camera.frustum.fov = newFov |
| | | } |
| | | }, |
| | | showGZ(){ |
| | |
| | | this.camera = new Cesium.Camera(viewer.scene) |
| | | // 视锥体参数 |
| | | this.camera.frustum = new Cesium.PerspectiveFrustum({ |
| | | fov: Cesium.Math.PI_OVER_THREE, |
| | | // aspectRatio: this.aspectRatio, |
| | | // fov: Cesium.Math.PI_OVER_THREE, |
| | | fov:Cesium.Math.toRadians(60), |
| | | aspectRatio: 1920 / 1080, |
| | | }); |
| | | this.camera.setView({ |
| | |
| | | updateOnChange: true, |
| | | }) |
| | | viewer.scene.primitives.add(this.frustumOutline); |
| | | |
| | | this.listenChange() |
| | | } |
| | | |