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))
        }

 

  

 

 

 

钻研不易,转载请注明出处。。。。。。

 

posted @ 2020-08-04 14:05  莫小龙  阅读(1970)  评论(0编辑  收藏  举报