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)) }
钻研不易,转载请注明出处。。。。。。