首页 / 资讯中心 / 文章详情

OpenLayers加载天地图瓦片:从零搭建WebGIS底图实战

OpenLayers加载天地图瓦片:从零搭建WebGIS底图实战 ★ FEATURED ARTICLE
我一直觉得WebGIS 入门最劝退的不是算法也不是服务端切片而是“底图从哪来”。以前帮某项目组做数据可视化时第一版方案直接在页面里嵌入商业地图 JS API开发确实快但一进入业务评审就卡住了坐标加密、资质要求、调用配额每一项都要重新谈。后来我们把底图切到天地图半小时就把地图拉起来了也正是那次经历让我决定把“加载天地图”作为 WebGIS 实战练习的第一篇。这篇内容聚焦一件事用 OpenLayers 在前端页面里正确加载天地图瓦片。适合刚接触 WebGIS、会一点 HTML/CSS/JavaScript、想自己动手把地图跑起来的读者。全程不涉及复杂后端跟着做就能看到地图出现在浏览器里同时我会把最容易被忽略的坐标、密钥、协议细节一起讲清楚。1. 底图选型为什么这个练习从天地图开始1.1 消费级地图 API 和 WebGIS 底图的差异很多前端同学习惯用商业地图的 JS API一个 script 标签加一段初始化代码地图就有了。但在实际 WebGIS 项目里这类方案往往不是首选原因集中在三个方面。第一是坐标体系。商业地图对外展示用的坐标普遍经过偏移处理而你手里的业务数据——无论是 GPS 采集点、测绘成果还是第三方提供的 GeoJSON——大多基于公开标准坐标。两套坐标混在一起点位会“漂移”到马路对面去。要修正就得写纠偏算法而纠偏规则本身不公开维护成本很高。第二是服务形态。商业地图 JS API 通常只能通过它的脚本库调用瓦片地址不透明不便和自研图层、服务端数据灵活拼接。而天地图提供标准的 WMTS/REST 瓦片服务任何支持 XYZ 规范的 Web 地图库都能直接消费这为后续叠加业务图层留了很大空间。第三是合规和配额。正规项目对地图服务的数据来源、版权、部署环境有明确要求免费的消费级 API 不一定适用。天地图作为国家级互联网地图服务网站提供的是权威、公开、稳定的瓦片数据源对项目组来说省去了大量沟通成本。当然如果你只是做一次性展示页商业地图 API 仍然很方便。但如果你想系统学习 WebGIS、做点能沉淀的业务功能从天地图开始更合适。1.2 坐标基准一开始就要树立的意识这一步必须提前讲否则后面大概率出问题。地图瓦片在浏览器里显示时采用的是 Web 墨卡托投影也就是 EPSG:3857这个坐标系把地球近似展开成一张平面图适合做底图显示和缩放。而天地图服务里瓦片服务命名中带w的通常是 Web 墨卡托投影带c的通常对应经纬度地理坐标系具体以官方服务文档为准。加载时选哪个直接影响瓦片能不能对齐、业务数据能不能叠上去。简单理解浏览器地图世界是“平面投影坐标”业务数据通常是“经度纬度”两者之间需要转换。OpenLayers 里ol.proj.fromLonLat()就是干这件事的后面代码里会用到。现在你只需记住——不要拿着经纬度直接填到以 3857 为投影的地图中心点上规范做法是先转坐标。2. 动手前准备密钥申请、服务地址与容易忽略的协议细节2.1 申请应用密钥的完整流程加载天地图瓦片不是把 URL 拼上去就能跑需要先获得一个授权密钥就是代码里常见的tk参数。流程不复杂打开天地图官网注册账号进入开发者页面创建应用后系统会分配一个应用密钥。创建应用时通常需要填写应用名称、类型等信息。个人学习用途和项目商用用途的配额不同具体以官网当时公布的开发者规则为准。这里提醒三点。第一密钥是和域名/应用绑定的如果本地调试用file://协议直接打开 HTML部分环境下请求会被拦或无效建议用一个本地静态服务来跑比如npx serve或 VS Code 的 Live Server 插件。第二密钥不要硬编码到需要提交到公开仓库的业务代码里后面我讲封装时会给出临时方案但彻底解决要靠服务端代理。第三官方文档里的服务地址模板会变域名、路径、参数以“服务资源”页面展示的最新地址为准不要凭记忆写死。2.2 认识瓦片服务地址WMTS 与 REST 的细节天地图瓦片服务看起来是一串很长的 URL拆开看就清晰了。以矢量底图服务为例一个典型的请求参数包括参数含义示例值SERVICE服务类型WMTSREQUEST操作类型GetTileLAYER图层名称vecTILEMATRIXSET瓦片矩阵集即投影/坐标系w 或 cTILEMATRIX缩放层级zTILEROW瓦片行号yTILECOL瓦片列号xFORMAT瓦片格式tilestk应用密钥申请得到的 key可以看到瓦片服务的本质就是“按照层级、行号、列号去请求一张图片”这正是 XYZ 规范做的事。所以 OpenLayers 里用ol.source.XYZ就可以把天地图当作普通瓦片源加载不需要手动维护复杂的 WMTS 网格。如果你看到某个项目里写的是 WMTS 图层也别慌只是换了个更严格的瓦片网格定义方式底层请求到的还是格式化的瓦片图片。2.3 HTTPS 和跨域浏览器控制台最容易暴露问题的地方现在网页基本都是 HTTPS 环境。如果你的页面是 HTTPS而瓦片请求地址写成了http://浏览器会直接拦截混合内容地图区域干干净净一片空白。所以服务地址务必使用 HTTPS。跨域问题相对隐蔽。浏览器加载瓦片属于跨域资源请求天地图服务通常允许跨域访问但如果你在本地随便起服务有些配置下还是会看到 CORS 报错。我遇到过一次项目组内部网络代理拦截了带tk参数的请求响应头瓦片加载失败排查半天不是代码问题。如果页面控制台出现“No Access-Control-Allow-Origin header”之类的信息不要立刻觉得是天地图服务的问题先检查自己的页面是否走了代理、服务地址是否写错、密钥是否有效。具体排查链路我放在第 4 章那儿全是实战教训。3. 核心代码用 OpenLayers 把天地图跑起来3.1 最小可运行示例先来一个能直接运行的版本用 CDN 引入 OpenLayers方便你在纯 HTML 文件里验证。!DOCTYPE html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / title加载天地图/title link relstylesheet hrefhttps://cdnjs.cloudflare.com/ajax/libs/ol3/.../ / style #map { width: 100%; height: 600px; } /style /head body div idmap/div script srchttps://cdnjs.cloudflare.com/ajax/libs/ol3/.../ol.js/script script const tk 你的密钥; // 天地图矢量底图服务地址域名与参数以官方服务页面最新文档为准 const vecUrl https://tiles.tianditu.gov.cn/vec_w/wmts?SERVICEWMTSREQUESTGetTileVERSION1.0.0LAYERvecSTYLEdefaultTILEMATRIXSETwFORMATtilesTILEMATRIX{z}TILEROW{y}TILECOL{x}tk tk; const map new ol.Map({ target: map, layers: [ new ol.layer.Tile({ source: new ol.source.XYZ({ url: vecUrl, wrapX: false }) }) ], view: new ol.View({ center: ol.proj.fromLonLat([104, 30]), zoom: 5 }) }); /script /body /html注意上面 CDN 链接里的版本号我故意没写全因为 OpenLayers 的 CDN 地址经常调整请自行替换为当前正式版本。代码里fromLonLat([104, 30])是对经纬度坐标做投影转换转换后才是 Web 墨卡托坐标地图中心会落在示例坐标附近。如果这一步顺利你会在页面里看到一张能拖动、能缩放的矢量底图。这个时刻很重要意味着“瓦片请求在哪里、HTML 结构里放什么”你已经掌握了。3.2 关键参数说明projection、tileGrid 与 wrapX第一个容易迷惑的参数是projection。在上面的 XY.Z 示例里我没有显式设置OpenLayers 默认认为 XYZ 瓦片基于 Web 墨卡托投影这也是绝大多数在线瓦片服务的默认规则。如果你的瓦片服务是经纬度坐标的c矩阵集那就要手动指定投影否则瓦片会贴在错误的位置上。第二个是tileGrid。严格讲WMTS 和 XYZ 的瓦片原点、网格规则不完全一样。天地图的 Web 墨卡托瓦片服务使用起来和 XYZ 兼容所以用ol.source.XYZ可以省去配置瓦片网格的麻烦。如果你后续接入别的 WMTS 服务就需要根据服务文档配置tileGrid的原点、分辨率列表和瓦片尺寸。第三个是wrapX。我习惯设为false意思是地图横向平移时不要无限循环包裹。有些底图源允许跨越 180 度经线循环显示但业务数据叠加时容易出现“明明在这个位置瓦片却从另一侧跑出来”的错觉。练习阶段关闭它减少干扰因素。关于图层类型建议只用ol.layer.Tile和ol.source.XYZ。没有特殊需求时不要嵌套TileWMS、WMTS这些更高层封装那是 WMTS 官方服务才需要的。3.3 从单层底图到完整地图叠加注记层矢量底图加载起来只是第一步。细看你会发现地图上没有任何地名、道路名纯色块加线条业务价值不大。天地图的服务里通常还有注记图层专门提供地名、水系名、道路名等文字信息。加载注记层的方式和底图几乎一样只是把LAYER参数从vec改成注记图层对应的名称服务地址换一套。代码结构上可以把两层都放进layers数组先放底图再放注记这样注记自然显示在底图上方。const cvaUrl https://tiles.tianditu.gov.cn/cva_w/wmts?SERVICEWMTSREQUESTGetTileVERSION1.0.0LAYERcvaSTYLEdefaultTILEMATRIXSETwFORMATtilesTILEMATRIX{z}TILEROW{y}TILECOL{x}tk tk; const baseLayer new ol.layer.Tile({ source: new ol.source.XYZ({ url: vecUrl, wrapX: false }) }); const annotLayer new ol.layer.Tile({ source: new ol.source.XYZ({ url: cvaUrl, wrapX: false }) }); const map new ol.Map({ target: map, layers: [baseLayer, annotLayer], view: new ol.View({ center: ol.proj.fromLonLat([104, 30]), zoom: 5 }) });影像底图、地形晕渲等其他图层也遵循同样的模式只要把服务资源和LAYER参数换掉即可。这一套逻辑吃透以后加任何瓦片图层都会很快。4. 运行时的三个麻烦坐标错位、灰格子、配额失效4.1 坐标错位瓦片级地图为何“漂移”症状表现地图能加载但业务点位的经纬度位置明显偏移比如点在路东底图显示在路西或者底图本身看起来整屏“平移”了一段。排查链路按顺序来。先确认业务数据的坐标系。如果是 GPS、测绘数据大概率是标准经纬度可以直接交给 OpenLayers 处理如果数据本身有加偏那就是数据侧问题和底图无关。然后检查瓦片服务使用的TILEMATRIXSET参数是否和你的 view 投影匹配。天地图通常用wWeb 墨卡托更省事。再检查 view 的 center不要写[104, 30]而要写ol.proj.fromLonLat([104, 30])这是新手最常见的错误点。有个反直觉的现象值得说有时地图中心显示正确但缩放到某一级后点位开始偏移。这种通常不是坐标算错而是瓦片层级对应的分辨率与业务数据比例尺不一致需要检查数据自身有没有经过投影转换。本质上Web 地图是“数据在不同坐标系之间不断转换”的过程。你在浏览器看到的坐标经过了一次从经纬度到投影坐标的换算。如果底图和数据用了不同基准那么只要目标投影一致转换一次就能对齐否则怎么调都是各偏各的。4.2 灰格子和模糊瓦片跨域与网络请求的锅症状表现地图区域出现成片的灰色网格页面放大时部分瓦片刷不出来有时过几秒又自己恢复了。不要直接怀疑天地图服务不稳定先打开开发者工具 Network 面板刷新页面过滤tiles或wmts请求看瓦片请求的响应状态。常见的三种情况。第一种是请求状态 403。大概率是tk参数无效或密钥和请求域名不匹配。检查密钥是否复制完整有没有多余空格开发者后台的应用配置是否允许当前域名。第二种是请求状态 200 但返回内容不是图片。按下响应预览可以看到有时返回的是 XML 错误提示这种现象通常和配额、参数格式有关注意看 XML 里的说明文字。第三种是浏览器直接拦截。如果是 HTTP/HTTPS 混合内容控制台会提示 blocked这种情况把瓦片地址改成 HTTPS 即可。如果页面部署在非 HTTPS 的内网环境也要检查瓦片服务是否允许从当前 origin 访问。这里有个偷懒技巧Network 面板里点一条瓦片请求直接在新标签页打开它的完整 URL。如果单独打开能显示图片说明问题出在页面侧的环境配置如果单独打开也报错直接检查 URL 参数和密钥。4.3 配额与鉴权异常key 失效不是玄学症状表现刚加载时地图正常滚动缩放一会儿后瓦片开始大面积空白控制台请求返回TILEOVERLIMIT之类的提示。这就是配额或并发限制被触发了。每个开发者密钥在个人练习阶段的每日请求量和每秒并发数是有限的地图连续拖动、快速缩放时一瞬间会发出大量瓦片请求很容易顶到上限。这不是代码 bug但决定了好习惯练习时不要写全屏自动轮播的图层刷新逻辑也不要在一个页面里重复初始化多个地图实例。如果只是做加载练习控制缩放频率即可。更规范的解法是中间加一层瓦片代理缓存把常用层级的瓦片缓存到本地服务器前端只请求自己的域名这样既隐藏了密钥也降低了源站配额压力。这个属于后续进阶内容现阶段先把密钥配置和日常使用规范做好。5. 从练习到工程模块把地图组件收进项目里5.1 模块化设计与配置管理现在你已经能把天地图加载到页面里了但代码还是“打开 HTML 就跑”的形态。如果放进真实项目建议立刻做两件事第一把所有可配置参数抽离出来第二把地图初始化封装成一个独立方法。比如可以建一个地图配置对象const mapConfig { tk: 你的密钥, layers: [ { name: vec, url: https://tiles.tianditu.gov.cn/vec_w/wmts?...tk 你的密钥, visible: true }, { name: cva, url: https://tiles.tianditu.gov.cn/cva_w/wmts?...tk 你的密钥, visible: true } ], view: { center: [104, 30], zoom: 5 } }; function initMap(target) { const layers mapConfig.layers.map(item ( new ol.layer.Tile({ source: new ol.source.XYZ({ url: item.url, wrapX: false }), visible: item.visible }) )); return new ol.Map({ target, layers, view: new ol.View({ center: ol.proj.fromLonLat(mapConfig.view.center), zoom: mapConfig.view.zoom }) }); }这样地图相关的 URL、层级、中心点都集中在配置里以后切换影像底图或调整初始视角不用改初始化逻辑。但在把tk写进前端代码这件事上我要提醒你前端代码里的密钥一定会暴露给用户任何人打开开发者工具都能看到。个人练习阶段无所谓正式项目建议通过后端接口代理瓦片请求把tk放在环境变量或服务端配置里。如果只是做内网可视化大屏还要评估是否需要做请求转发。5.2 面向后续练习的地图骨架加载天地图虽然只是一个小功能但它给你搭好了后续所有练习的地图骨架。有了这个骨架你可以继续做这些事叠加 GeoJSON 图层把业务点位渲染到地图上加矢量标注和弹窗做点位详情展示接入轨迹回放让地图动起来封装图层控制面板让用户切换底图和注记结合绘图交互在地图上画范围、量算面积。我自己的体会是做 WebGIS 第一步千万别贪多先把底图加载这件事弄透——坐标、密钥、瓦片协议、图层叠加——后面每加一个功能都是站在这个稳定底座上的自然延伸。等这章练习完全跑通你可以试着在浏览器控制台里执行一些地图方法获取当前中心点、缩放级别看看地图对象到底暴露了哪些能力这会比急着写复杂功能更有收益。最后分享一个实操小技巧调试时把view的初始 zoom 设小一点比如 3 或 4这样可以一次性请求大量瓦片快速判断图层叠加顺序对不对、注记层是否透明、请求是否超限。等确认无误再把视图调整到实际业务需要的缩放级别。
阅读完成 · 觉得有帮助?
咨询建站