Cesium 上手不齐全指北
将最近学习的CesiumJS
做一个零碎梳理,从我的项目配置开始,记录罕用API
的应用。
环境搭建与装置
首先,什么是 Cesium
,Cesium
是一款开源的基于 JavaScript
的 3D
地图框架,即地图可视化框架。产品基于 WebGL
技术,能够应用 CesiumJS
创立虚构场景的 3D
地理信息平台。其指标是用于创立以基于 Web
的地图动态数据可视化。在晋升平台的性能、准确率、虚拟化能力、易用性方面提供各种反对。
更多介绍和信息可通过官网进行学习。
注册
Cesium ion
是一个提供瓦片图和 3D
天文空间数据的平台,Cesium ion
反对把数据增加到用户本人的 CesiumJS
利用中。应用二三维贴图和世界地形都须要 ion
的反对,如果没有本人的数据源须要 cesium
提供的数据源就须要申请 ion
的 token
,具体能够通过以下链接申请 access token。
在创立 Cesium Viewer
的时候,将 access token
填为本人的 access token
即可。
Cesium.Ion.defaultAccessToken = '<YOUR ACCESS TOKEN HERE>';
我的项目搭建
进入我的项目搭建过程,项目选择在 Vue
平台上进行实现,首先创立我的项目装置 cesium
库:
vue create cesium-vuecd cesium-vuenpm i cesium@1.61 --save
留神:目前应用 webpack
进行配置援用最新版本(1.71) cesium
临时不能导入,实测 cesium@1.61
版本能够进行 import
导入。
我的项目配置
根目录下新建 vue.config.js
配置文件,对我的项目进行根本配置:
const CopyWebpackPlugin = require('copy-webpack-plugin')const webpack = require('webpack')const path = require('path')const debug = process.env.NODE_ENV !== 'production'let cesiumSource = './node_modules/cesium/Source'let cesiumWorkers = '../Build/Cesium/Workers'module.exports = { publicPath: '', devServer: { port: 9999 }, configureWebpack: { output: { sourcePrefix: ' ' }, amd: { toUrlUndefined: true }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js', '@': path.resolve('src'), 'cesium': path.resolve(__dirname, cesiumSource) } }, plugins: [ new CopyWebpackPlugin([{ from: path.join(cesiumSource, cesiumWorkers), to: 'Workers'}]), new CopyWebpackPlugin([{ from: path.join(cesiumSource, 'Assets'), to: 'Assets'}]), new CopyWebpackPlugin([{ from: path.join(cesiumSource, 'Widgets'), to: 'Widgets'}]), new CopyWebpackPlugin([{ from: path.join(cesiumSource, 'ThirdParty/Workers'), to: 'ThirdParty/Workers'}]), new webpack.DefinePlugin({ CESIUM_BASE_URL: JSON.stringify('./') }), new CopyWebpackPlugin([{ from: path.join('./static', 'model'), to: 'model3D' }]), new CopyWebpackPlugin([{ from: path.join('./static', 'images'), to: 'images' }]) ] }}
在根目录下创立 static
文件夹用于后续 model
和 images
的寄存。
组件实现
在 src/components/
下新建 CesiumViewer.vue
进行组件实现:
<template> <div id="cesiumContainer"></div></template><script>import Cesium from 'cesium/Cesium'import 'cesium/Widgets/widgets.css'export default { name: 'CesiumViewer', mounted () { // token Cesium.Ion.defaultAccessToken = 'your token'; let viewer = new Cesium.Viewer('cesiumContainer'); }, methods: {}}</script><style>#cesiumContainer { display: block; position: absolute; top: 0; left: 0; border: none; width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;}</style>
能够看到地图在调用 Cesium
的 Viewer
时开始构建。Viewer
是 Cesium
API
的终点,new Viewer
后便能够看见地球对象。
组件申明
在 App.vue
中援用组件:
<template> <div id="app"> <CesiumViewer></CesiumViewer> </div></template><script>import CesiumViewer from './components/CesiumViewer'export default { name: 'App', components: { CesiumViewer }}</script>
运行查看成果:
npm run serve
此时,曾经能够看见最开始的地球????成果,咱们进行一些简略配置和调整:
<script>import Cesium from 'cesium/Cesium'import 'cesium/Widgets/widgets.css'export default { name: 'CesiumViewer', mounted () { this.init(); }, methods: { init () { let viewerOption = { geocoder: false, // 地理位置搜寻控件 homeButton: false, // 首页跳转控件 sceneModePicker: false, // 2D,3D和Columbus View切换控件 baseLayerPicker: false, // 三维地图底图切换控件 navigationHelpButton: false, // 帮忙提醒控件 animation: false, // 视图动画播放速度控件 timeline: false, // 时间轴控件 fullscreenButton: false, // 全屏控件 infoBox: false, // 点击显示窗口控件 selectionIndicator: false, // 实体对象抉择框控件 scene3DOnly: true // 仅3D渲染,节俭GPU内存 } // token Cesium.Ion.defaultAccessToken = 'your token'; let viewer = new Cesium.Viewer('cesiumContainer', viewerOption); // 暗藏Logo viewer.cesiumWidget.creditContainer.style.display = "none"; } }}</script>
npm run serve
最终成果如下:
至此,最开始的构建运行就曾经实现了,上面对具体 API
进行学习。
Imagery
图层
开始 API
学习之前,为了不便办法实现,应用 ref
在元素上注册一个援用信息,不便通过 ID
间接拜访一个子组件实例。
批改如下,援用信息将会注册在父组件的 $refs
对象上,子组件通过 this.$viewer
进行拜访。
这里引入图层的概念(Imagery
),瓦片图汇合依据不同的投影形式映射到虚构的三维数字地球表面。依赖于相机指向地表的方向和间隔,Cesium
会去申请和渲染不同层级的图层详细信息。
具体代码如下:
<template> <div id="cesiumContainer" ref="viewer"></div></template><script>import Cesium from 'cesium/Cesium'import 'cesium/Widgets/widgets.css'export default { name: 'CesiumViewer', mounted () { // 初始化 this.init(); // 增加图层 this.addLayers(); }, methods: { // 初始化 init () { let viewerOption = {...} // token Cesium.Ion.defaultAccessToken = 'your token'; this.$viewer = new Cesium.Viewer(this.$refs.viewer, viewerOption); this.$viewer.cesiumWidget.creditContainer.style.display = "none"; }, // 增加 `Imagery` (图层) addLayers () { // Remove default base layer // this.$viewer.imageryLayers.remove(this.$viewer.imageryLayers.get(0)); // Add grid imagery this.$viewer.imageryLayers.addImageryProvider(new Cesium.GridImageryProvider()); } }}</script>
原理上和 PS
的图层统一,多个图层能够增加、移除和排序,渲染并适应到 Cesium
中。
Terrain
地形
Cesium
的地形图层反对渐进式加载和渲染寰球高精度地图,并且包含地形地势、水形数据,包含陆地、湖泊、河流、山峰、峡谷等成果。
为了增加地形数据,咱们须要创立一个 CesiumTerrainProvider
,通过 createWorldTerrain
函数创立一个由 Cesium ion
提供服务的 Cesium WorldTerrian
,同时可提供配置项,申请额定的水和光数据,最终咱们通过 camera
下的函数定位到创立的地位进行查看:
export default { name: "CesiumViewer", mounted() { // 初始化 this.init(); // 增加图层 // this.addLayers(); // 增加地形 this.addTerrain(); }, methods: { // 初始化 init() {...}, // 增加图层 addLayers() {...}, // 增加地形 addTerrain() { this.$viewer.terrainProvider = Cesium.createWorldTerrain({ requestWaterMask: true, // required for water effects requestVertexNormals: true, // required for terrain lighting }); // Enable depth testing so things behind the terrain disappear. this.$viewer.scene.globe.depthTestAgainstTerrain = true; this.$viewer.scene.camera.flyTo({ destination: Cesium.Cartesian3.fromRadians( -2.6399828792482234, 1.0993550795541742, 5795 ), orientation: { heading: 3.8455, pitch: -0.4535, roll: 0.0, }, }); }, }}
Viewer
控件
回到最开始的调整配置上,咱们在 viewerOption
中对 Viewer
申明的一系列根本小控件做了移除和优化操作,具体 API
官网给出了如下形容:
- Geocoder : A location search tool that flies the camera to queried location. Uses Bing Maps data by default.
- HomeButton : Flies the viewer back to a default view.
- SceneModePicker : Switches between 3D, 2D and Columbus View (CV) modes.
- BaseLayerPicker : Chooses the imagery and terrain to display on the globe.
- NavigationHelpButton : Displays the default camera controls.
- Animation : Controls the play speed for view animation.
- CreditsDisplay : Displays data attributions. Almost always required!
- Timeline : Indicates current time and allows users to jump to a specific time using the scrubber.
- FullscreenButton : Makes the Viewer fullscreen.
咱们能够依据本身需要抉择是否启用。
官网形容地址
同时咱们还能够对视窗进行配置,达到本人冀望的成果,如开启依据动静工夫激活太阳地位的光照,对实在地球进行模仿:
export default { name: "CesiumViewer", mounted() { // 初始化 this.init(); // 增加图层 // this.addLayers(); // 增加地形 // this.addTerrain(); // 配置视窗 this.configScene(); }, methods: { // 初始化 init() {...}, // 配置视窗 configScene() { // Enable lighting based on sun/moon positions(激活基于太阳地位的光照) this.$viewer.scene.globe.enableLighting = true; }, }}
更近一步,能够利用上一大节应用的 camera
实现主视窗的定位:
// 配置视窗configScene() { // Enable lighting based on sun/moon positions(激活基于太阳地位的光照) this.$viewer.scene.globe.enableLighting = true; // Create an initial camera view let initialPosition = new Cesium.Cartesian3.fromDegrees( -73.998114468289017509, 40.674512895646692812, 2631.082799425431 ); let initialOrientation = new Cesium.HeadingPitchRoll.fromDegrees( 7.1077496389876024807, -31.987223091598949054, 0.025883251314954971306 ); let homeCameraView = { destination: initialPosition, orientation: { heading: initialOrientation.heading, pitch: initialOrientation.pitch, roll: initialOrientation.roll, }, }; // Set the initial view this.$viewer.scene.camera.setView(homeCameraView);},
这里须要介绍的是 Scene
的概念,Scence
虚构场景是所有3D
图形对象和状态的容器,通常不是由开发者间接创立,而是在 Viewer
或者 CesiumWidget
外部隐式创立的。
通过 Scene
场景,咱们能够管制 Globe
地球(包含地形和图层)、Camera
相机、Primitives
(默认矢量数据层)和 PostProcessStage
(前期解决成果)等。
除了以上配置,当初咱们须要理解的有以下 Cesium
根本类型的 API
:
- Cartesian3 : 一个三维笛卡尔坐标——当它被用作绝对于地球核心的地位时,应用地球固定框架(
ECEF
)。 - Cartographic : 由经度、纬度(弧度)和
WGS84
椭球面高度确定的地位。 - HeadingPitchRoll : 在西南向上的框架中对于部分轴的旋转(弧度)。航向是围绕负
Z
轴的旋转。俯仰是围绕负Y
轴的旋转。滚动是对于正X
轴的旋转。 - Quaternion :以
4D
坐标示意的3D
旋转。
Camera
相机
Camera
是 Cesium
中罕用的 API
,属于 viewer.scene
中的属性,用来管制以后可见的域。能够管制场景的察看视角,例如旋转、缩放、平移以及航行定位。
一些最罕用的办法如下:
- Camera.setView(options): 在特定地位和方向立刻设置相机。
- Camera.zoomIn(amount): 沿着视角矢量挪动摄像机。
- Camera.zoomOut(amount): 沿视角矢量向后挪动摄像机。
- Camera.flyTo(options): 创立从以后相机地位到新地位的动画相机航行。
- Camera.lookAt(target, offset): 定位并定位摄像机以给定偏移量瞄准指标点。
- Camera.move(direction, amount): 朝任何方向挪动摄像机。
- Camera.rotate(axis, angle): 绕任意轴旋转相机。
例子参考上一大节的视窗定位。
Clock
时钟
应用 viewer
的 Clock
和 Timline
能够管制 scene
的工夫进度。
上面通过批改 init
函数实现一个日夜交替成果:
export default { name: "CesiumViewer", mounted() { // 初始化 this.init(); }, methods: { // 初始化 init() { let clock = new Cesium.Clock({ startTime: Cesium.JulianDate.now(), currentTime: Cesium.JulianDate.now(), stopTime: Cesium.JulianDate.addDays(Cesium.JulianDate.now(), 1, new Cesium.JulianDate()), clockRange: Cesium.ClockRange.LOOP_STOP, clockStep: Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER, multiplier: 9000, shouldAnimate: true }); let viewerOption = { geocoder: false, homeButton: false, sceneModePicker: false, baseLayerPicker: false, navigationHelpButton: false, animation: false, timeline: false, fullscreenButton: false, infoBox: false, selectionIndicator: false, scene3DOnly: true, shadows: true, shouldAnimate: true, clockViewModel: new Cesium.ClockViewModel(clock) }; // your token Cesium.Ion.defaultAccessToken = "token"; this.$viewer = new Cesium.Viewer(this.$refs.viewer, viewerOption); // 暗藏Logo this.$viewer.cesiumWidget.creditContainer.style.display = "none"; this.$viewer.scene.globe.enableLighting = true; }, }}
通过定义 clock
,设置起始工夫、速率和循环等配置,应用 clockViewModel
在实例中增加时钟视图模型,而后启用光照,实现成果。
注:此成果为演示,init
函数后续复原为开始的创立实例状态,不便之后的例子应用。
Entity
实体
Cesium
中的所有空间数据都应用 Entity API
来示意。Entity API
以一种无效提供灵便的可视化的形式,以便对 Cesium
进行渲染。Cesium Entity
是能够与款式化图形示意配对并定位在空间和工夫上的数据对象。
在 Cesium
中,加载点线面矢量有两种形式:
- Entity API 是数据驱动的一组高级对象,具备接口统一,容易应用的特点,但性能略低。
- Primitive API 是面向三维图形开发,更为底层,具备灵便,性能高的特点,但应用简单。
其中,Entity API
的应用通过 viewer.entities.add()
办法增加矢量数据:
export default { name: "CesiumViewer", mounted() { // 初始化 this.init(); // 加载实体 this.loadEntities(); }, methods: { // 初始化 init() {...}, loadEntities() { let polygon = this.$viewer.entities.add({ name: "正方形", id: "square", polygon: { hierarchy: Cesium.Cartesian3.fromDegreesArray([ -109.080842, 45.002073, -105.91517, 45.002073, -104.058488, 44.996596, -104.053011, 43.002989, -104.053011, 41.003906, -105.728954, 40.998429, -107.919731, 41.003906, -109.04798, 40.998429, -111.047063, 40.998429, -111.047063, 42.000709, -111.047063, 44.476286, -111.05254, 45.002073, ]), height: 0, material: Cesium.Color.RED.withAlpha(0.5), outline: true, outlineColor: Cesium.Color.BLACK, }, }); this.$viewer.zoomTo(polygon); // polygon.show = false; } }}
成果如下:
除了绘制实体,还能够通过内部加载的形式进行模型导入。
这里咱们在 static
文件夹下放入 J15.glb
文件进行导入:
export default { name: "CesiumViewer", mounted() { // 初始化 this.init(); // 增加模型 this.addEntities(); }, methods: { // 初始化 init() {...}, addEntities() { let fighter = this.$viewer.entities.add({ name: "fighter", id: "J15", model: { uri: "model3D/J15.glb", minimumPixelSize: 100, maximumScale: 1000, }, position: Cesium.Cartesian3.fromDegrees(-110.345, 30, 70000), }); // this.$viewer.trackedEntity = fighter; this.$viewer.zoomTo(fighter, new Cesium.HeadingPitchRange(-1, -0.3, 35)); } }}