Cesium+earthSD實現相機飛行動畫


Cesium+earthSD實現相機飛行動畫

效果:

 

 

 原理:

1.通過earthsdk將在兩個點之間畫出飛線,得到飛線點集數據

2.通過飛線點集數據,計算出每個點上的攝像機方向,得到攝像機方向集合

 注意:在經過經度180度線的時候,會有攝像機反向問題,需特別處理 

3.將飛線點集數據和攝像機方向集合生成  Path  對象,完成攝像機貼線飛行

4.在飛線路徑外圍添加 polylineVolume 管道對象達成效果

 注意:當管道長度很長的時候,中間會出現管道扭曲,未找到原因。

  解決辦法:將飛線沒兩個點拆成一個線段建立多個管道。由於entityid不能重復,所以需要使用 CustomDataSource 創建實體集合

代碼:

  調用:

createCameraFollow(item)

  1.創建地球

            init() {
                XE.ready()
                    .then(this.startup)
                    .then(() => {
                        this._earth.camera.position = [2.0991215, 0.549, 2000]
                        this._earth.camera.rotation = [0, -0.41493986255762305, 0]
                        this.mapInit = true
                    })
            },
            startup() {
                let earth = new XE.Earth('earthContainer')
                this._earth = earth

                earth.weather.atmosphere.enabled = false

                earth.interaction.picking.enabled = true
                earth.interaction.picking.hoverEnable = false

                const bloom = earth.postProcess.bloom
                bloom.enabled = true
                bloom.glowOnly = false
                bloom.contrast = 119
                bloom.brightness = -0.4
                bloom.delta = 0.9
                bloom.sigma = 3.78
                bloom.stepSize = 5
                bloom.isSelected = false

                earth.sceneTree.root = {
                    children: [
                        {
                            czmObject: {
                                name: '影像',
                                xbsjType: 'Imagery',
                                xbsjImageryProvider: {
                                    XbsjImageryProvider: {
                                        url:
                                          'https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}',
                                        srcCoordType: 'WGS84',
                                        dstCoordType: 'WGS84'
                                    }
                                }
                            }
                        }
                    ]
                }

                var tileset = earth.sceneTree.$refs.tileset.czmObject
            },

  2.主要方法:

        // 創建相機跟隨
        createCameraFollow(line) {
            // var p1 =  [
            //     [[DtoR(-122.174699), DtoR(37.433888), 0], [ 2.0991215, 0.5497211,50] ]
            // ];
            if (line.tgtIp === '' || line.srcIp === line.tgtIp) {
                return
            }
            var p1 =  [
                [
                    [this.DtoR(line.srcIpLon * 1), this.DtoR(line.srcIpLat * 1), 0],
                    [this.DtoR(line.clueLon * 1), this.DtoR(line.clueLat * 1), 0]
                ]
            ];
            // 獲取飛行距離
            // let distance = this.getDistance(p1[0])
            let parms = [
                this.RtoD(p1[0][0][0]),
                this.RtoD(p1[0][0][1]),
                this.RtoD(p1[0][1][0]),
                this.RtoD(p1[0][1][1]),
            ]
            let distance = this.getFlatternDistance(...parms)
            // 獲取飛線路徑
            let flyLine = this.getFlyLine(this._earth, p1)
            // 添加管道
            this.createCylinder(this._earth, flyLine)
            // 攝像機貼線飛行
            setTimeout(() => {
                this.cameraFly(flyLine, distance)
            }, 100)
        },
        // 攝像機貼線飛行
        cameraFly (flyLine, distance) {
            // 計算攝像機方向
            let rotations = this.countRotations(flyLine)
            const leafConfig = {
                ref: 'path1',
                czmObject: {
                    xbsjType: 'Path',
                    positions: flyLine,
                    rotations: rotations,
                    show: false, // 顯示路徑
                    loop: false, // 是否為環線
                    showDirection: false, // 顯示方向(默認為true)
                    // 是否處於播放狀態
                    // 如果屬性值為true,則所有'current'開頭的相關屬性會動態發生變化。 可以通過手動設置為false,來結束播放狀態。 當loopPlay屬性為false時,playing屬性會在路徑播放到最后一個關鍵點的位置時,自動變為false。
                    playing: true,
                    // 是否循環播放
                    // 如果為false,則playing設置為true時,會從當前位置播放到最后一個關鍵點,並停止播放,此時playing屬性會自動變成false。 若此屬性為true時,播放到最后一個關鍵點以后,將自動重第一個關鍵點繼續播放。
                    loopPlay: true
                }
            }
            this._earth.sceneTree.root.children.push(leafConfig)
            var path1 = this._earth.sceneTree.$refs.path1.czmObject;
            // path1.flyTo();
            path1.show = false; // 是否顯示
            path1.currentSpeed = (distance)/5; // 運行速度
            path1.currentD = 1;  // 當前位置,單位米
            path1.cameraAttached = true; // 綁定相機
            path1.playing = true; // 飛行
            // path1._currentPosition  = true; // 飛行
            path1.preUpdateEvalString  = `
                    if (p.currentD === 0) {
                        var path1 = p.earth.sceneTree.$refs.path1.czmObject;
                        path1.flyTo();
                        let dataSources = p.earth.czm.viewer.dataSources
                        dataSources.removeAll()
                        p.playing = false
                        path1.destroy();
                    }
                `;
            // 定義一個pin用來跟蹤路徑
            // const pin = new XE.Obj.Pin(this._earth);
            // XE.MVVM.track(pin, 'position', path1, 'currentPosition');
        },
        // 添加管道-圓柱形-三角柱
        createCylinder(earth, line) {
            var viewer = earth.czm.viewer
            console.log(line)
            // 添加點
            function addpoint(viewer, item) {
                function RtoD(val) {
                    let r = 180 * val / Math.PI
                    return r
                }
                viewer.entities.add({
                    name: 'shin_point2',
                    position: Cesium.Cartesian3.fromDegrees(RtoD(item[0]), RtoD(item[1]), item[2]),
                    point: {
                        show: true,
                        color: Cesium.Color.RED.withAlpha(0.3),
                        pixelSize: 2
                    }
                })
            }
            for(let item of line){
                // addpoint(viewer, item)
            }
            // 將飛線每兩個點處理成一條線段
            let line1 = JSON.parse(JSON.stringify(line))
            let lineArr =[]
            for (let i in line1) {
                if (i>0) {
                    let lineS = []
                    lineS.push(this.RtoD(line1[i-1][0]))
                    lineS.push(this.RtoD(line1[i-1][1]))
                    lineS.push(line1[i-1][2] - 30000)
                    lineS.push(this.RtoD(line1[i][0]))
                    lineS.push(this.RtoD(line1[i][1]))
                    lineS.push(line1[i][2] - 30000)
                    lineArr.push(lineS)
                }
            }
            
            // 兩種顏色交替材質
            /*var stripeMaterial = new Cesium.StripeMaterialProperty({
                evenColor: Cesium.Color.WHITE.withAlpha(0.5),
                oddColor: Cesium.Color.YELLOW.withAlpha(1),
                repeat: 20.0,  // 交替次數
                // orientation: Cesium.StripeOrientation.VERTICAL, // 材質方向
                // repeat: 1500.0,  // 交替次數
            });*/
            var stripeMaterial = new Cesium.ImageMaterialProperty({
                image:'/images/MS12.png',
                repeat: new Cesium.Cartesian2(1, 1.0),
                color: Cesium.Color.fromCssColorString('#3fffe4').withAlpha(0.6),
                transparent: true
            });
            // 計算圓
            function computeCircle(radius) {
                var positions = [];
                for (var i = 0; i < 360; i++) {
                    var radians = Cesium.Math.toRadians(i);
                    positions.push(
                        new Cesium.Cartesian2(
                            radius * Math.cos(radians),
                            radius * Math.sin(radians)
                        )
                    );
                }
                return positions;
            }
            // 計算棱柱  arms 棱柱角的數量  rOuter 外角突出距離 rInner  內角收縮距離
            function computeStar(arms, rOuter, rInner) {
                var angle = Math.PI / arms;
                var length = 2 * arms;
                var positions = new Array(length);
                for (var i = 0; i < length; i++) {
                    var r = i % 2 === 0 ? rOuter : rInner;
                    positions[i] = new Cesium.Cartesian2(
                        Math.cos(i * angle) * r,
                        Math.sin(i * angle) * r
                    );
                }
                return positions;
            }
            // 創建實體集合
            var dataSource = new Cesium.CustomDataSource('lines');
            viewer.dataSources.add(dataSource);
            for (let i=0; i< lineArr.length;i++) {
                dataSource.entities.add({
                    polylineVolume: {
                        positions: Cesium.Cartesian3.fromDegreesArrayHeights(lineArr[i]),
                        // shape: computeCircle(60000), // 圓柱子
                        // shape: computeStar(3, 26000, 50000), // 三角形柱子
                        // shape: computeStar(2, 50000, 50000), // 正方型柱子
                        shape: computeStar(5, 40000, 50000), // 五邊形柱子
                        // shape: computeStar(5, 70000, 40000), // 五角星柱子
                        material: stripeMaterial,
                        outlineColor: Cesium.Color.WHITE,
                        outlineWidth: 1
                    },
                });
            }
        },
        // 清除管道和攝像機綁定
        clearCylinder(earth){
            let dataSources = earth.czm.viewer.dataSources
            dataSources.removeAll()
            var path1 = this._earth.sceneTree.$refs.path1.czmObject;
            path1.playing = false
        },

  3.工具方法:

// 創建飛線 - 返回飛線點數據集合 -earthsdk方法
        getFlyLine(earth, data) {
            var busLines = [];
            // var p =  [
            //     [ [ 1.5990215, 0.5493211, 0 ], [ 2.0991215, 0.5497211, 50 ] ],
            //     /*[ [ 2.0992215, 0.5499211, 0 ], [ 2.0991215, 0.5497211, 50 ] ],*/
            // ];
            var p = data
            var positionsCollection = p.map(e => {
                const toDegree = 180.0 / Math.PI;
                // Cesium.xbsjCreateTransmitPolyline 根據 首末端點生成弧線,
                // 參數有:
                // startPosition, 端點1
                // endPosition, 端點2
                // minDistance, 計算出的線段的最小間隔距離   增大該值可減少點數量
                // heightRatio=1.0 弧線高度抬升程度,值越大,抬高得越明顯
                // 返回值是cartesian類型的坐標數組
                const cartesians = Cesium.xbsjCreateTransmitPolyline(e[0], e[1], 100000.0, 1.0);
                const poss = cartesians.map(ee => {
                    const carto = Cesium.Cartographic.fromCartesian(ee);
                    return [carto.longitude, carto.latitude, carto.height];
                });
                return poss;
            });
            return positionsCollection[0]
        },
        // 計算攝像機角度數組 -- 忽略俯仰角面映射  line:線路徑
        countRotations(line) {
            // 將點的單位統一為度
            let l = line.map(res => {
                return [this.RtoD(res[0]), this.RtoD(res[1]), this.MtoD(res[2])]
            })
            // 計算偏航角
            function countHornX(point1, point2) {
                var x0=point1[0];
                var y0=point1[1];
                var x1=point2[0];
                var y1=point2[1];
                let a,x,y
                // 防止相鄰兩個點跨越赤道或子午線
                if ((x1 > 0 && x0 > 0) || (x1<0 && x0 < 0)) {
                    x = x1-x0
                } else {
                    x = x0 - x1
                }
                if ((y1 > 0 && y0 > 0) || (y1<0 && y0 < 0)) {
                    y = y1-y0
                } else {
                    y = y0 - y1
                }
                a = Math.atan2(x,y);
                return a
            }
            // 計算俯仰角
            function countHornY(point1, point2) {
                var x0=point1[0];
                var y0=point1[1];
                var h0=point1[2];
                var x1=point2[0];
                var y1=point2[1];
                var h1=point2[2];
                let a
                a = Math.atan2((h1 - h0),(Math.sqrt(Math.pow((x1 - x0),2) + Math.pow((y1 - y0),2)))) * 10
                return a
            }
            // 計算rotations
            let rotations = []
            for (let i = 0; i < l.length - 1; i++) {
                let rx = countHornX(l[i], l[i * 1 + 1])
                let ry = countHornY(l[i], l[i * 1 + 1])
                rotations.push([rx, ry, 0])
            }
            rotations.push(rotations[rotations.length - 1])
            return rotations
        },
        // 獲取兩點間的距離
        getFlatternDistance(lat1,lng1,lat2,lng2){
            var EARTH_RADIUS = 6378137.0;    //單位M
            var PI = Math.PI;

            function getRad(d){
                return d*PI/180.0;
            }
            var f = getRad((lat1 + lat2)/2);
            var g = getRad((lat1 - lat2)/2);
            var l = getRad((lng1 - lng2)/2);

            var sg = Math.sin(g);
            var sl = Math.sin(l);
            var sf = Math.sin(f);

            var s,c,w,r,d,h1,h2;
            var a = EARTH_RADIUS;
            var fl = 1/298.257;

            sg = sg*sg;
            sl = sl*sl;
            sf = sf*sf;

            s = sg*(1-sl) + (1-sf)*sl;
            c = (1-sg)*(1-sl) + sf*sl;

            w = Math.atan(Math.sqrt(s/c));
            r = Math.sqrt(s*c)/w;
            d = 2*w*a;
            h1 = (3*r -1)/2/c;
            h2 = (3*r +1)/2/s;

            return d*(1 + fl*(h1*sf*(1-sg) - h2*(1-sf)*sg));
        },
        // 度轉弧度
        DtoR(val) {
            let r = val * Math.PI / 180
            return r
        },
        // 弧度轉度
        RtoD(val) {
            let r = 180 * val / Math.PI
            return r
        },
        // 米轉為度
        MtoD(val) {
            let r = val / 1112000
            return r
        },
        // 米轉為度
        DtoM(val) {
            let r = val * 1112000
            return r
        },
        // 弧度轉米
        RtoM (val) {
            return this.DtoM(this.RtoD(val))
        }

 

  

 

 

 

鑽研不易,轉載請注明出處。。。。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM