简介天地图离线API完整包是一套基于天地图官方API v4.0构建的离线地图服务资源面向需要在无网络环境下实现地图展示、缩放、轨迹移动等功能的开发者尤其适用于户外探险、应急响应和内网GIS项目。压缩包共2000个文件以1991张PNG格式瓦片地图为主辅以7个JavaScript脚本、1个HTML入口和1个CSS样式表整体体积仅6.02MB其中JS文件负责地图初始化与轨迹动画HTML提供页面载体CSS控制视觉样式。已有2999人学习下载。资源内含完整离线瓦片目录配合CarTrack.js可模拟车辆轨迹移动D3SvgOverlay.js实现SVG地理叠加开发者可直接部署到本地服务器运行同时可借助military.js、service.js等模块快速扩展态势标绘与地图服务能力适用于离线场景下的深度二次开发。 干GIS开发的同行应该都有过这种经历项目做到一半甲方突然说我们这里没有外网地图还能不能显示或者演示那天网络一卡整个系统跟着白屏太丢人了。我这次做的天地图离线API完整包就是为了解决这类问题——把天地图官网那一套操作能力包括轨迹移动、坐标拾取、图层切换这些全部搬到本地断网也能跑。这套离线方案的定位很明确不是简单地把瓦片下载下来拼个底图而是把官方API的交互能力也一起离线化。适合这几类场景内网部署的政务GIS、野外无网作业的移动端、对外演示时不想冒网络风险的项目。如果你也正在研究天地图离线化这篇文章能帮你省掉不少试错时间。1. 在线API的痛点清单为什么非要做离线方案先说结论天地图的在线API本身做得不差瓦片服务稳定、坐标系规范、接口文档也算齐全。但在真实项目里它有几个绕不开的硬伤。第一是网络依赖问题。很多GIS项目部署在政务内网、涉密机房或偏远作业现场这些环境根本没有公网访问权限。就算有外网项目现场的网络质量也完全不可控——我在一个山区水利巡检项目里就遇到过4G信号时有时无地图瓦片加载到一半卡住整个页面白屏领导在旁边看着那种尴尬经历过的人都懂。第二是请求频率限制。天地图的在线瓦片接口基于token鉴权虽然开放给个人和企业使用但服务稳定性受官方调度策略影响。项目上线后如果并发过高或者某个时段请求量激增瓦片加载就会变慢甚至被限流。做对外演示的时候这种不可控就是最大的风险。第三是操作能力不全在API里。官网除了基础的地图浏览还有坐标拾取、轨迹回放、测距、标绘这些交互功能。你要是只用官方JavaScript API会发现轨迹移动这类功能需要自己写不少代码而坐标系转换、范围边界获取这些操作更是要到处找资料拼凑。我决定做离线方案的触发点是一个应急管理项目需要在断网环境下展示人员巡检轨迹。当时甲方要求不仅要能看到地图底图还要能播放轨迹、拾取坐标、切换影像和矢量图层。这就意味着离线包不能只缓存瓦片还得把交互逻辑一并本地化。所以这套东西的架构从一开始就是按照半成品应用框架来设计的而不是一个简单的瓦片下载器。2. 离线API包的架构设计瓦片库、本地服务与接口封装2.1 瓦片下载先搞清楚要哪些图层和级别天地图的瓦片服务分几个类型缩写分别是vec矢量底图、cva矢量注记、img影像底图、cia影像注记。做离线包之前先问自己三个问题项目需要哪些图层只做底图展示就下载veccva需要卫星影像就下载imgcia两个都要就全下。需要哪些级别普通城市级应用1到15级够用县域级项目建议下到17级涉及重点区域精细展示的下到18级。覆盖范围多大确定好最小外接矩形避免下载无用瓦片占用磁盘空间。下载工具可以自己写脚本也可以用市面上的瓦片下载器。自己写的思路不复杂根据范围、级别、比例尺公式反算出每个级别下的瓦片行列号范围然后按xyz路径拼接URL批量请求。关键参数是比例尺与级别的关系天地图使用Web墨卡托投影全球剖分规则与Google Maps一致第level级的瓦片数量是2的level次方。只要确定左上角原点坐标-20037508.3427892, 20037508.3427892和单张瓦片尺寸256×256像素就能精确算出某个经纬度点所在的瓦片行列号。2.2 本地瓦片服务一个轻量HTTP服务器就够瓦片下好之后需要一个本地服务来提供瓦片访问。最省事的方案是Nginx直接配置一个静态文件目录把瓦片按{layer}/{z}/{x}/{y}.jpg的路径组织好就能用HTTP访问了。我也试过用Node.js写个几十行的静态服务器效果一样但Nginx在并发和缓存上更有优势。需要注意的一点瓦片文件名的大小写和路径分隔符必须统一否则Windows部署和Linux部署会踩到文件系统大小写敏感的坑。我习惯统一用小写字母和斜杠分隔部署的时候省心很多。2.3 前端API封装让离线包用起来像官方API瓦片只是底子交互逻辑才是离线包的核心。我按照官方天地图API的调用习惯封装了一个本地版的OfflineMap对象提供地图初始化、图层切换、添加标记、绘制轨迹、坐标拾取、测距等常用方法。这样业务代码里只需要改一个引用地址就能从在线API平滑切到离线API。初始化逻辑大致是const map new OfflineMap({ container: map, baseUrl: http://localhost:8080/tiles, layers: [vec, cva], center: [116.391, 39.907], zoom: 12, minZoom: 3, maxZoom: 17 });这里的核心是把瓦片地址模板传给底层渲染引擎我用的是Leaflet也兼容MapLibre GL让它在切片请求时自动拼接本地URL。图层切换的原理就是动态替换当前底图层把vec换成img同时把cva切换成cia保持注记层级一致。2.4 坐标系问题CGCS2000与Web墨卡托的取舍天地图官方提供的瓦片服务底层是基于CGCS2000坐标系构建的但在Web端默认输出的是Web墨卡托投影EPSG:3857的切片。这就带来一个常见的坑如果你在ArcMap里直接加载天地图在线服务软件默认的坐标系可能和瓦片的实际投影对不上出现位置偏移。离线包内部统一采用EPSG:3857做瓦片渲染但对外暴露坐标拾取接口时会同时返回CGCS2000经纬度坐标EPSG:4490和投影坐标方便业务系统对接。转换公式并不复杂墨卡托的X方向就是经度等比例展开Y方向用R * ln(tan(π/4 φ/2))计算扒开官方工具源码就能拿到现成实现。这个内部统一、外部兼容的思路避免了项目组里不同成员各查各的坐标系转换资料、最后对不上数的问题。3. 轨迹移动的核心实现坐标插值、动画驱动与视口联动3.1 轨迹数据准备从设备记录到可播放的GeoJSON轨迹移动说白了就是把一段历史轨迹在地图上重放出来。数据来源通常是GPS设备、手机定位或业务系统的工单记录字段大同小异经度、纬度、时间戳可能还有速度、方向角。我惯用的格式是GeoJSON的FeatureCollection每个Feature代表一个轨迹点Properties里带上时间和速度。如果原始数据的时间间隔不均匀——比如每5秒一个点但中间有几分钟的空档——播放会出现明显的停顿感。处理办法是对轨迹做等时间间隔重采样用线性插值补齐缺失的点。时间间隔一般取1秒这样播放起来顺滑数据量也不会爆炸。3.2 动画驱动requestAnimationFrame替代setInterval轨迹播放的经典实现是用setInterval每隔一段时间更新一次标记点位置但这种方式在页面卡顿或切后台时会积累延迟播放速度越来越不准。我改成了requestAnimationFrame驱动每帧根据时间戳计算当前应该插值到的位置再更新标记。核心代码逻辑function animate(timestamp) { if (!startTime) startTime timestamp; const elapsed (timestamp - startTime) / 1000; const totalDuration getTotalDuration(trackData); const progress Math.min(elapsed / totalDuration, 1); const currentPos interpolatePoint(trackData, progress); marker.setLatLng(currentPos); map.panTo(currentPos, { animate: false }); if (progress 1) { requestAnimationFrame(animate); } } requestAnimationFrame(animate);这里有个关键设计interpolatePoint函数根据当前进度算出目标位置如果两个轨迹点之间距离较大直接线性插值会出现标记穿楼的效果看起来不自然。我的解决办法是在计算经纬度插值的同时根据两个点之间的实际地理距离和高度变化做一个简单的分段插值——距离近的点直接线性距离远的点按折线路径多点插值模拟出转弯的效果。配合轨迹线本身的渐变色起点绿色、终点红色整个回放过程的视觉体验会好很多。3.3 标记方向角让车辆图标看向正确方向轨迹播放时如果标记是一辆车的图标方向不对会很出戏。计算方向角其实用简单的三角函数就能搞定取当前点和前后两个点的经纬度差值用Math.atan2(latDiff, lngDiff)算出角度再转成CSS的rotate值即可。需要注意坐标系里Y轴朝上但CSS旋转角度是顺时针方向的中间要做一个角度换算。这块我当初偷懒没做结果演示时车标一直横着走被甲方当场指出来后来老老实实补上了。3.4 离线条件下的轨迹匹配没有路网数据怎么办在线环境下轨迹可以用地图匹配Map Matching算法吸附到道路上效果很漂亮。但离线环境下没有路网数据做不了真正的匹配。我的折中方案是把轨迹按照空间范围裁剪只看缓冲区内的道路信息如果本地有的话没有就直接播放原始轨迹。其实对大多数巡检、巡逻类业务来说原始GPS轨迹已经足够说明问题吸附到道路只是锦上添花不值得为此引入一套复杂算法增加落地难度。4. 与ArcMap、QGIS的联调细节与坐标拾取校准4.1 ArcMap加载天地图影像WMTS服务地址与图层拼接ArcMap里加载天地图正统做法是使用WMTS服务。天地图官方提供了一套符合OGC标准的WMTS接口你只需要在ArcMap里添加WMTS服务器填入服务地址就能一层层展开图层列表把影像底图和影像注记拖进去。要注意的是ArcMap的缓存机制有时会记住第一次连接时的图层状态如果服务端参数变了需要清除缓存重新加载。如果不想走WMTS也可以直接拿离线包里的瓦片目录做一个ArcGIS Online风格的切片服务但这个方案配置量更大只推荐给需要C/S架构离线的场景。B/S架构用Leaflet离线包C/S架构用ArcMap直连——这两条路我都跑通过目前没发现明显副作用。4.2 QGIS加载天地图插件与TMS配置对比QGIS加载天地图要灵活得多有两个常用路径。一条是用专门的天地图插件图形化界面里勾选图层类型插件自动拼接瓦片地址另一条是手动添加TMS图层在XYZ Tiles里直接填http://localhost:8080/tiles/vec/{z}/{x}/{y}.jpg这种本地地址需要注意的是QGIS识别TMS的XYZ顺序与天地图默认路径一致不需要额外调整方向。我用QGIS主要是做数据校对把离线包的底图数据和业务shapefile叠加检查坐标偏移。遇到偏移时先确认项目坐标系和图层坐标系是否一致——QGIS默认的CRS是EPSG:4326而天地图瓦片是EPSG:3857两者叠加时QGIS会自动实时重投影但如果数据本身带有错误的坐标系定义就会出现看着挺对、导出来偏移几百米的诡异问题。解决办法是在导入时明确指定源文件的坐标系别让软件去猜。4.3 坐标拾取离线环境下的经纬度查询与边界JSON在线版的天地图有个坐标拾取工具点击地图任意位置就能显示经纬度。离线包必须复刻这个功能因为很多业务操作比如标注点位、圈定范围、检查地块边界都以坐标拾取为起点。我的实现方式是在Leaflet的click事件里用map.mouseEventToLatLng取出经纬度然后通过坐标转换方法输出成CGCS2000和Web墨卡托两种格式。顺便把当前视野中心坐标、缩放级别一并展示出来做成一个悬浮信息面板比官方的还实用。另一个热词是天地图滨州市影视图json地图边界这本质上是行政区划边界的GeoJSON数据。离线包把常用行政区划边界预先存成JSON文件加载时按名称检索。边界数据精度是个敏感点我一般优先使用公开的国界和省界数据县市级边界如果有官方来源就用官方的没有就提醒使用者确认数据合规性不要在图上乱画边界线。5. 踩坑记录与调优建议缓存一致性、并发拉瓦与内存占用5.1 瓦片花屏与缓存不一致版本号是关键离线环境下的花屏大多数不是网络问题而是瓦片版本新旧混用。天地图的影像数据会不定期更新你在不同时间段下载的瓦片同一区域可能一个是老版本一个是新版本拼在一起就会出现接边处色彩跳变。治本的办法是每次全量更新时彻底清空瓦片目录再重新下载不要增量覆盖。我经历过一次增量覆盖导致的密集居民区屋顶颜色深浅不一排查了两天才找到原因纯属浪费时间。5.2 并发拉瓦的限速别把自己的服务打死下载瓦片时如果脚本不做并发控制一口气开50个线程去请求在线服务大概率会被限流甚至封IP。我建议并发控制在5~8个线程每下载一个瓦片间隔50到100毫秒整体速度虽然慢一些但稳定。按这个速度下载一个中等城市15级的全部瓦片大约几小时能完成完全在接受范围内。5.3 前端内存占用轨迹点太多会卡轨迹数据量过大的时候——比如连续跑了一天的设备每秒一个点接近9万个点——一次全加载进Leaflet会非常卡。我的做法是分级抽稀播放轨迹时按当前缩放级别动态抽稀显示比如zoom小于10时每隔20个点取一个zoom大于15时全部显示。抽稀算法不用多复杂Douglas-Peucker就够了500行代码不到效果明显。5.4 热词里的QGIS插件版本坑热词里有一条qgis4.2.1天地图插件使用。我实测过QGIS不同小版本对插件API的兼容性差异很大同一个插件在3.28上运行正常到3.32就可能报canvas相关错误。遇到这类问题优先看插件仓库有没有对应版本的发布包别迷信最新版一定最好。如果插件死活装不上退而求其次用TMS手动加载效果一样。6. 离线API包的验收清单与扩展方向按照我的经验离线包做完之后至少要走一遍这个验收清单才敢往现场拿完全断网状态下地图能够加载全部底图和注记切换影像/矢量无报错。坐标拾取返回的经纬度与在线版工具在同一区域比对偏差在可接受范围内。轨迹播放具备开始、暂停、停止、倍速控制标记方向角正确轨迹线与标记同步。在ArcMap和QGIS中能直接加载同一份瓦片数据坐标系信息正确。边界JSON数据能正常加载且与底图套合没有明显偏移。这套离线包做完之后后续扩展方向也不少比如接入本地路径规划用离线路网数据跑Dijkstra、加一个本地地名搜索缓存、把轨迹数据导出成GPX/KML等标准格式。我目前已经把它用在了两个内网项目和一次野外应急演练里整体表现稳定至少再也不用看网络的脸色了。最后分享一个小技巧瓦片下载时把每个图层级别的瓦片数量统计出来做成一个清单文件。这玩意儿在项目交付时很管用——运维人员靠它快速估算磁盘占用也方便排查哪个级别下漏了瓦片的疑难杂症。本文还有配套的精品资源点击获取