一、认识Cesium
概念
- Cesium 是一个跨平台、跨浏览器的展示三维地球和地图的 javascript 库;
- Cesium 使用WebGL 来进行硬件加速图形,使用时不需要任何插件支持,但是浏览器必须支持WebGL;
- Cesium是基于Apache2.0 许可的开源程序。它可以免费的用于商业和非商业用途。
作用
- 多种场景模式(3D,2.5D以及2D场景)的支持,真正的二三维一体化
- 可以绘制各种几何图形、高亮区域,支持导入图片,甚至3D模型等多种数据可视化展示;
- 可用于动态数据可视化并提供良好的触摸支持,支持绝大多数的浏览器和mobile;
- Cesium还支持基于时间轴的动态数据展示。
- 全球级别的高精度的地形和影像服务
- 地形影像、模型、倾斜、二三维场景与时态数据
二、环境建立
开发之前设置步骤:
-
访问Cesium Viewer确保您的系统与Cesium兼容
-
安装Node.js的。
-
克隆或下载zip并提取内容。
-
在控制台中,导航到根
cesium-workshop
目录。 -
运行
npm install
。 -
运行
npm start
,点击控制台上的http://localhost:8080
应用程序目录
-
Source/ : 我们项目的代码。
-
ThirdParty/ : 外部js库,目前只包含cesium。
-
LICENSE.md : 我们项目的说明条款。
-
index.html : 主页,包含项目程序代码和页面结构。
-
server.js : 简单的基于nodejs的http服务器。
CesiumJS完全兼容现代javascript 库和框架,下面是一些示例:
- Cesium and webpack 教程展示了使用webpack集成cesium去更高效的开发web项目。
- React集成
- CesiumJS和Threejs集成
- Vue集成
使用说明
点击 http://localhost:8080/ 出现的页面:
-
Documentation
里面是Cesium的完整的API说明,里面可以找到:
1、某一个模块的所有函数、属性
2、部分效果截图3、部分函数,属性调用代码示例
-
Sandcastle
一个沙盒,你可以在里面浏览当前版本的一些功能特性:
1、一个可运行的代码库
2、新建一个页面,进行代码测试
3、导出测试代码
快速搭建最简环境
-
创建文件夹 cesium-test
-
引入编译成果 ,将cesium源码中的Build文件夹,拷入cesium-test
-
创建html ,将cesium源码中Apps/ 中的HelloWorld.html 拷入cesium-test
-
修改js和css的文件路径
-
发布
三、界面以及小组件显示隐藏
viewer介绍
- Geocoder : 查找位置工具,查找到之后会将镜头对准找到的地址,默认使用bing地图
- Home Button :视角返回初始位置
- Scene Mode Picker : 选择视角的模式,有三种:3D,2D,哥伦布视图(CV)
- Base Layer Picker : 图层选择器,选择要显示的地图服务和地形服务
- Navigation Help Button :导航帮助按钮,显示默认的地图控制帮助
- Animation : 动画器件,控制视图动画的播放速度
- Timeline :时间线,指示当前时间,并允许用户跳到特定的时间
- Credits Display :版权显示,显示数据归属,必选
- Fullscreen Button :全屏按钮
隐藏界面中的元素
1、通过js代码控制
界面上默认的小控件可以通过在初始化Viewer的时候设置相应的属性来关闭
var viewer = new Cesium.Viewer('cesiumContainer',{
geocoder:false,
homeButton:false,
sceneModePicker:false,
baseLayerPicker:false,
navigationHelpButton:false,
animation:false,
creditContainer:"credit",
timeline:false,
fullscreenButton:false,
vrButton:false,
});
//界面默认的小控件的关闭方法,还有很多额外的属性,可以查看帮助文档
2、通过css控制
/* 不占据空间,无法点击 */
.cesium-viewer-toolbar, /* 右上角按钮组 */
.cesium-viewer-animationContainer, /* 左下角动画控件 */
.cesium-viewer-timelineContainer, /* 时间线 */
.cesium-viewer-bottom /* logo信息 */
{
display: none;
}
.cesium-viewer-fullscreenContainer /* 全屏按钮 */
{ position: absolute; top: -999em; }
/*注:全屏按钮不能通过display:none的方式来达到隐藏的目的,这是因为生成的按钮控件的行内样式设置了display属性,会覆盖引入的css属性*/
显示帧速(FPS)
viewer.scene.debugShowFramesPerSecond = true;
四、绘制形状
通过Entity添加形状
案例:添加立方体
var viewer = new Cesium.Viewer('cesiumContainer');
var redBox = viewer.entities.add({
name : 'Red box with black outline',
position: Cesium.Cartesian3.fromDegrees(-107.0, 40.0, 300000.0),
box : {
dimensions : new Cesium.Cartesian3(400000.0, 300000.0, 500000.0),
material : Cesium.Color.RED.withAlpha(0.5),
outline : true,
outlineColor : Cesium.Color.BLACK
}
});
viewer.zoomTo(viewer.entities);
通过CZML添加
- CZML也可以添加几何形状,而且CZML还可以描述动画
官方说明:CZML是一种JSON格式的字符串,用于描述与时间有关的动画场景,CZML包含点、线、地标、模型、和其他的一些图形元素,并指明了这些元素如何随时间而变化。某种程度上说, Cesium 和 CZML的关系就像 Google Earth 和 KML
var czml = [{
"id" : "document",
"name" : "box",
"version" : "1.0"
},{
"id" : "shape2",
"name" : "Red box with black outline",
"position" : {
"cartographicDegrees" : [-107.0, 40.0, 300000.0]
},
"box" : {
"dimensions" : {
"cartesian": [400000.0, 300000.0, 500000.0]
},
"material" : {
"solidColor" : {
"color" : {
"rgba" : [255, 0, 0, 128]
}
}
},
"outline" : true,
"outlineColor" : {
"rgba" : [0, 0, 0, 255]
}
}
}];
var viewer = new Cesium.Viewer('cesiumContainer');
var dataSourcePromise = Cesium.CzmlDataSource.load(czml);
viewer.dataSources.add(dataSourcePromise);
viewer.zoomTo(dataSourcePromise);
五、3D Tiles
3D Tiles 是什么
3D Tiles是一种开放式规范,用于跨桌面,Web和移动应用程序共享,可视化,融合以及与大量异构3D地理空间内容交互。
3D Tiles将用于流式传输3D内容,包括建筑物,树木,点云和矢量数据。
参考[官网 3dtiles 介绍 ][https://cesium.com/blog/2015/08/10/introducing-3d-tiles/]
3D Tiles 运用
//contextCapture 可以将无人机成果转换成Cesium支持的倾斜摄影成果 ,数据的加载比较简单
//但是问题在于生成的数据不一定是落在地面上,有可能是浮在空中的
var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url: url, //数据路径
maximumScreenSpaceError: 2, //最大的屏幕空间误差
maximumNumberOfLoadedTiles: 1000, //最大加载瓦片个数
modelMatrix: m //形状矩阵
}))
希望拍摄的成果能贴到地面上,和地图能很好的融合在一起
modelMatrix
Cesium3DTile里面的一个属性:可以更改位置
-
自己获取偏移量
目标位置 = 平移矩阵 * 原始位置 (参考《WebGl编程指南》的第三章第四章)
//Tx,Ty,Tz就是我们需要设置的 x,y,z方向上的平移距离 //由于Cesium的矩阵是列主序的,所以这里写成: //创建平移矩阵方法一 /*m = Cesium.Matrix4.fromArray([ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, x, y, z, 1.0 ])*/ //创建平移矩阵方法二 var translation=Cesium.Cartesian3.fromArray([x, y, z]); m= Cesium.Matrix4.fromTranslation(translation); //生效 tileset._modelMatrix = m; /*这里我们只需要不断的修改 x,y,z,就可以调整物体的位置了 获取 x,y,z 之后,在加载3D Tiles 时将modelMatrix 设置成目标 x,y,z值,就完成了*/
-
计算偏移量
[官方示例 ][https://cesiumjs.org/Cesium/Build/Apps/Sandcastle/index.html?src=3D Tiles Adjust Height.html]
//直接调用函数,调整高度,height表示物体离地面的高度
function changeHeight(height) {
height = Number(height);
if (isNaN(height)) {
return;
}
var cartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center);
var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, cartographic.height);
var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude,height);
var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
}
六、cesium中的坐标
概念
cesium中常用的坐标有两种WGS84地理坐标系和笛卡尔空间坐标系(世界坐标)。
-
WGS84坐标一般以经纬度来指明一个地点,原点在椭球的质心 。
-
笛卡尔空间坐标系常用来做一些空间位置变换如平移旋转缩放等等,坐标原点在椭球的中心 。
三维空间的右手笛卡尔坐标
偏航(yaw)/俯仰(pitch)/滚动(roll)
屏幕坐标----------------------------------------------->Pick
世界坐标----------------------------------------------->Cartesian
地理坐标(弧度) ---------------------------------->cartographic
经纬度坐标-------------------------------------------->Point
说明:以经纬度固定模型位置,要先将经纬度转换为世界坐标
1.坐标系
new Cesium.Cartesian2(1,1) //表示一个二维笛卡尔坐标系,也就是直角坐标系(屏幕坐标系)
new Cesium.Cartesian3(1,1,1) //表示一个三维笛卡尔坐标系,也是直角坐标系(真实世界的坐标系)
2. 二维屏幕坐标系到三维坐标系的转换
var pick1= scene.globe.pick(viewer.camera.getPickRay(pt1), scene)
//其中pt1为一个二维屏幕坐标。
3. 三维坐标到地理坐标的转换
var geoPt1= scene.globe.ellipsoid.cartesianToCartographic(pick1)
//其中pick1是一个Cesium.Cartesian3对象。
4. 地理坐标到经纬度坐标的转换
var point1=[geoPt1.longitude / Math.PI * 180,geoPt1.latitude / Math.PI * 180];
//其中geoPt1是一个地理坐标。
5. 经纬度坐标转地理坐标(弧度)
var cartographic = Cesium.Cartographic.fromDegree(point) //point是经纬度值
var coord_wgs84 = Cesium.Cartographic.fromDegrees(lng, lat, alt);//单位:度,度,米
6. 经纬度坐标转世界坐标
var cartesian = Cesium.Cartesian3.fromDegree(point)
7. 计算两个三维坐标系之间的距离
var d = Cesium.Cartesian3.distance(
new Cesium.Cartesian3(pick1.x, pick1.y, pick1.z),
new Cesium.Cartesian3(pick3.x, pick3.y, pick3.z)
);
//pick1、pick3都是三维坐标系
七、相机(camera)
概念
- 相机相当于我们在场景中的视野,模拟人的眼睛,用于观察视角。
Cesium默认支持使用鼠标和触摸事件控制相机 :
- 左键单击并拖动 - 移动整个地图
- 右键单击并拖动 - 放大和缩小相机。
- 中轮滚动 - 也可以放大和缩小相机。
- 中间点击并拖动 - 围绕地球表面的点旋转相机。
设置相机的位置
方法一、Cartesian
camera.setView({
destination : new Cesium.Cartesian3(x, y, z),
orientation: {
//heading/pitch/roll 的单位是弧度
heading : headingAngle, //方向向量以正东方向为轴的旋转角度
pitch : pitchAngle, //方向和水平平面的夹角
roll : rollAngle //方向向量指向水平平面上方,反之表示方向向量指向平面下方
}
});```
##### 方法二、Rectangle
> 设置为一个矩形区域
```js
viewer.camera.setView({
///西,南,东,北
destination : Cesium.Rectangle.fromDegrees(west, south, east, north),
orientation: {
heading : headingAngle,
pitch : pitchAngle,
roll : rollAngle
}
})
//参数为可选参数,如果不设置默认以当前的相机去计算对应的属性
方法三、flyTo
跳转镜头到指定位置
//lookAt(target, offect)
//target目标位置在世界坐标,offect以目标为中心的当地东北向参考系中的目标的偏移量。
view.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(116.435314,39.960521, 15000.0), // 设置位置
orientation: {
heading : Cesium.Math.toRadians(20.0), // 方向
pitch : Cesium.Math.toRadians(-90.0),// 倾斜角度
roll : 0
},
duration:5, // 设置飞行持续时间,默认会根据距离来计算
complete: function () {
// 到达位置后执行的回调函数
console.log('到达位置');
},
cancle: function () {
// 如果取消飞行则会调用此函数
console.log('取消飞行')
},
// 如果摄像机飞越高于该值,则调整俯仰俯仰的俯仰角度,并将地球保持在视口中。
pitchAdjustHeight: -90,
maximumHeight:5000, // 相机最大飞行高度
flyOverLongitude: 100, // 如果到达目的地有2种方式,设置具体值后会强制选择方向飞过这个经度
});
方法四、lookAt
lookAt会将视角固定在设置的点上
var center = Cesium.Cartesian3.fromDegrees(-72.0, 40.0);
var heading = Cesium.Math.toRadians(50.0);
var pitch = Cesium.Math.toRadians(-20.0);
var range = 5000.0;
view.camera.lookAt(center, new Cesium.HeadingPitchRange(heading, pitch, range));
直接限制相机高度范围
- 设置后当相机高度达到设置的最大和最小高度时将不再放大和缩小
//相机的高度的最小值
viewer.scene.screenSpaceCameraController.minimumZoomDistance = 250000;
//相机高度的最大值
viewer.scene.screenSpaceCameraController.maximumZoomDistance = 22000000;
//设置相机缩小时的速率
viewer.scene.screenSpaceCameraController._minimumZoomRate = 30000;
//设置相机放大时的速率
viewer.scene.screenSpaceCameraController._maximumZoomRate=5906376272000
官网Demo笔记
var viewer = new Cesium.Viewer('cesiumDemo',{
baseLayerPicker: false,
imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer'
})
});
var scene = viewer.scene;
var canvas = viewer.canvas; // 获取画布
canvas.setAttribute('tabindex', '0'); // 获取焦点
canvas.onclick = function() {
canvas.focus();
};
var ellipsoid = viewer.scene.globe.ellipsoid; // 获取地球球体对象
// 禁用默认的事件处理程序
// 如果为真,则允许用户旋转相机。如果为假,相机将锁定到当前标题。此标志仅适用于2D和3D。
scene.screenSpaceCameraController.enableRotate = false;
// 如果为true,则允许用户平移地图。如果为假,相机将保持锁定在当前位置。此标志仅适用于2D和Columbus视图模式。
scene.screenSpaceCameraController.enableTranslate = false;
// 如果为真,允许用户放大和缩小。如果为假,相机将锁定到距离椭圆体的当前距离
scene.screenSpaceCameraController.enableZoom = false;
// 如果为真,则允许用户倾斜相机。如果为假,相机将锁定到当前标题。这个标志只适用于3D和哥伦布视图。
scene.screenSpaceCameraController.enableTilt = false;
// 如果为true,则允许用户使用免费外观。如果错误,摄像机视图方向只能通过转换或旋转进行更改。此标志仅适用于3D和哥伦布视图模式。
scene.screenSpaceCameraController.enableLook = false;
// 鼠标开始位置
var startMousePosition;
// 鼠标位置
var mousePosition;
// 鼠标状态标志
var flags = {
looking : false,
moveForward : false, // 向前
moveBackward : false, // 向后
moveUp : false,// 向上
moveDown : false,// 向下
moveLeft : false,// 向左
moveRight : false// 向右
};
var handler = new Cesium.ScreenSpaceEventHandler(canvas);
// 接收用户鼠标(手势)事件
handler.setInputAction(function(movement) {
// 处理鼠标按下事件
// movement: 接收值为一个对象,含有鼠标单击的x,y坐标
flags.looking = true;
// 设置鼠标当前位置
mousePosition = startMousePosition = Cesium.Cartesian3.clone(movement.position);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
handler.setInputAction(function(movement) {
// 处理鼠标移动事件
// 更新鼠标位置
mousePosition = movement.endPosition;
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(function(position) {
// 处理鼠标左键弹起事件
flags.looking = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
// 根据键盘按键返回标志
function getFlagForKeyCode(keyCode) {
switch (keyCode) {
case 'W'.charCodeAt(0):
return 'moveForward';
case 'S'.charCodeAt(0):
return 'moveBackward';
case 'Q'.charCodeAt(0):
return 'moveUp';
case 'E'.charCodeAt(0):
return 'moveDown';
case 'D'.charCodeAt(0):
return 'moveRight';
case 'A'.charCodeAt(0):
return 'moveLeft';
default:
return undefined;
}
}
// 监听键盘按下事件
document.addEventListener('keydown', function(e) {
// 获取键盘返回的标志
var flagName = getFlagForKeyCode(e.keyCode);
if (typeof flagName !== 'undefined') {
flags[flagName] = true;
}
}, false);
// 监听键盘弹起时间
document.addEventListener('keyup', function(e) {
// 获取键盘返回的标志
var flagName = getFlagForKeyCode(e.keyCode);
if (typeof flagName !== 'undefined') {
flags[flagName] = false;
}
}, false);
// 对onTick事件进行监听
// onTick事件:根据当前配置选项,从当前时间提前计时。应该每个帧都调用tick,而不管动画是否发生。
// 简单的说就是每过一帧都会执行这个事件
viewer.clock.onTick.addEventListener(function(clock) {
// 获取实例的相机对象
var camera = viewer.camera;
if (flags.looking) {
// 获取画布的宽度
var width = canvas.clientWidth;
// 获取画布的高度
var height = canvas.clientHeight;
// Coordinate (0.0, 0.0) will be where the mouse was clicked.
var x = (mousePosition.x - startMousePosition.x) / width;
var y = -(mousePosition.y - startMousePosition.y) / height;
var lookFactor = 0.05;
camera.lookRight(x * lookFactor);
camera.lookUp(y * lookFactor);
}
// 获取相机高度
// cartesianToCartographic(): 将笛卡尔坐标转化为地图坐标,方法返回Cartographic对象,包含经度、纬度、高度
var cameraHeight = ellipsoid.cartesianToCartographic(camera.position).height;
var moveRate = cameraHeight / 100.0;
// 如果按下键盘就移动
if (flags.moveForward) {
camera.moveForward(moveRate);
}
if (flags.moveBackward) {
camera.moveBackward(moveRate);
}
if (flags.moveUp) {
camera.moveUp(moveRate);
}
if (flags.moveDown) {
camera.moveDown(moveRate);
}
if (flags.moveLeft) {
camera.moveLeft(moveRate);
}
if (flags.moveRight) {
camera.moveRight(moveRate);
}
});
八、平面裁切
3D Tiles裁切
简单案例:创建一个法线向下的裁切平面并且经过原点,上面黄色区域的就被截掉了,如图所示:
let clippingPlanes = new Cesium.ClippingPlaneCollection({
planes: [
new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -0.1), 0.0)
],
unionClippingRegions: true,
edgeColor: Cesium.Color.RED,
edgeWidth: 1
})
// 设置模型的裁切平面clippingPlanes
this.tileset = this.viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url: '../../static/Cesium/Assets/Models/' + this.url,
dynamicScreenSpaceError: true,
cullWithChildrenBounds: false,
skipLevels: 0,
maximumScreenSpaceError: 0,
clippingPlanes: clippingPlanes
}))
ClippingPlaneCollection
相关概念
-
指定一组剪裁平面 。
-
通常,剪裁平面的坐标是相对于它们所附着的对象,因此距离设置为0的平面将剪切穿过对象的中心。
-
平面的法向,如果法线向下,以平面未界限,相反方向都是外部,如图:
- 平面法向所在坐标系是以正东方为x轴正方向,正北方为y轴正方向,正上面为z轴正方向的坐标系
参数
-
planes:用于在每个平面的外部选择性地禁用渲染的对象数组
- ClippingPlane:设置平面的法线方向和从原点到平面的最短距离,可以同时创建多个方向切片
-
edgeColor:应用的颜色用于突出显示剪切对象的边缘,就是测试阶段能让你看到剪切的位置
-
edgeWidth:应用于剪裁对象的边缘的高光的宽度(以像素为单位)
-
unionClippingRegions:如果为true,则区域将被剪裁(如果它位于集合中任何平面的外部)。否则,只有区域位于每个平面的外侧,才会剪切区域,就是所谓切片的交集。
如图所示:黄色区域就是交集,原谅我的图画的好丑啊QAQ…
[ 官方案例][https://cesiumjs.org/Cesium/Build/Apps/Sandcastle/?src=3D%20Tiles%20Clipping%20Planes.html]