¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |