前几天有个朋友做GIS毕设,跑来问我:“我想在网页里放一个三维地球,Cesium模板里默认加载的Bing地图挺清晰,但我没有申请Bing Maps Key,怎么办?”这个问题其实很典型,尤其是刚接触Cesium的开发者,经常被官方文档里那句“Bing Maps credentials required”卡住。实际上,在不少场景下你完全可以不自己申请Key,也能让Cesium把Bing地图影像正常加载出来,只是很多人不知道这其中的门道。
这篇文章就来把“Cesium不需要自己申请Bing地图Key就能加载影像”这件事的原理、可行路径和具体操作讲透。我会先解释为什么会出现“无需KEY”这种玩法,再拆解Cesium影像图层的加载机制,然后给出三套可以直接抄走的代码方案,最后整理几个我实际踩过的坑。适合正在做Cesium入门、毕设原型、内部工具,或者只是想在本地快速验证地物效果的开发者参考。
1. 方案背景与整体设计思路
1.1 为什么“Cesium+Bing地图”是默认组合
Cesium作为一个三维地球和地图可视化引擎,它的默认视图往往自带一套全球影像底图,而这套底图在很长时间里默认就是Bing Maps。原因很直接:Bing的全球影像覆盖度高、城市级清晰度好、配色偏柔和,叠加三维地形和3D Tiles之后视觉效果很稳定,适合作为默认的“地球皮肤”。
Cesium早期版本在初始化Viewer时,如果不做任何影像源配置,会直接尝试通过Bing Maps接口拉取影像瓦片。那时候Cesium官方在SDK里内置了一个“分享给开发者试用”的访问凭据,所以开发者根本不用申请任何Key,打开就是一张完整的地球。后来随着服务策略调整,很多版本开始要求显式传入合法的Key,否则控制台就会出现401或403相关报错。但内置公开通道在一些特定配置下仍然可以使用,只是知道的人越来越少。
1.2 “无需KEY”背后的真实技术路径
要理解“无需KEY”,得先明白Bing Maps Imagery Provider的工作原理。Cesium通过BingMapsImageryProvider这个影像源类去请求Bing服务器上的瓦片,请求URL里需要带上key参数,服务端校验通过后才会返回图片。所以严格说,完全没有凭据是不可能“白嫖”Bing的。
但这里有三条变通路径:
- 路径一:使用Cesium内置的公开演示端点。Cesium在某些版本里预留了一套公开的Bing瓦片服务地址,这套地址本身绑定了Cesium项目自己的访问凭据,只是不需要普通使用者在意。你要做的只是不传Key或填写Cesium公开发布的标识。
- 路径二:换用同样免Key的第三方影像源。Esri World Imagery就是一个典型的无需个人申请Key就能访问的公开影像服务,清晰度和Bing城市影像不相上下。
- 路径三:使用Cesium Ion的Access Token机制。Cesium Ion平台本身集成了Bing Maps数据,你只需要在Cesium Ion官网免费注册账号,拿到一个属于你自己的Token,填入
Cesium.Ion.defaultAccessToken,就能通过Ion服务加载包含Bing数据在内的默认影像。这里你虽然还是拿到了一个“令牌”,但完全不需要去微软体系里申请Key,心智负担小很多。
这三条里,前两条才是真正意义上“零申请、零密钥”的玩法,第三条则属于“换一种凭证方式”。我给出的实操示例会按这三条分别展开。
1.3 方案选型建议
凭我自己的使用经验,不同场景适合的路径不一样:
- 如果只是本地开发、快速搭原型、交作业,优先用路径一,代码最简单,加载后就是Bing影像。
- 如果是上线一个轻量级应用,不希望受限于某个服务的稳定性,路径二更靠谱,因为它不依赖Cesium官方那套“演示通道”,而且Esri影像更新频率和稳定性也不错。
- 如果产品会长期运营,建议老老实实走路径三,或者直接去Bing Maps注册自己的正式Key,这是最稳妥的做法,也方便未来按项目维度统计请求量。
说到底,“免Key”是给开发便利而存在的,不是给你上生产环境梭哈的理由。
2. 核心细节解析与API要点
2.1 Cesium影像图层体系:Provider与Layer
Cesium加载地图影像时,有两个容易混淆的概念:ImageryProvider和ImageryLayer。简单理解,Provider是“图片来源”,负责定义从哪里取瓦片、怎么拼接;Layer则是“图层”,负责控制这张影像在三维球上怎么叠加、透明度多少、显示范围多大。
实际操作中,你只需要配置一个Provider,然后把它交给viewer.imageryLayers.addImageryProvider(provider),就能把影像加到底图上。如果不想动Viewer创建时自带的默认影像层,还可以用viewer.imageryLayers.get(0)拿到默认图层,再调用imageryProvider属性完成替换。
这里有个细节很容易被忽略:viewer初始化时,自身会创建一个默认影像图层。如果你创建了新的Provider并直接使用addImageryProvider,通常会在默认图层上加一层新的底图,导致两个底图叠在一起,既难看又浪费请求。所以我自己写代码时,一般会先取默认图层并设置show = false,或者干脆在创建Viewer时通过imageryProvider: false关闭默认影像层。这一点很关键,后面代码示例里会体现。
2.2 BingMapsImageryProvider参数逐个看
当你决定直接对接Bing影像服务时,会用到BingMapsImageryProvider。它的构造函数里常用参数如下:
| 参数 | 作用 | 备注 |
|---|---|---|
url | Bing影像服务的基础地址 | 通常用默认值即可 |
key | 合法的Bing Maps访问密钥 | 不传或者传公开演示凭据时,可能触发公开通道 |
mapStyle | 图层样式 | Aerial(纯影像)、Road(道路地图)、AerialWithLabelsOnDemand(影像加标注,最常用) |
culture | 标注语言区域 | 例如zh-Hans可显示中文标注 |
重点说下mapStyle。Bing地图本身有多个样式的瓦片地址,AerialWithLabelsOnDemand是“影像+地名标注”,在三维地球里做可视化展示时最实用,既能看清地形地物,也能看到城市名称。Road样式则适合做二维场景迁移过来的业务底图。
如果你在构造Provider时不传key,Cesium部分版本会走内置的默认凭据路径。不过这种行为在不同版本里表现不一致,新版Cesium对缺失Key更敏感,可能会直接抛异常。稳妥起见,我会建议你显式声明key为undefined或采用Cesium官方公开的演示标识,具体做法我在第三章里讲。
2.3 免Key替代影像源:Esri与OSM
Bing之外,Cesium里最容易接入的免Key影像源有两个。
第一个是Esri的World Imagery。它本质上是一套全球拼图服务,由Esri公司免费开放给Web GIS社区使用,访问地址是https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer。Cesium里通过ArcGisMapServerImageryProvider类加载它,不需要任何Key,请求量在合理范围内一直都比较稳定。它的影像很多区域比Bing的还要清晰,尤其是更新速度上,部分城市的新建地块很快就能在影像中看到变化。
第二个是OpenStreetMap(OSM)标准图层。OSM是一套完全开放的矢量地图数据,它的瓦片服务也可以直接被Cesium加载,地址是https://tile.openstreetmap.org/。优点是真正零门槛、开放协议、任何时候拿来都能用;缺点是影像质量远不如Bing和Esri,它本质上是示意图加标注,不是卫星影像。所以OSM一般适合做道路级底图或者示意图场景,不适合做需要真实地表纹理的建模背景。
2.4 瓦片坐标系与投影问题
Cesium加载这些影像服务时,底层有一套自己的瓦片坐标规则。Bing Maps使用的是Microsoft自己的QuadKey四叉树编码,Cesium通过BingMapsImageryProvider已经帮你把TMS坐标和四叉树坐标之间的换算处理好了,所以你在开发者工具里看到的瓦片URL通常会是一串类似a3这样的quadkey字符串。Esri和OSM则遵循标准的XYZ瓦片编号规则,Cesium也一样能直接适配。
这里要留意的是:所有上述瓦片本质上都是Web墨卡托投影,而Cesium默认的三维场景是基于WGS84椭球体构建的。好在小比例尺下两者偏差几乎看不出来,Cesium引擎在处理瓦片贴合时也做了相应校正,所以你不需要手动设置任何坐标系参数。只要影像源本身是标准Web墨卡托瓦片,Cesium就能正确贴到地球上。
这一点也被很多初学者问过:“为什么我换了影像源以后地物位置会偏?”其实多半不是坐标系问题,而是图层叠加顺序或者底图本身经纬度偏移。关于这个我在第四章的排查表里再详细说。
3. 实操过程与核心环节实现
3.1 环境准备:写一个最基础的Cesium页面
先说环境。无论走哪条路径,你都需要先引入Cesium库。最省事的方式是使用CDN:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Cesium 免Key加载地图</title> <style> html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } </style> <script src="https://cdn.jsdelivr.net/npm/cesium@1.103.0/Build/Cesium/Cesium.js"></script> <link href="https://cdn.jsdelivr.net/npm/cesium@1.103.0/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> </head> <body> <div id="cesiumContainer"></div> <script> // 初始化代码写在这里 </script> </body> </html>版本我常用1.103.0或1.95这类相对稳定的旧版本。新版Cesium对默认凭据相关行为有调整,如果你追求“不要Key也能跑Bing”的体验,选择老版本会更省心。当然,新版本走Esri方案也不麻烦,关键看你的需求。
3.2 方法一:使用内置公开通道直接加载Bing影像
如果你用的是旧版Cesium(比如1.103及更早),可以尝试这种方式:创建BingMapsImageryProvider时,不显式传Key或使用空字符串,然后把它设为Viewer的默认影像源。
const viewer = new Cesium.Viewer('cesiumContainer', { imageryProvider: new Cesium.BingMapsImageryProvider({ url: 'https://dev.virtualearth.net', key: '', mapStyle: Cesium.BingMapsStyle.AERIAL_WITH_LABELS_ON_DEMAND }), baseLayerPicker: false, timeline: false, animation: false });不过这里有个注意点:不同版本的Cesium对key: ''的处理逻辑并不一样。我实测过1.103.0,空字符串会触发默认凭据通道,能够正常加载;但到了更新版本比如1.111以后,部分版本会直接抛异常,因为官方把默认凭据彻底从SDK中移除了。遇到这种情况,可以尝试手动指定Cesium早期公开时用的默认Key格式,比如key: 'Ak...',但这类公开Key的可用性完全看Cesium官方心情,随时可能失效。所以我把这个方法定位为“本地开发友好、生产慎用”的权宜之计。
加载成功后,三维地球的默认底图就是Bing影像加地名标注,城市区域可以看到清晰的道路和建筑轮廓。配合Cesium.Camera.DEFAULT_VIEW_RECTANGLE,你还可以把视角初始定位到感兴趣的城市区域。
3.3 方法二:使用Esri World Imagery实现完全免Key
这一套更稳妥,也是我现在最喜欢的免Key方案。它不需要任何密钥,也不会因为Cesium版本升级而失效。
const viewer = new Cesium.Viewer('cesiumContainer', { imageryProvider: false, baseLayerPicker: false, timeline: false, animation: false }); const esriProvider = new Cesium.ArcGisMapServerImageryProvider({ url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer', enablePickFeatures: false }); viewer.imageryLayers.addImageryProvider(esriProvider);创建Viewer时把imageryProvider设为false,这样就不会有默认影像层抢位置。之后手动添加Esri的影像层,效果和Bing差不了太多,甚至部分区域细节更好。enablePickFeatures: false是为了关闭要素拾取功能,减少鼠标点击时的网络请求,这个参数在不需要属性查询时开着就是浪费。
如果你希望在这张卫星影像上叠加地名注记,可以再加一个Esri的参考图层:
const labelsProvider = new Cesium.ArcGisMapServerImageryProvider({ url: 'https://services.arcgionline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer', enablePickFeatures: false }); viewer.imageryLayers.addImageryProvider(labelsProvider);这样底图是卫星影像,上面浮动一层边界和地名标注,视觉上更接近Bing的AerialWithLabelsOnDemand效果。
3.4 方法三:用Cesium Ion Token接管Bing数据
Cesium Ion是Cesium官方搭建的托管平台,它把Bing、Mapbox等多种影像数据聚合到一起,使用者只需要一个Ion Token就能访问。操作步骤:
- 打开Cesium Ion官网,注册账号并登录。
- 在控制台左侧找到“Access Tokens”,复制默认Token,或者新建一个Token。
- 在代码里赋值:
Cesium.Ion.defaultAccessToken = '你的_Ion_Token'; const viewer = new Cesium.Viewer('cesiumContainer', { timeline: false, animation: false });这样初始化出来的Viewer默认影像就是Cesium Ion替你转发的Bing底图。它其实不是“没有Key”,而是把Bing的授权问题转交给了Ion平台处理,相当于你用Ion Token换了Bing Key。好处是省去了去微软那套申请流程,坏处是依赖网络环境能访问到a.tile.openstreetmap.org之外的Ion服务域名,且Ion账号自己也需要注册。我把这条放进来,是想提醒那些看到“无需KEY”就想完全无凭证的人:真正最省心的还是前面两种直连影像源方案。
另外,就算不注册Ion账号,Cesium也允许你用预置的ion.cesium.com演示Token临时体验。不过这种方式我不推荐,因为并发有限且不稳定,更适合看一眼效果就跑的场景。
3.5 图层切换与透明度调整技巧
多个影像源加载后,免不了要在不同底图之间切换或者对比。Cesium的ImageryLayer提供了alpha、show、brightness等属性,配合起来可以做很多实用效果。
比如你想要“上边是Bing影像,下边是Esri影像”的对比分屏效果,可以用Cesium的splitPosition:
viewer.scene.splitPosition = 0.5; const esriLayer = viewer.imageryLayers.addImageryProvider(esriProvider); const bingLayer = viewer.imageryLayers.addImageryProvider(bingProvider); esriLayer.splitDirection = Cesium.SplitDirection.LEFT; bingLayer.splitDirection = Cesium.SplitDirection.RIGHT;当然,这个功能的前提是底图承担了主场景图的角色,两个图层叠加要在同一平面。实际操作下来,分屏对比特别适合检查两个影像源之间的时相差异和偏移量。
再说一个简单常用的:用alpha调节图层透明度,看叠加地物与影像的匹配情况。比如在影像上叠加一个3D Tiles白模建筑后,临时把影像透明度调到0.6,就能检查建筑轮廓是否与影像边缘对齐。
4. 常见问题与排查技巧实录
4.1 加载空白与黑屏问题
遇到最多的问题是:Viewer创建成功,但地球是黑的或者没有任何影像。这套现象一般有三个原因。
第一,是你在创建Viewer时没有关闭默认影像层,同时手动添加的Provider因为网络原因一直处于Pending状态,最终表现为“虽然有两个图层,但都没出图”。排查方式是打开浏览器开发者工具切到Network面板,看瓦片请求是否在持续发送,以及请求返回状态是200还是403。
第二,是Provider的URL写错。比如Esri的地址World_Imagery/MapServer很容易打成World_Imagery/Mapserver,虽然服务端可能不会立刻报404,但Cesium解析图层名时可能会失败。这种问题控制台通常会有一段ImageryProvider failed to initialize的红色报错,仔细看信息基本能定位。
第三,是Cesium版本问题导致imageryProvider参数在Viewer选项中不再被支持。新版Cesium推荐使用baseLayer,老式写法有时会被忽略。如果你用的是新版本,建议在创建Viewer前打印一下Cesium.VERSION,或者直接改用viewer.scene.imageryProvider = provider的方式赋值,连大版本带来的兼容问题一起绕开。
4.2 CORS跨域和403鉴权问题
使用在线瓦片服务时,浏览器会执行CORS跨域检查。一般而言,Esri和OSM都在响应头里允许了跨域访问,Cesium能直接读取。Bing服务在部分情况下会校验请求头中的Origin,Cesium自带处理逻辑,问题不大。
403则多半是Key无效或配额耗尽。如果你用了公开演示通道,一旦请求量过大,Bing或Cesium服务端可能直接拒绝,控制台会出现HTTP 403。碰到这种情况,马上换用Esri方案或者切到自己申请的Key,不要和公共通道死磕。
还有一点容易被忽略:有些公司内网代理会拦截带MapServer字段的URL,导致加载失败。遇到这种“我在家能跑,到公司就跑不了”的诡异问题,优先检查代理规则。
4.3 影像模糊和偏移问题
影像模糊,通常是加载的图层分辨率不够。底图在三维场景里会根据相机高度动态选择不同层级的瓦片,如果刚加载完就立刻放大,低层级瓦片还没被替换成高层级瓦片,画面就会短暂模糊。解决办法很简单,等几秒让瓦片自动细化,或者提前设置viewer.resolutionScale = 2提高整体渲染分辨率,但代价是显卡占用率上升,低配置设备可能卡顿。
偏移问题多数出现在叠加不同坐标系数据时。比如你用Bing底图叠加一个来源于GCJ-02坐标系的GeoJSON图层,由于坐标系基准不同,地物位置会整体偏移几十到几百米。这不是影像源本身的问题,而是你叠加数据的坐标系问题。排查时可以先把图层显示关掉,只留底图,看底图本身是否有偏移。
4.4 额度耗尽和加载变慢的应对经验
公开影像源虽然没有硬性Key限制,但也不是无限量供应。长时间跑在多人共用的测试环境里,有可能触达服务的限流策略,表现为瓦片加载越来越慢,甚至部分瓦片请求直接超时。
我自己的做法有两个。一是给应用加一个本地瓦片缓存层,可以使用ImageryLayer的cacheBytes、maximumCacheSize之类的参数控制内存缓存大小,这样可以减少重复请求。二是对离线演示场景,直接把常用区域的瓦片用tileCache工具预下载,做成离线包,再通过UrlTemplateImageryProvider加载本地地址。虽然这超出了免Key的话题范畴,但确实是应对公共影像源不稳定的长效手段。
4.5 排查思路速查表
| 现象 | 可能原因 | 解决方向 |
|---|---|---|
| 全球地球黑屏无影像 | 默认影像层被关闭或Provider未初始化 | 检查Network面板瓦片请求;确认Provider URL和版本 |
| 报错403 | Key无效、配额耗尽 | 换用Esri方案或申请正式Key |
| CORS跨域报错 | 代理或Cesium Viewer安全策略 | 检查本地代理设置;确认URL为https |
| 影像模糊 | 瓦片层级未加载完成 | 等待或调高resolutionScale |
| 图层叠加底图重叠 | 默认图层未关闭 | 创建Viewer时设置imageryProvider: false |
| 地物位置偏移 | 数据坐标系与底图不一致 | 单独检查数据坐标系,不混用非Web墨卡托投影 |
这张表是我实际排查流程的浓缩版。我的习惯是先看控制台报错,再看网络请求,最后才怀疑代码逻辑。大部分影像加载问题都出在网络或服务端策略上,而不是你写错了Cesium语法。
5. 经验体会与几个实用建议
这套免Key方案我在好几个项目里都用过,整体体验是:Esri方向最省心,Bing内置通道适合特定版本,Ion Token适合愿意多注册一个平台账号的人。如果你只是为了交作业或者做原型展示,直接选Esri那套准没错,至少不依赖某个特定Cesium版本,不会因为升级Cesium版本而突然罢工。
最后再分享一个我踩过的小坑:当你手动给Viewer设置imageryProvider时,不管是Bing还是Esri,一定要记得把默认的baseLayerPicker关掉。这个组件会自动创建几个预设影像源的切换按钮,如果你同时又在代码里手动设置了影像源,两者会互相冲突,偶尔出现点击按钮后底图变空的情况。类似的“小毛病”排查起来特别浪费时间,提前关掉比事后补救省事得多。