简介:天地图离线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(影像注记)。做离线包之前,先问自己三个问题:
- 项目需要哪些图层?只做底图展示就下载vec+cva,需要卫星影像就下载img+cia,两个都要就全下。
- 需要哪些级别?普通城市级应用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等标准格式。我目前已经把它用在了两个内网项目和一次野外应急演练里,整体表现稳定,至少再也不用看网络的脸色了。
最后分享一个小技巧:瓦片下载时,把每个图层级别的瓦片数量统计出来,做成一个清单文件。这玩意儿在项目交付时很管用——运维人员靠它快速估算磁盘占用,也方便排查"哪个级别下漏了瓦片"的疑难杂症。
本文还有配套的精品资源,点击获取