资讯详情

资讯详情

OpenLayers、Mapbox GL JS、CesiumJS 飞行漫游方案对比与实现

做课程作业、毕业设计或者参加 WebGIS 比赛的时候经常绕不开一个问题需要在网页里展示一段“飞行视角”或者“自动漫游”的效果。有的项目要求从 A 点飞到 B 点有的要求沿着一条线路把城市模型看一遍还有的只要求在 2D 地图上平滑移动。这个时候就会出现三款库同时被推荐的场面OpenLayers、Mapbox、Cesium。很多人第一反应是把三个库都学一遍其实没必要。先说结论OpenLayers 适合 2D 地图数据展示Mapbox GL JS 适合带倾角的地图飞行CesiumJS 适合真三维场景和模型漫游。三者的“飞行视角”实现思路完全不同本文会用一套通用示例分别跑通三套方案并给出代码、部署步骤、常见坑和性能优化建议。本文不是入门概念科普而是直接告诉你每个库能做什么、不擅长什么、写出来的代码怎么用。你会看到OpenLayers 如何用view.animate做 2D 视角移动和路线巡航Mapbox GL JS 如何用flyTo做带倾角和偏航角的飞行切入CesiumJS 如何用相机flyTo、实体轨迹跟踪实现三维漫游三个库在课程设计、毕设和比赛中的选型建议路由点位、GeoJSON、底图服务、接口封装和批量任务怎么做常见的空白、偏移、卡顿、动画丢失问题如何排查。这里没有固定的显存门槛因为三个都是前端渲染库跑在浏览器里主要吃 CPU、GPU 和内存。你的开发机只要能正常开一个 3D 网页基本都能跑。下面直接进入正文。1. 核心能力速览1.1 三件套能力对比能力维度OpenLayersMapbox GL JSCesiumJS项目定位经典 WebGIS 2D 地图库矢量切片与 3D 倾斜地图三维数字地球与大规模场景飞行视角方式平滑移动 center / zoom非真实透视flyTo/easeTo带 pitch、bearingcamera.flyTo/camera.setView/ 实体跟踪默认 3D 地形不支持支持栅格地形 DEM 叠加支持全球地形与 3D Tiles自带底图不内置需自己接 OSM、天地图等需要 access token用 Mapbox 样式服务默认 Cesium ion 全球影像需要 token主要适用场景图层管理、GeoJSON 展示、坐标量算数据可视化 带倾斜的“飞行切入”动效城市级三维场景、建筑模型、模拟飞行课程设计友好度高无账号也能跑中需 token但示例很多中无 token 需要自己准备底图扩展能力WMS/WMTS/MVT/GeoJSON自定义样式、地形、建筑模型glTF、3D Tiles、粒子、雷达波等从表格可以看出三件套并不是竞争关系而是各自覆盖 WebGIS 开发的不同层次。做“飞行视角-漫游”的时候应该先想清楚目标是 2D 还是 3D再决定用哪套。1.2 飞行视角漫游方案速选判断逻辑很简单如果只是地图上两个点之间平滑移动看不到倾斜和透视效果选择 OpenLayers。如果要看到“飞机飞过去”的感觉视角带俯仰角、方向偏转和距离拉近选择 Mapbox GL JS。如果场景里有建筑模型、3D Tiles、地形夸张、或者要沿着路线进行第一人称 / 第三人称浏览选择 CesiumJS。本文的三套示例都采用“多航点连续飞行”这个思路事先定义一个巡航点数组每个点包含经纬度、高度、朝向、俯仰角然后让地图库逐个飞过去。这样既是一个完整的课程演示也能直接扩展成比赛作品中的“自动巡检”功能。2. 适用场景与使用边界2.1 课程设计、毕设、比赛分别怎么选课程作业更适合 OpenLayers原因是它不需要 account token不依赖第三方底图服务全本地代码就能把地图跑起来。做图层切换、点线面标注、查询定位、简单路径移动时OpenLayers 是最好交差的方案。毕设如果做地理信息管理系统比如校园地图、交通监控、城市设施管理推荐 Mapbox GL JS。Mapbox 的矢量底图视觉效果好pitch加bearing的组合能在一屏内展示出类似三维的透视感代码量又比 Cesium 少很多。用于演示“飞行到某个小区”“飞行到某条路线起点”非常合适。比赛作品如果是智慧城市、数字孪生、仿真模拟重点往往在三维效果上建议直接使用 CesiumJS。Cesium 可以加载倾斜摄影模型、3D Tiles、建筑白模、地形高程飞行漫游的镜头可控性也最强。缺点是前期学习曲线比前两者陡需要在数据准备和场景构建上多花时间。2.2 使用前的合法边界与数据合规WebGIS 项目里最容易翻车的地方不是代码而是数据和底图使用边界公开底图服务都有自己的服务条款个人学习、课程演示可以用商用和公开发布前要看清楚授权范围。从其他网站抓取瓦片、复制加密图层、下载离线地图包再分发都可能涉及数据版权风险不建议在毕设或比赛作品里这么做。使用带坐标偏移的服务时不要混用不同坐标系的数据否则会出现“底图正常业务点全部偏移”的经典问题。项目里如果使用真实建筑物、道路、影像数据建议只使用公开合规渠道提供的数据并在作品文档中注明数据来源与使用许可。发布到公网的应用要注意 token 泄露问题。Mapbox 和 Cesium ion 的 access token 应该做域名白名单限制不能让密钥被随意复制使用。这些内容不会直接决定你的功能能不能实现但会影响你的作品能不能顺利评审、能不能公开演示。3. 环境准备与基础搭建3.1 开发环境要求三个库本质上都是前端 JavaScript 库因此环境要求非常统一环境项建议配置说明操作系统Windows 10/11、macOS、Linux 均可无特殊限制浏览器Chrome / Edge 最新版需要 WebGL 支持开发工具VS Code Live Server 或 Vite纯 HTML 也可以Node.js建议 LTS 版本使用 npm 安装依赖时需要显卡驱动更新到最新版3D 场景渲染异常时先查驱动网络能访问底图服务底图加载快慢影响体验不要求很高的 GPU 算力这三个库的官方示例用集成显卡也能跑。Cesium 加载大型模型时对显存有要求但这个消耗会根据模型大小动态变化。本文不承诺具体显存数字你自己运行时可以在任务管理器里观察 GPU 的 Dedicated GPU memory 占用。3.2 工程创建方式最简单的验证方式是先用 Vite Vanilla JS 工程跑通之后再往项目里整合。命令如下npm create vitelatest webgis-flight -- --template vanilla cd webgis-flight npm install npm install ol mapbox-gl cesium安装完成后的目录结构如下webgis-flight/ ├── index.html ├── package.json ├── src/ │ ├── main.js │ ├── style.css │ └── ...如果你不想用构建工具也可以直接在 HTML 文件里引用三个库的 CDN。但 CDN 方式有两个问题一是版本变化快代码复制之后可能因为版本不同而报错二是离线和内网电脑无法运行。因此本文示例以 npm 安装、构建工具运行为主。3.3 token 准备说明OpenLayers 不需要 token。Mapbox 需要先到官网注册账号并创建一个 access token示例代码中需要把mapboxgl.accessToken替换成自己的 token。CesiumJS 也需要 Cesium ion 账号获取 token 后再设置Cesium.Ion.defaultAccessToken。作为替代方案Cesium 可以不依赖 ion 底图直接使用 OSM 瓦片或者本地准备好的影像服务。代码中会给出两种写法的差异。3.4 巡航点数据结构设计为了让后面的代码通用把巡航点先设计成 JSON 格式。这样课程作业、毕设或者比赛中只需要修改 JSON 点位不需要频繁改代码[ { lon: 120.15, lat: 30.28, height: 800, heading: 0, pitch: -30 }, { lon: 120.09, lat: 30.24, height: 1200, heading: 45, pitch: -25 }, { lon: 120.07, lat: 30.20, height: 500, heading: 120, pitch: -40 } ]字段说明lon/lat巡航目标点的经纬度height镜头高度单位为米heading镜头朝向0 度表示正北方向pitch俯仰角负数表示向下看。这段数据显示的是示例轨迹实际项目中替换成自己业务的线路即可。不同库对经纬度和投影坐标的处理不一样真正动手开发时要注意坐标转换。4. 使用 OpenLayers 实现 2D 视角漫游4.1 初始化 OpenLayers 地图OpenLayers 默认使用 Web Mercator 投影坐标因此传入经纬度前要先用fromLonLat转换。创建一个基础地图的代码如下import ol/ol.css; import Map from ol/Map.js; import View from ol/View.js; import TileLayer from ol/layer/Tile.js; import OSM from ol/source/OSM.js; import { fromLonLat } from ol/proj.js; const map new Map({ target: map, layers: [ new TileLayer({ source: new OSM() }) ], view: new View({ center: fromLonLat([120.15, 30.28]), zoom: 10 }) });这时页面会出现一个 2D 地图。代码里使用 OSM 底图是为了减少环境依赖正式项目中可以换成天地图、GeoServer 发布的 WMS 服务或自己的瓦片库。4.2 使用 view.animate 实现两个点之间的视角切换OpenLayers 的“飞行视角”本质是View对象的中心点和缩放级别动画。下面这段代码演示点击地图后平滑移动到下一个点位const waypoints [ [120.15, 30.28], [120.09, 30.24] ]; let currentIndex 0; function flyToNext() { if (currentIndex waypoints.length) { currentIndex 0; return; } const next waypoints[currentIndex]; const view map.getView(); view.animate({ center: fromLonLat(next), zoom: 14, duration: 2500, easing: function (t) { return t; } }); currentIndex; } map.on(singleclick, flyToNext);执行后每次点击地图都会触发一次中心点移动同时缩放级别提升到 14。看起来像“镜头飞过去”但因为没有透视变化只能算 2D 平移动画。4.3 OpenLayers 多段路线自动巡航如果要把多个点串成一条航线可以结合setTimeout循环执行。注意动画执行过程中再次调用animateOpenLayers 会把它加入动画队列效果是连续切换目的地const route [ [120.15, 30.28], [120.12, 30.25], [120.09, 30.24], [120.07, 30.20] ]; let routeIndex 0; let cruising false; function playCruise() { if (!cruising) return; if (routeIndex route.length) { routeIndex 0; setTimeout(playCruise, 1000); return; } const next route[routeIndex]; map.getView().animate({ center: fromLonLat(next), zoom: 14, duration: 3000 }); routeIndex; setTimeout(playCruise, 3200); } function startCruise() { cruising true; playCruise(); } function stopCruise() { cruising false; map.getView().cancelAnimations(); } document.getElementById(startBtn).addEventListener(click, startCruise); document.getElementById(stopBtn).addEventListener(click, stopCruise);这里使用cancelAnimations()强制清空动画队列适合停止巡航时使用。按钮的点击事件需要页面存在对应startBtn和stopBtn元素如果没有按钮可以直接换成其他业务逻辑。4.4 OpenLayers 飞行漫游的边界OpenLayers 的“飞行”效果是二维坐标变化视觉上更像“镜头平滑滚动”没有俯仰角、没有倾斜、也没有地形高度。如果课程设计要求只要能看到点位从一个地方移动到另一个地方那么 OpenLayers 完全够用。但如果你想做“无人机视角航拍”“沿道路贴地飞行”“模拟驾驶”这类效果应该选择 Mapbox 或 Cesium。另外OpenLayers 的view.animate在连续切换多个点时要注意setTimeout时间和动画时长之间的协调。如果定时器时间过短后续动画可能打断前一段动画如果过长动画之间会有明显停顿。实际开发中可以根据航线距离动态计算duration。5. 使用 Mapbox GL JS 实现带倾角的飞行漫游5.1 初始化带 3D 地形的地图Mapbox GL JS 的飞行效果接近“摄像机航拍”。初始化时可以先启用pitch和bearing再叠加一个 DEM 地形源。示例代码如下import mapboxgl from mapbox-gl; import mapbox-gl/dist/mapbox-gl.css; mapboxgl.accessToken 替换成自己的token; const map new mapboxgl.Map({ container: map, style: mapbox://styles/mapbox/satellite-streets-v12, center: [120.15, 30.28], zoom: 10, pitch: 60, bearing: -30 }); map.on(load, () { map.addSource(mapbox-dem, { type: raster-dem, url: mapbox://mapbox.terrain-rgb }); map.setTerrain({ source: mapbox-dem, exaggeration: 1.5 }); });如果使用的是streets-v12系列样式地图默认自带一部分三维建筑叠加地形后画面会更有“飞行”的感觉。若你的版本没有暴露 Terrain API需要检查mapbox-gl版本和样式配置。5.2 用 flyTo 实现单点飞行map.flyTo是 Mapbox 最常用的飞行方法它可以同时控制视角的终点、缩放级别、俯仰角和偏航角。代码如下map.flyTo({ center: [120.09, 30.24], zoom: 15, pitch: 70, bearing: 60, duration: 4000, essential: true });essential设置为true后即使用户系统开启了“减少动态效果”等辅助功能系统也会尽量保留这段动画减少因为系统设置导致动画被禁用的问题。5.3 Mapbox 连续航线巡航多个点位的连续巡航可以在每次flyTo结束后监听moveend事件再触发下一个点。核心代码const route [ { center: [120.15, 30.28], zoom: 13, pitch: 60, bearing: 0 }, { center: [120.09, 30.24], zoom: 16, pitch: 75, bearing: 120 }, { center: [120.07, 30.20], zoom: 14, pitch: 65, bearing: 240 } ]; let routeIndex 0; let flying false; function flyNext() { if (!flying) return; if (routeIndex route.length) { routeIndex 0; setTimeout(flyNext, 800); return; } const target route[routeIndex]; map.flyTo({ ...target, duration: 3000, essential: true }); routeIndex; map.once(moveend, () { if (flying) { setTimeout(flyNext, 600); } }); } function startFlight() { flying true; flyNext(); } function stopFlight() { flying false; map.stop(); } document.getElementById(startFlightBtn).addEventListener(click, startFlight); document.getElementById(stopFlightBtn).addEventListener(click, stopFlight);这段代码把每个巡航点都定义成对象后面的flyNext展开对象传入flyTo。注意在动画结束后必须调用一次map.stop()否则用户手动拖拽地图时,可能残留原来的动画状态。5.4 token 使用与安全Mapbox 的 token 分为公开发布 token 和密钥 token。网页前端会直接暴露 token 字符串因此需要在 Mapbox 后台设置 URL 白名单只允许特定域名使用。课程设计和比赛提交的代码如果包含 token建议同时准备一个空的演示 token 说明文档避免自己的额度被滥用。Mapbox 官方样式和服务在中国大陆网络环境下加载速度不稳定实际部署时要根据目标用户网络环境选择底图服务不能直接把演示代码当成公网生产代码。如果必须要完全离线部署建议改用 OpenLayers 加本地瓦片或者 Cesium 加本地底图。6. 使用 CesiumJS 实现三维飞行视角漫游6.1 初始化 Cesium 场景CesiumJS 最适合做“飞行视角-漫游”因为它有完整的相机对象、时间轴系统和 3D Tiles 支持。先初始化一个基本场景import * as Cesium from cesium; import cesium/Build/Cesium/Widgets/widgets.css; Cesium.Ion.defaultAccessToken 替换成自己的ion token; const viewer new Cesium.Viewer(cesiumContainer, { animation: true, timeline: true, baseLayerPicker: true, geocoder: true });如果不希望使用 Cesium ion 底图可以改成不加载 default 底图然后手动接入 OSM 瓦片const viewer new Cesium.Viewer(cesiumContainer, { baseLayer: false, baseLayerPicker: false, geocoder: false }); viewer.imageryLayers.addImageryProvider( new Cesium.OpenStreetMapImageryProvider({ url: https://tile.openstreetmap.org/ }) );baseLayer: false会在部分 Cesium 版本中生效你的版本如果提示参数不存在可以改为imageryProvider: false或使用viewer.imageryLayers.removeAll()后再添加图层。具体以所安装版本的 API 为准。6.
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →