由于工作原因,做了一些有关
cesium
的三维地图开发。基于我所用到的功能,于此做一个入门总结,并记录下曾踩下的坑。初学者可依照以下整个完整流程,复现功能内容。做此记录时,cesium
版本为 ^1.64.0
注:这里虽然利用了
npm
或 yarn
来快速安装 cesium
,但实际的demo只需直接引用文件,不需要打包测试用demo路径结构描述
cesium-demo ├─node_modules/ ├─index.html └─package.json
准备工作
开发环境
建议使用 VSCode 进行开发,并安装插件
Live Server
,用于启动此 index.html
文件安装cesium
# 在 cesium-demo 目录下输入如下命令 npm install --save cesium # 或者 yarn add cesium
index.html文件初始化
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Cesium-test</title> <script src="./node_modules/cesium/Build/Cesium/Cesium.js"></script> <style> @import url("./node_modules/cesium/Build/Cesium/Widgets/widgets.css"); html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } </style> </head> <body> <div id="cesiumContainer"></div> <script> const viewer = new Cesium.Viewer('cesiumContainer'); // 这里直接传入的也可以是获取到的cesiumContainer的dom元素对象 </script> </body> </html>
初始界面预览

常用初始化配置
以下内容不会给出完整代码,但会给出完整核心代码片段,读者对照初始化
index.html
自行增添或者更改即可运行,若出现误差,可尝试把 cesium
版本调整到 ^1.64.0
。以下 js
代码均直接在 script
标签全局命名空间中出现。隐藏界面小控件
通过初试配置,将界面上默认显示的控件隐藏,以便进行自定义
const viewer = new Cesium.Viewer('cesiumContainer', { animation: false, // 隐藏界面左下角控制动画的面板 baseLayerPicker: false, // 隐藏界面左上角地图底图的切换按钮 fullscreenButton: false, // 隐藏界面右下角全屏按钮 geocoder: false, // 隐藏界面右上角搜索按钮 homeButton: false, // 隐藏界面右上角初始化地球位置按钮 sceneModePicker: false, // 隐藏界面右上角视角切换按钮 timeline: false, // 隐藏正下方时间线 navigationHelpButton: false, // 隐藏右上角帮助按钮 });
仅仅是如此设置,底部还剩一段图片加文字信息,这里可以通过
css
手动隐藏。在 style
中添加如下代码。.cesium-viewer-bottom { display: none; }
如此,一个干净完整的地球便出现了

去掉光圈
如果有人觉得地球外层的光圈太扎眼,可以通过以下方式去除
const viewer = new Cesium.Viewer('cesiumContainer', { // ... skyAtmosphere: false, });
效果如下

聚焦框和信息框的隐藏
默认情况下,如果左键双击
cesium
地图上的实体内容,camera
视角会自动聚焦过去,并显示自带的聚焦框以及实体的信息框默认不显示聚焦框和信息框
const viewer = new Cesium.Viewer('cesiumContainer', { // ... selectionIndicator: false, // 隐藏聚焦框 infoBox: false, // 隐藏信息框 });
取消默认的左键双击实体效果,便于自定义其他交互
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
地形加载
初始化时默认加载地形
const viewer = new Cesium.Viewer('cesiumContainer', { // ... terrainProvider: Cesium.createWorldTerrain({ requestWaterMask: true, requestVertexNormals: true }) });
后期手动添加地形
viewer.terrainProvider = Cesium.createWorldTerrain({ requestWaterMask: true, requestVertexNormals: true });
坑:此方法加载地形时,加载的是
cesium
自己发布的地形服务。一旦 cesium
官方版本更新,则会导致地形加载失败(默认加载的底图也是如此)。浏览器控制台会报Token过期的错,但是实际上就算你更改了 Cesium.Ion.defaultAccessToken
的值仍然不能解决此问题。正确操作是,升级项目的 cesium
版本即可。最好的方法是,发布自己的地形以及底图服务,并加载。这样就不会受到 cesium
版本更新的影响。底图服务加载
imageryProvider
除了下面示例的 ArcGisMapServerImageryProvider
以外,还有分别用于加载其他不同框架底图服务的构造函数,具体内容,可在官方文档中搜索 imageryProvider
查询。
初始化默认加载其他底图
const viewer = new Cesium.Viewer('cesiumContainer', { // ... imageryProvider: new Cesium.ArcGisMapServerImageryProvider({ url: '<https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer>' }) });
后期手动添加底图
viewer.imageryLayers.addImageryProvider(new Cesium.ArcGisMapServerImageryProvider({ url: '<https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer>' }));
注:此坑与地形等同
视角转换
viewer.scene.camera.flyTo({ destination: new Cesium.Cartesian3.fromDegrees(106.49673, 29.61736, 1500), // 经纬度,高度 orientation: { heading: 0, pitch: -0.52458664812464143, roll: 2.0174972803488345e-11 }, duration: 2, complete: () => { // 飞行结束后执行逻辑 } });
camera
相关的所有操作在此处查看较为常用功能
以下内容,涉及到具体私人发布使用的服务部分,只给出最为核心的代码片段,如何实现。需要读者自行发布或寻找服务进行测试
geojson的加载使用
const jsonUrl = '...'; // 你的 geojson 文件路径,可以是本地相对路径 const mapJSON = Cesium.GeoJsonDataSource.load(jsonUrl, { // 对 geojson 文件的初始配置,可在 <https://cesium.com/docs/cesiumjs-ref-doc/GeoJsonDataSource.html> 查看详情 }); viewer.dataSources.add(mapJSON); // 将其加载到地图上去 mapJSON.then(entities => { // entities 是该 geojson 内部所有实体的数组,可对实体进行属性变更的操作 entities.forEach(entity => { entity.show = false; // 隐藏该实体 }); });
针对高精度倾斜摄影加载及显示效果优化
倾斜摄影数据以
3dtile
的形式加载出来,通常情况下,只需要有 url
就足够了。以下参数是由于倾斜摄影数据总数据太大,清晰度过高,从而调节参数进行性能优化。这不是一个通用的解决方案,但可以以此为参考。const tileSetPromise = new Cesium.Cesium3DTileset({ url: '...', // 你的倾斜摄影服务发布地址 skipLevelOfDetail: true, baseScreenSpaceError: 1024, maximumScreenSpaceError: 256, // 数值加大,能让最终成像变模糊 skipScreenSpaceErrorFactor: 16, skipLevels: 1, immediatelyLoadDesiredLevelOfDetail: false, loadSiblings: true, // 如果为true则不会在已加载完概况房屋后,自动从中心开始超清化房屋 cullWithChildrenBounds: true, cullRequestsWhileMoving: true, cullRequestsWhileMovingMultiplier: 10, // 值越小能够更快的剔除 preloadWhenHidden: true, preferLeaves: true, maximumMemoryUsage: 128, // 内存分配变小有利于倾斜摄影数据回收,提升性能体验 progressiveResolutionHeightFraction: 0.5, // 数值偏于0能够让初始加载变得模糊 dynamicScreenSpaceErrorDensity: 0.1, // 数值加大,能让周边加载变快 dynamicScreenSpaceErrorFactor: 1, // 不知道起了什么作用没,反正放着吧先 dynamicScreenSpaceError: true // 根据测试,有了这个后,会在真正的全屏加载完之后才清晰化房屋 }); tileSetPromise.readyPromise.then(tileSet => { this.scene.primitives.add(tileSet); // 将倾斜摄影实体加载到地图上 this.changeHeight(tileSet, 310); // 将此 tileSet 提高 310 米 }); // 可通过此函数,来修改 tileSet 的高度 function changeHeight(tileSet, height) { height = Number(height); if (isNaN(height)) { return; } const cartographic = Cesium.Cartographic.fromCartesian(tileSet.boundingSphere.center); const surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, cartographic.height); const offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, height); const translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3()); tileSet.modelMatrix = Cesium.Matrix4.fromTranslation(translation); }
限制camera高度的上下限
viewer.scene.screenSpaceCameraController.maximumZoomDistance = 65000; // 相机高度的最大值设定为 65000 米 viewer.scene.screenSpaceCameraController.minimumZoomDistance = 1000; // 相机高度的最小值设定为 1000 米
事件绑定
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction(movement => { // movement.position 是一个 Cartesian2 对象,可以通过它拿到所点击的屏幕坐标 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); // 监听左键点击
通过Cartesian2获得经纬度的方式
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction(movement => { const cartesian = this.scene.globe.pick(this.camera.getPickRay(movement.position), this.scene); // 获取空间坐标 const cartographic = Cesium.Cartographic.fromCartesian(cartesian); const lon = Cesium.Math.toDegrees(cartographic.longitude); // 经度 const lat = Cesium.Math.toDegrees(cartographic.latitude); // 纬度 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); // 监听左键点击
地图二三维转化
// 地图转化为三维立体状态 viewer.scene.morphTo3D(0); // 地图转化为二维平面状态 viewer.scene.morphTo2D(0);
- Author:游方
- URL:https://blog.ykxkykx.cn/article/d8b922e0
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!