天地图离线地图JS与CSS本地化:内网GIS项目断网白屏解决方案
简介这份资源面向需要在无外网环境下部署地图服务的前端与内网应用开发者提供天地图离线访问所需的 JavaScript 与 CSS 源码解决内网无法直连天地图在线接口、地图瓦片与控件无法加载的问题。压缩包共 5 个文件包含 4 个 js 与 1 个 css整体约 100KB其中 js 文件承担天地图 API 封装、请求地址替换、瓦片加载与坐标转换等逻辑调整css 文件负责图层控制、比例尺、指北针等界面元素的样式定制使离线状态下的交互体验尽量贴近在线效果。目前已有 2358 人学习下载说明该方案在受限网络场景中具有实际参考价值。读者可据此理解离线地图的改造思路掌握将在线请求指向本地资源的关键做法并依据自身内网环境调整服务器地址与资源路径快速搭建可用的离线地图服务。1. 天地图离线地图的 js 和 css为什么你的内网项目一断网就白屏做过内网 GIS 项目的人大概都遇到过这个场景开发机上地图好好的一部署到隔离环境页面直接白屏控制台一堆net::ERR_NAME_NOT_RESOLVED指向的全是t{0-7}.tianditu.gov.cn这类在线瓦片和 API 域名。问题不在你的代码而在于天地图的 JS API 和底图瓦片默认都是走公网的离线环境根本拿不到。这篇笔记讲的就是怎么把天地图这套东西真正搬到离线环境里跑起来——核心是两件事JS API 脚本本地化和CSS 样式本地化再配合瓦片离线化。适合做内网 GIS、涉密专网、工业现场大屏、无外网展厅这类项目的开发者。我会把目录结构、脚本改写、样式路径、瓦片加载这几块拆开讲参数和坑都标出来照着做能复现。2. 离线化到底要搬哪些文件JS、CSS 与瓦片的边界很多人以为「离线地图」就是把api.tianditu.gov.cn/api?v4.0这个脚本下载下来放本地就完事了结果一跑还是白屏。原因是天地图这套东西是三层依赖JS API 负责逻辑和控件CSS 负责控件样式和版权信息排版瓦片负责真正的地图图像。三层里任何一层还在走公网离线环境就会出问题。2.1 三层依赖分别是什么、能不能省先明确每一层的作用才知道哪些必须本地化。层级典型文件/资源作用能否省略JS APIapi?v4.0返回的主脚本初始化地图、控件、事件、坐标转换不能省核心逻辑CSSAPI 内部引用的样式文件缩放按钮、比例尺、版权条排版不能省省了控件错位瓦片t{0-7}.tianditu.gov.cn/...的 png地图底图图像不能省省了只有灰底附加资源图标、字体、光标控件图标、标注图标视情况缺了图标会裂JS API 主脚本里其实还内联了一部分逻辑并且会动态拼接瓦片 URL 和部分资源 URL。这就是为什么单纯把脚本存本地还不够——脚本内部仍然会去请求公网域名。所以离线化的本质是把脚本存本地 把脚本里写死的公网域名替换成本地路径或本地服务地址。2.2 目录结构怎么规划我一般会按下面这个结构放清晰且好维护offline-map/ ├── js/ │ ├── tianditu-api.js # 本地化的 API 主脚本 │ └── tianditu-api.js.bak # 原始备份方便对比 ├── css/ │ └── tianditu.css # 本地化样式 ├── tiles/ # 离线瓦片按 z/x/y 组织 │ └── {z}/{x}/{y}.png └── index.html瓦片目录的{z}/{x}/{y}是行业通用约定后面讲瓦片服务时会用到。JS 和 CSS 放同级目录方便 HTML 里相对路径引用也方便整体拷贝到内网。2.3 怎么把 API 脚本和样式抓下来在线环境下用浏览器开发者工具就能拿到。打开一个用了天地图 API 的页面切到 Network 面板筛选api和css把主脚本和样式文件分别另存。注意两点一是脚本可能是压缩过的单行别手动格式化后再用容易改坏二是样式文件可能不止一个API 会按需加载把 Network 里所有.css都过一遍。# 用 curl 抓主脚本在线环境执行仅作示例 curl -o tianditu-api.js https://api.tianditu.gov.cn/api?v4.0tk你的密钥 # 抓样式具体 URL 以 Network 面板实际请求为准 curl -o tianditu.css https://api.tianditu.gov.cn/xxx/tianditu.css这里tk是天地图的密钥参数离线环境虽然不联网校验但脚本内部逻辑仍会读取这个变量所以抓的时候带上本地引用时也保留。抓下来的脚本先备份一份.bak改坏了能回滚这是血泪经验。3. 把 JS API 改成本地引用路径替换与初始化改写文件抓下来只是第一步真正让离线跑通的是改写脚本内部的公网引用和调整 HTML 的初始化方式。这一章是核心做不对就是白屏。3.1 脚本内部的公网域名怎么找、怎么替用编辑器打开tianditu-api.js搜索这几个关键词tianditu.gov.cn、http://、https://、t0、t7。你会看到两类东西一类是瓦片 URL 模板一类是资源加载地址。瓦片 URL 模板通常长这样压缩后可能是一行// 原始脚本里类似这样的拼接逻辑 var tileUrl http://t i .tianditu.gov.cn/DataServer?Tvec_wx x y y l z tk tk;离线化时把它改成指向本地瓦片服务或本地目录// 改为本地瓦片服务假设本地起了个静态服务 var tileUrl /tiles/vec_w/ z / x / y .png;逻辑说明原来t0~t7是八个瓦片子域名做负载均衡离线环境没有这个必要直接指向本地一个路径即可。Tvec_w是图层类型参数vec是矢量底图、img是影像底图、cva是注记_w表示经纬度投影球面墨卡托是_c。这些参数决定了你请求的是哪套瓦片替换时图层类型要和本地瓦片目录对应上否则路径对不上就是 404。参数说明z是缩放级别x、y是瓦片行列号。本地目录必须严格按z/x/y组织顺序错了图就拼不出来。tk在纯离线瓦片场景下可以去掉因为本地服务不校验。3.2 HTML 里怎么引用本地脚本和样式在线时大家习惯这样写script srchttps://api.tianditu.gov.cn/api?v4.0tk你的密钥/script离线改成相对路径link relstylesheet href./css/tianditu.css script src./js/tianditu-api.js/script注意顺序样式在前脚本在后。因为脚本初始化时会去读样式里的类名做控件布局样式没加载完就初始化控件位置会错乱。这个顺序问题在在线环境不明显因为 CDN 快离线本地文件加载顺序敏感容易翻车。3.3 初始化代码要不要改大部分情况下初始化代码不用大改还是熟悉的写法// 初始化地图容器 id 为 mapDiv var map new T.Map(mapDiv); // 设置中心点和级别 map.centerAndZoom(new T.LngLat(116.397, 39.909), 12); // 添加缩放控件 map.addControl(new T.Control.Zoom());逻辑说明T.Map是天地图 API 的全局对象脚本本地化后这个对象依然存在所以初始化逻辑不变。centerAndZoom的第二个参数是缩放级别离线瓦片如果只切到 12 级这里设成 15 就会请求不存在的瓦片显示空白。离线瓦片的级别范围必须和初始化级别匹配这是最常见的坑之一。参数说明T.LngLat(经度, 纬度)注意是经度在前。addControl添加的控件依赖 CSS如果样式没本地化控件会显示成没有样式的裸按钮。提示改完脚本后先在浏览器里断网测试开发者工具 Network 面板勾选 Offline确认没有任何请求发往公网域名再部署到内网。这一步能提前暴露 90% 的路径问题。4. 离线瓦片的组织与本地服务让底图真正显示出来JS 和 CSS 本地化解决的是「地图框架能跑」瓦片离线化解决的是「地图有图」。这两件事经常被混为一谈其实完全独立。框架跑起来但瓦片 404你看到的就是一片灰底加控件。4.1 瓦片从哪来、怎么切离线瓦片的来源一般有两种一是提前用工具批量下载指定范围的瓦片二是用本地地图数据自己切。下载工具很多核心是设定范围、级别、图层类型三个参数。范围用经纬度框定级别决定清晰度和数据量图层类型要和前面脚本里改的T参数一致。数据量估算很关键瓦片数量随级别呈指数增长。一个城市范围1 到 10 级可能就几百张到 15 级就是几万张到 18 级能到几十万张。内网项目一般切到 15 到 16 级够用再高存储和加载都吃不消。4.2 本地瓦片服务怎么起最简单的做法是用静态文件服务把tiles目录暴露出去。开发阶段用 Python 一行命令就能起# 在 offline-map 目录下起一个静态服务端口 8080 python -m http.server 8080逻辑说明这样/tiles/vec_w/12/3372/1552.png就能直接访问到对应文件。生产环境一般用 Nginx 托管静态目录配置更稳、并发更好。# Nginx 配置片段把 /tiles/ 映射到本地瓦片目录 location /tiles/ { alias /data/offline-map/tiles/; expires 30d; # 瓦片不常变缓存久一点 add_header Cache-Control public; }参数说明alias后面必须带结尾斜杠和location的斜杠对应否则路径会拼错。expires 30d让浏览器缓存瓦片减少重复请求内网带宽也是资源。4.3 瓦片加载不出来怎么定位按这个顺序排查基本能覆盖所有情况浏览器直接访问一张瓦片 URL看是不是 404。404 说明路径或文件缺失。看 Network 面板里瓦片请求的 URL 结构和本地目录结构对比z/x/y顺序对不对。看初始化级别是否超出瓦片级别范围。看图层类型T参数和瓦片目录名是否一致。我遇到最多的是第 2 条脚本里拼接顺序是z/x/y但下载工具存成了y/x/z路径对不上全 404。这种问题不看 Network 面板根本发现不了。5. 离线化避坑清单五个让项目返工的细节这一章全是踩过的坑每条按「现象 → 原因 → 解决」写照着排能省不少时间。坑一断网后地图白屏控制台报域名解析失败。现象是页面加载后一片白控制台全是ERR_NAME_NOT_RESOLVED。原因是脚本或样式里还有残留的公网域名没替换干净尤其是脚本里动态拼接的资源地址。解决方法是全局搜索tianditu.gov.cn和http逐个确认替换改完断网测试。坑二控件显示正常但位置错乱、按钮变形。现象是缩放按钮挤在一起或者跑到地图外面。原因是 CSS 没本地化或者样式加载顺序在脚本之后。解决方法是确认link在script之前且样式文件完整。坑三地图有灰底但没图像瓦片请求 404。现象是框架正常、控件正常就是没底图。原因是瓦片路径结构不匹配或级别超范围。解决方法是浏览器直接访问瓦片 URL 验证对比目录结构和请求 URL。坑四本地能跑拷到内网另一台机器就挂。现象是开发机正常部署机白屏。原因是用了绝对路径如/js/tianditu-api.js而部署路径不同或者瓦片服务没起。解决方法是统一用相对路径部署时确认静态服务已启动。坑五地图能显示但拖动卡顿、加载慢。现象是底图出来了但操作不流畅。原因是瓦片没做缓存每次拖动都重新请求或者瓦片文件太大没压缩。解决方法是在 Nginx 加缓存头瓦片统一转成压缩后的 png 或 webp。注意离线化完成后一定要在完全断网的环境里完整走一遍所有功能包括缩放、拖动、图层切换、标注点击。只在开发机断网测试不够因为开发机可能有缓存。6. 进阶用配置化方式管理多套离线地图与版本切换前面讲的是单套离线地图怎么跑通。实际项目里经常要面对多套底图矢量、影像、注记和多个版本不同年份、不同精度如果每次都改脚本维护成本会爆炸。我一般会把瓦片路径和图层配置抽出来做成一个配置对象脚本只读配置。// 离线地图配置把图层和路径抽出来方便切换 var offlineConfig { // 当前使用的底图类型 baseLayer: vec_w, // 各图层的本地路径模板 layerPaths: { vec_w: /tiles/vec_w/{z}/{x}/{y}.png, // 矢量底图 img_w: /tiles/img_w/{z}/{x}/{y}.png, // 影像底图 cva_w: /tiles/cva_w/{z}/{x}/{y}.png // 注记 }, // 离线瓦片的级别范围超出会 404 minZoom: 1, maxZoom: 16 }; // 根据配置生成瓦片 URL function buildTileUrl(layer, z, x, y) { // 级别越界直接返回空避免无效请求 if (z offlineConfig.minZoom || z offlineConfig.maxZoom) { return ; } return offlineConfig.layerPaths[layer] .replace({z}, z) .replace({x}, x) .replace({y}, y); }逻辑说明把路径模板和级别范围集中管理切换底图只改baseLayer加新图层只加一条layerPaths。buildTileUrl里做了级别越界判断避免请求不存在的瓦片减少无谓的 404。这套写法在需要频繁切换底图的大屏项目里特别省事。参数说明{z}/{x}/{y}是占位符替换时注意顺序和本地目录一致。minZoom、maxZoom要和实际切图范围严格对应宁可写小一点越界返回空比返回 404 更干净。验证离线化是否彻底我有个习惯把项目拷到一台从来没连过外网的机器上或者用防火墙把公网出口全封了完整跑一遍。能跑通才算真离线。另外浏览器缓存会骗人测试前记得清缓存或者用无痕模式否则你以为的「离线可用」可能只是缓存里的旧资源在撑着。这套方案值不值得做取决于你的场景只要项目部署环境不能稳定访问公网离线化就是必选项而且越早做越好别等到部署前一天才发现白屏。希望帮到你。本文还有配套的精品资源点击获取