ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Cesium加载天地图、OSM、谷歌底图全攻略:选型、代码与避坑

Cesium加载天地图、OSM、谷歌底图全攻略:选型、代码与避坑 做GIS可视化或者数字孪生项目不管是搞Cesium三维地球还是用Leaflet、OpenLayers做二维地图第一步永远是选底图。底图选对了项目事半功倍底图一出问题全局看着都不对劲。这篇文章就专门讲怎么在Cesium里加载三种最常见的在线底图天地图、OSM和谷歌底图把每种方案的适用场景、代码实现和常见坑都过一遍。无论你是刚接触Cesium的新手还是在公司里维护WebGIS基础框架的老手这套东西都能直接抄作业。先说结论这三种底图没有绝对的好坏只有适不适合当前项目。天地图在国内项目里最稳中文注记全、服务合规、用官方WMTS接口稳定缺点是全球其他区域数据质量一般OSM开放免费、加载简单适合做原型验证和海外项目但公共瓦片服务器有并发限制生产环境不能死磕谷歌底图影像细节丰富、全球覆盖度高做视觉效果极佳的飞行漫游和数字沙盘很合适但要在技术选型和合规层面提前想清楚。我们一个个拆开看。1. 底图选型三种在线底图到底该怎么挑很多初学者一开始就纠结“哪个底图最好”其实这个问题没有标准答案。你看一个项目用了什么底图基本就能反推出这个项目的用户群体、数据来源和部署环境。1.1 天地图国内项目优先考虑的正规军天地图是国家地理信息公共服务平台提供的在线地图服务最大的优势是中文地名、道路、水系等要素标注非常全而且在国内范围的数据更新及时还免费提供WMTS标准的瓦片接口。做政务项目、智慧城市、园区管理、应急系统这类场景天地图几乎首选——一方面合规上省心另一方面坐标体系用的是CGCS2000和国内测绘数据的对接成本低。它的瓦片服务分为影像img、矢量vec、地形晕渲ter以及对应的注记层cia、cva、cta。实际项目里最常见的组合是“影像影像注记”或者“矢量矢量注记”原理其实和你在二维地图里叠加标注图层一样只不过在Cesium里要分别用两个ImageryProvider去加载然后叠在一起。1.2 OSM免费开放、适合原型和演示OpenStreetMap是开源社区维护的全球地图数据瓦片地址就是一个简单的https://tile.openstreetmap.org/{z}/{x}/{y}.png不用申请key不需要token代码里三五行就能跑起来。所以做POC、Demo、内部工具的时候OSM几乎是效率最高的选择。但代价也很明显一方面公共瓦片服务器的使用政策明确说了不允许高并发、高流量地调用生产环境如果直接用官方瓦片地址很容易被限流或者封IP另一方面OSM在国内的中文注记和POI信息并不完整渲染风格也比较素不适合作为面向大众用户的正式产品底图。更合理的做法是把它当成“临时底图”或者用工具提前下载瓦片做离线发布。1.3 谷歌底图影像质感好、细节丰富谷歌底图的瓦片服务地址比较特殊属于“公开可用但没有正式开放接口”的类型。它的优势非常直观卫星影像清晰度高、全球覆盖完整、色调统一尤其在大城市和地形复杂区域影像细节往往比天地图更丰富。很多数字孪生项目尤其是涉及建筑外观、地形地貌、飞行浏览的场景都会优先用谷歌影像底图来撑视觉效果。需要说明的是谷歌底图的可用性和部署环境直接相关每个人的服务器或本地网络能不能顺畅访问对应瓦片服务器情况不一样。如果访问不到就不要硬上换回天地图影像或者自建瓦片服务效果也未必差。技术方案本身没有对错能在当前环境稳定跑起来才是第一原则。对比项天地图OSM谷歌底图坐标系Web墨卡托近似WGS84Web墨卡托Web墨卡托是否需要密钥免费申请tk不需要不需要最大瓦片级别18级左右19级20级以上中文注记完整一般较丰富国内影像更新较快较慢大城市较好高并发生产使用较稳定受限明显依赖网络环境2. 动手前的关键准备瓦片规则、TilingScheme 与 tk底图选好了先别急着写代码有三个方面如果不搞清楚后面一定会栽跟头。2.1 XYZ 瓦片到底是怎么编号的在线底图本质上是把全球地图切成了无数张正方形小图浏览器端根据当前中心点和缩放级别动态请求需要的瓦片。这个切片规则通常叫XYZz是缩放级别x是瓦片列号y是瓦片行号原点在左上角。OSM、谷歌底图、天地图的WMTS服务走的都是这套规则所以用Cesium的UrlTemplateImageryProvider可以直接替换对应的URL模板。但要注意瓦片底层还有TMS规则行号从下往上数和XYZ的y方向相反。Cesium的URL模板里提供了{reverseY}变量专门用来兼容TMS服务。如果你遇到某个瓦片服务加载出来上下颠倒、拼接错乱十有八九就是TMS和XYZ搞混了。判断方法很简单打开浏览器开发者工具看Network随便找一张瓦片URL对比y值和实际地图位置基本就能确认方向。2.2 TilingScheme 是新手最容易踩的坑在Cesium里UrlTemplateImageryProvider默认使用地理坐标系切片方案GeographicTilingScheme也就是按经纬度等间隔切片的。但天地图、OSM、谷歌底图全是Web墨卡托投影EPSG:3857必须显式指定tilingScheme: new Cesium.WebMercatorTilingScheme()。这个参数漏掉的后果很典型地图不是黑屏就是影像被拉得歪歪扭扭或者只有低层级正常、放大后全部错位。原因在于谷歌墨卡托的切片范围是[-20037508.34, 20037508.34]和经纬度范围[-180, 180]不一样同一个z级别下两种方案的瓦片分割位置完全不同。你如果不声明Cesium就会用错配的规则去请求和贴图自然对不上。我见过不少同事代码看着没问题就是漏了这一行排查一下午。所以记住一句话凡是加载在线互联网底图TilingScheme必须写WebMercatorTilingScheme。2.3 天地图 tk 的申请和使用注意天地图虽然免费但需要先申请一个密钥tk。流程不复杂去天地图官网注册账号进入开发者控制台创建一个“浏览器端”应用系统会生成一串tk。每一个请求的URL上都要带上tk这个参数否则服务会返回错误。这里有两个容易忽略的细节。第一tk创建时通常要配置域名白名单或者IP白名单如果你在本地调试一切正常换到服务器上就报403先检查是不是白名单没配好。第二tk是跟应用绑定的不要硬编码在前端代码里提交到公开仓库否则被人盗刷流量或者恶意调用损失的是你自己。至少要做个后端代理转发把tk藏在服务端。3. 三种底图加载的完整代码实现接下来进入正题。下面的代码我都基于一个基础的Cesium Viewer来写默认你已经初始化好了viewer对象。3.1 天地图影像、矢量与注记天地图的WMTS接口地址比较长核心参数是LAYER不同的图层类型对应不同的值img是影像vec是矢量cia是影像注记cva是矢量注记。我习惯先封装一个函数后面复用起来很爽。function createTiandituProvider(layer, tk) { return new Cesium.UrlTemplateImageryProvider({ url: https://t{s}.tianditu.gov.cn/ layer _w/wmts?SERVICEWMTSREQUESTGetTileVERSION1.0.0LAYER layer STYLEdefaultTILEMATRIXSETwFORMATtilesTILEMATRIX{z}TILEROW{y}TILECOL{x}tk tk, subdomains: [0, 1, 2, 3, 4, 5, 6, 7], tilingScheme: new Cesium.WebMercatorTilingScheme(), maximumLevel: 18 }); } const TD_TK 你的tk值; // 先移除默认底图避免和自建底图叠加 viewer.imageryLayers.removeAll(); // 加载影像底图 viewer.imageryLayers.addImageryProvider(createTiandituProvider(img, TD_TK)); // 叠加影像注记 viewer.imageryLayers.addImageryProvider(createTiandituProvider(cia, TD_TK));注意这里的subdomains天地图的瓦片服务分布在t0.tianditu.gov.cn到t7.tianditu.gov.cn这8个子域名上Cesium的URL模板用{s}占位后会自动轮询这些子域名。这种做法的好处很实际浏览器对同一域名的并发连接数有限制子域名轮询可以有效分散请求压力明显提升地图加载速度。如果你需要矢量底图只要把layer换成vec和cva就行。实际项目里影像底图适合做宏观展示矢量底图适合做业务数据叠加可以根据场景灵活切换。3.2 OSM 标准瓦片加载OSM的加载要简单得多连token都不需要。直接拼URL模板const osmProvider new Cesium.UrlTemplateImageryProvider({ url: https://tile.openstreetmap.org/{z}/{x}/{y}.png, tilingScheme: new Cesium.WebMercatorTilingScheme(), maximumLevel: 19 }); viewer.imageryLayers.removeAll(); viewer.imageryLayers.addImageryProvider(osmProvider);这里要注意OSM公共瓦片服务器的格式以前老教程里写的是http://a.tile.openstreetmap.org/{z}/{x}/{y}.png这类带字母子域名的地址现在官方已经迁移到tile.openstreetmap.org单域名并且强烈建议用HTTPS。如果你还按老地址写部分网络环境下会加载失败。另外Cesium自带一个OpenStreetMapImageryProvider但是它的配置灵活性不如UrlTemplateImageryProvider尤其在自定义子域名、自定义最大级别、加请求参数这些场景下。所以我个人建议统一用UrlTemplateImageryProvider一套思路解决所有底图加载问题。3.3 谷歌影像、混合与矢量加载谷歌底图的瓦片服务域名为mt0.google.com到mt3.google.com通过lyrs参数区分图层类型m是矢量街道图s是纯影像图y是影像加注记的混合图p是带地形晕染的地形图。最常用的还是s和y。function createGoogleProvider(lyrs) { return new Cesium.UrlTemplateImageryProvider({ url: https://mt{s}.google.com/vt/lyrs lyrs x{x}y{y}z{z}, subdomains: [0, 1, 2, 3], tilingScheme: new Cesium.WebMercatorTilingScheme(), maximumLevel: 20 }); } // 加载纯影像底图不带标注 viewer.imageryLayers.removeAll(); viewer.imageryLayers.addImageryProvider(createGoogleProvider(s)); // 需要标注时加载混合图 viewer.imageryLayers.removeAll(); viewer.imageryLayers.addImageryProvider(createGoogleProvider(y));一个很实用的小技巧如果你想要“影像底图自研标注”的效果不要用lyrsy而是加载纯影像lyrss然后通过Cesium的Entity、3D Tiles或者Primitive在场景里绘制你自己的业务标注。这样既保留了谷歌影像的高质感又能完全控制注记的样式和更新频率。混合图里的标注是图片格式你没法单独控制它们的显隐更没法绑定点击事件。4. 多图层组合与业务扩展只会加载单层底图只能算入门实际项目中你大概率还要做底图切换、图层叠加这些操作。4.1 多底图切换与透明度控制很多应用会在右上角放一个底图选择器让用户自己切换影像、矢量、街道等底图。实现逻辑很简单切换时先把现有的影像图层清掉再加新的Provider进去。function switchBaseLayer(type) { // 先清掉所有影像图层 viewer.imageryLayers.removeAll(); if (type tianditu) { viewer.imageryLayers.addImageryProvider(createTiandituProvider(img, TD_TK)); viewer.imageryLayers.addImageryProvider(createTiandituProvider(cia, TD_TK)); } else if (type osm) { viewer.imageryLayers.addImageryProvider(osmProvider); } else if (type google) { viewer.imageryLayers.addImageryProvider(createGoogleProvider(y)); } }如果你不是想彻底替换而是想做一个半透明叠加对比比如同时显示天地图影像和谷歌影像来对比某个区域的变化那就要用到图层的透明度属性。const layer viewer.imageryLayers.addImageryProvider(createGoogleProvider(s)); layer.alpha 0.6; // 60%不透明度透明度数值在0到1之间0完全透明1完全不透明。这个功能在数据对比、历史影像分析、竣工前后效果展示里非常实用。调透明度的时候建议同时关闭其他临时图层不然一层叠一层视觉上会非常乱。4.2 底图与3D Tiles、业务数据共存底图加载归底图业务数据归业务数据。在Cesium里影像图层和3D Tiles、Entity、Primitive是两条独立的渲染管线互不干扰。你可以在影像底图之上随便叠加3D Tiles模型、点击事件、热力图、雷达扫描效果等等。但有一个顺序问题要特别提醒Cesium的imageryLayers集合有层级概念后添加的ImageryLayer默认排在上面。如果你先加了影像底图再加注记层注记自然在影像上方排列是正确的。但如果你用removeAll()清图后重新添加注意顺序别搞反否则就会出现“注记被影像盖住”的情况。如果需要手动调整层级可以这样viewer.imageryLayers.raiseToTop(annotationLayer); // 提升到顶层 viewer.imageryLayers.lowerToBottom(baseLayer); // 降到底层这套操作在底图切换、业务叠加频繁的项目里几乎天天用建议直接封装成公共方法。5. 常见问题与排查实录下面这些问题都是我在实际项目里踩过的也是团队新人来找我问得最多的。整理成速查表放在这里方便你直接对照排查。5.1 底图不显示白屏或黑色地球绝大多数情况是这三类原因第一Cesium默认加载了Ion的全球影像你以为自己加的底图没生效实际上是加了但被盖住了。排查办法是先把imageryLayers.removeAll()执行一遍再添加自己的Provider。我建议在初始化Viewer时就设置baseLayer: false或imageryProvider: false从源头避免默认底图干扰。第二URL模板里的占位符写错了。天地图的URL里有{z}、{y}、{x}三个占位符OSM是{z}/{x}/{y}你把顺序搞错请求出来的瓦片肯定不对。这里推荐一个调试方法随便打开一张瓦片URL把{z}、{x}、{y}手动替换成1、0、0在浏览器里直接访问如果能出图说明URL模板没问题否则就是地址或者权限问题。第三TilingScheme没配对。这个上面已经反复强调了遇到影像拉伸、错位、放大后瓦片丢失第一反应就去检查tilingScheme是不是WebMercatorTilingScheme。5.2 天地图tk无效或返回403优先怀疑三个地方tk是否粘贴完整、是否带了多余空格tk创建时配置的白名单是否包含当前域名本地测试为localhost以及是否误用了旧版域名。天地图的接口域名经历过调整网上很多老教程写的是旧地址直接拿来用会失败。现在标准地址是https://t{s}.tianditu.gov.cn注意是gov.cn结尾。另外天地图的瓦片服务对HTTPS支持没有问题但如果你的页面是HTTPS、瓦片是HTTP浏览器会拦截混合内容。反过来也一样。解决办法是保持页面协议和瓦片协议一致优先全站HTTPS。5.3 OSM加载慢、一会儿加载一会儿空白如果单纯是慢大概率是网络问题。但如果你在项目里高频刷新页面、循环请求大量瓦片很快就会被OSM官方限流。观察Network面板如果很多瓦片请求返回429 Too Many Requests那就是被限流了。生产环境不能依赖OSM官方公共瓦片建议用两种替代方案一是自己部署瓦片服务器提前把目标区域的OSM瓦片下载到本地或用工具批量切片发布二是换成天地图毕竟国内访问速度快很多。5.4 底图之间坐标对不齐差了几百米天地图、OSM、谷歌底图都基于Web墨卡托瓦片对齐是没有问题的。但如果你以前做过高德、腾讯地图注意了国内很多商用地图用的是GCJ-02火星坐标系和这三类标准XYZ底图存在偏移。业务数据如果是从高德坐标体系采集的直接叠加到天地图或Google底图上位置就会漂移。解决办法是写一个坐标转换方法把GCJ-02坐标转成WGS84坐标后再渲染。网上有很多公开的转换算法和测试用例这里不展开但方向要知道底图是标准Web墨卡托时业务数据不能直接拿火星坐标往上怼。现象可能原因处理方案白屏无底图默认Ion图层覆盖/未添加成功removeAll, 检查URL模板影像拉伸错位TilingScheme错误改成WebMercatorTilingScheme高缩放级别空白maximumLevel设置过低调高到18/19/20请求403/401tk失效/白名单未配置检查tk和域名白名单瓦片加载缓慢并发受限/单域名开启子域名轮询缓存数据坐标偏移GCJ-02火星坐标先转WGS84再叠加最后再分享一个小技巧。我在项目里给底图做选型时不只是看加载代码能不能跑通还会花十分钟做“压力测试”把相机快速飞到全国不同区域、不同层级截几个关键画面看瓦片加载速度和清晰度。这个办法用不了多长时间但能提前暴露不少问题比如某个区域没影像、某个层级瓦片空白、某个子域名响应慢。底图是全局的一处出错整个项目在客户眼里都是掉价的。宁可这十分钟多花在测试上也不要上线后让用户在汇报现场帮你找bug。
返回列表