zoukankan      html  css  js  c++  java
  • 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))
            }

      

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

  • 相关阅读:
    Html-浅谈如何正确给table加边框
    如何在移动设备上调试html5开发的网页
    swiper嵌套小demo(移动端触摸滑动插件)
    移动端如何用swiper实现导航栏效果
    background-color:transparent
    点击按钮 发送短信验证码后60秒倒计时
    placeholder的样式设置
    linux 定时任务crontab
    laravel学习一
    centos 7安装jdk
  • 原文地址:https://www.cnblogs.com/s313139232/p/13432896.html
Copyright © 2011-2022 走看看