Cesium 离线地形安装全攻略:从 DEM 切片到 terrainProvider 配置 简介这份资源面向需要在无网络环境下展示三维地形的Cesium开发者与GIS技术人员提供将DEM地形tif文件转换为Cesium可加载离线terrain瓦片的完整工具链与操作说明。压缩包共11个文件约45.9MB包含msi与exe安装程序、Python脚本、pdf使用说明及json、terrain等配置与数据文件覆盖Python 2.7、GDAL、PIL、NumPy等地理处理依赖的安装包以及坐标转换与瓦片生成脚本另附示例DEM数据与生成结果目录便于对照验证。已有3092人学习下载。通过这份资料读者可搭建起从DEM数据检查、SRTM格式瓦片生成到坐标系转换的完整流程理解各工具在转换链路中的作用并借助使用说明排查路径设置、坐标匹配、GDAL报错等常见问题最终把本地高程数据接入Cesium地形提供者实现离线地形渲染。1. 从一次地形加载失败说起Cesium 离线 terrain 到底该怎么装去年帮某实验室做一套内网环境的三维地理系统Cesium 底图跑得好好的一切到地形就白茫茫一片控制台报Failed to obtain terrain tile。排查了一下午才发现问题不在 Cesium 本身而是离线 terrain 数据压根没生成对——切片层级、瓦片格式、layer.json里的available数组任何一处对不上地形就加载不出来。这套离线地形方案在涉密内网、离线演示、教学环境里非常常见但它的安装链路比装一个 npm 包长得多要准备 DEM 原始数据、要装切片工具、要生成瓦片金字塔、要配layer.json、最后还要在 Cesium 里改terrainProvider。装不好不是报错就是地形塌陷新手很容易卡在“工具装完了但数据出不来”这一步。这篇笔记就把我踩过的坑按顺序捋一遍从环境准备到最终验证照着走能跑通。2. 环境准备与工具选型DEM 数据、切片程序和运行依赖2.1 先搞清楚离线 terrain 的数据链路Cesium 的离线地形不是一张图片而是一套按层级切好的瓦片集合通常采用heightmap或quantized-mesh两种格式。heightmap格式老一些兼容性好瓦片是 PNGCesium 直接能读quantized-mesh是现在的主流瓦片是.terrain二进制文件体积小、精度高但生成工具链要求更严。我一般推荐quantized-mesh因为 Cesium 官方从 1.40 之后对它的支持最完整加载性能也明显更好。整条链路是这样的原始 DEM通常是 GeoTIFF→ 切片工具生成瓦片目录 → 生成layer.json描述文件 → Cesium 通过CesiumTerrainProvider指向这个目录。任何一环版本不匹配都会导致地形加载失败。所以环境准备阶段重点不是装多少东西而是把版本对齐。2.2 工具版本对照表下面这张表是我在多个环境里验证过的组合Windows 和 Linux 都适用。注意 Python 版本很关键很多切片工具对 Python 3.9 以上支持不好。组件推荐版本作用备注Python3.8.x运行切片脚本3.9 部分库编译失败GDAL3.4.x读取 GeoTIFF DEM需与 Python 绑定cesium-terrain-builder0.4.1生成 quantized-mesh 瓦片需编译Cesium1.95前端加载低版本不支持 quantized-meshNode.js16.x本地起静态服务仅验证用提示如果你只是做heightmap格式可以用cesium-terrain-builder的简化分支但quantized-mesh必须用完整版编译时依赖zlib和libpng。2.3 安装 Python 与 GDAL 绑定先确认 Python 版本别用系统自带的。我习惯用pyenv或直接装 3.8 的独立包。装完 Python 后GDAL 的 Python 绑定是第一个坑直接pip install gdal大概率失败因为版本要和系统 GDAL 库对应。# 以 Ubuntu 为例先装系统 GDAL 库 sudo apt-get install gdal-bin libgdal-dev # 查看系统 GDAL 版本 gdal-config --version # 假设输出 3.4.1则安装对应 Python 绑定 pip install GDAL3.4.1这段命令的逻辑是系统库提供底层读写能力Python 绑定只是接口层版本必须一致。gdal-config --version输出的版本号就是你要pip install的版本。如果报numpy相关错误先pip install numpy再重试。Windows 下建议用OSGeo4W安装包勾选 GDAL 和 Python 绑定省去编译麻烦。2.4 编译安装 cesium-terrain-builder这个工具是 C 写的需要cmake和make。源码编译是绕不过去的但步骤不复杂。git clone https://github.com/geo-data/cesium-terrain-builder.git cd cesium-terrain-builder mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease make -j4 sudo make install-DCMAKE_BUILD_TYPERelease开启优化切片速度会快不少-j4按 CPU 核数调整。编译完成后ctb-tile命令应该能在终端直接调用。如果提示找不到libgdal检查cmake输出里 GDAL 的路径是否正确必要时用-DGDAL_INCLUDE_DIR手动指定。3. 生成离线地形瓦片从 DEM 到 layer.json 的完整操作3.1 DEM 数据预处理投影与裁剪原始 DEM 如果是地理坐标系WGS84切片工具通常能直接处理但如果是投影坐标系如 UTM必须先转回 WGS84否则瓦片层级会错乱。我一般用gdalwarp做这一步。# 将 UTM 投影的 DEM 转为 WGS84 gdalwarp -t_srs EPSG:4326 input_utm.tif dem_wgs84.tif # 裁剪到目标区域减少切片量 gdalwarp -te 100.0 20.0 120.0 40.0 dem_wgs84.tif dem_clip.tif-t_srs EPSG:4326指定输出为 WGS84 经纬度-te是裁剪范围按你的实际区域改。裁剪这步很多人跳过结果切了几十万块瓦片磁盘直接爆掉。先裁再切能省一半以上时间。3.2 执行切片ctb-tile 参数详解切片命令本身不长但参数决定成败。# 生成 quantized-mesh 瓦片层级 0 到 12 ctb-tile -f Mesh -C -N -o ./terrain_tiles dem_clip.tif # 生成 layer.json ctb-tile -f Mesh -C -N -l -o ./terrain_tiles dem_clip.tif-f Mesh指定输出 quantized-mesh 格式-C表示生成layer.json-N表示不生成法线法线会增大体积除非需要光照-l是只生成layer.json不重复切片。-o是输出目录。执行完你会看到terrain_tiles下出现0/、1/等层级目录每个目录里是.terrain文件。注意层级范围默认根据 DEM 分辨率自动算但你可以用-z指定最大层级。层级越高瓦片越多12 层大概对应 10 米精度够用就别往上升。3.3 layer.json 的关键字段检查layer.json是 Cesium 加载地形的入口里面几个字段必须对。{ tilejson: 2.1.0, format: quantized-mesh-1.0, version: 1.0.0, scheme: tms, tiles: [{z}/{x}/{y}.terrain], projection: EPSG:4326, bounds: [100.0, 20.0, 120.0, 40.0], available: [[0,0,0], [1,0,0], [1,1,0], ...] }format必须是quantized-mesh-1.0写错 Cesium 直接不认scheme是tms不是xyzavailable数组列出所有存在的瓦片坐标如果这个数组和实际文件对不上Cesium 会请求不存在的瓦片然后报 404。我遇到过available只写了前几层的情况地形只加载了一部分排查了半天。3.4 本地验证瓦片是否可访问切片完别急着往 Cesium 里塞先用本地服务验证一下。# Python 起一个静态服务 cd terrain_tiles python -m http.server 8080 # 浏览器访问 # http://localhost:8080/layer.json # http://localhost:8080/0/0/0.terrain如果layer.json能打开.terrain文件能下载浏览器可能提示下载而不是显示说明瓦片本身没问题。这一步能排除掉一半的“Cesium 加载失败”问题——很多时候是文件路径或服务配置错了不是数据错了。4. Cesium 端配置与避坑terrainProvider 接入和常见报错4.1 在 Cesium 中指向离线地形Cesium 加载离线地形只需要改一个地方terrainProvider。// 创建 CesiumTerrainProvider指向本地服务 const terrainProvider new Cesium.CesiumTerrainProvider({ url: http://localhost:8080, requestVertexNormals: false, // 与切片时的 -N 对应 requestWaterMask: false // 没有水面掩膜就关掉 }); // 应用到 viewer const viewer new Cesium.Viewer(cesiumContainer, { terrainProvider: terrainProvider });url指向layer.json所在的根目录不要带layer.json本身requestVertexNormals如果切片时用了-N不生成法线这里必须false否则 Cesium 会请求法线数据然后失败requestWaterMask同理没有水面数据就关掉。这两个参数是新手最容易忽略的报错信息又不直观往往表现为地形加载一半就停。4.2 跨域与路径问题如果 Cesium 页面和地形服务不在同一个端口浏览器会拦跨域请求。本地验证时最简单的办法是用同一个服务托管 Cesium 页面和瓦片或者给静态服务加 CORS 头。# 用 Python 起服务时加 CORS 支持 python -c import http.server, socketserver class CORSHandler(http.server.SimpleHTTPRequestHandler): def end_headers(self): self.send_header(Access-Control-Allow-Origin, *) super().end_headers() socketserver.TCPServer((, 8080), CORSHandler).serve_forever() 这段脚本在响应头里加了Access-Control-Allow-Origin: *本地调试够用。生产环境别用*按实际域名配。4.3 避坑记录五个让我加班的地形加载问题现象一控制台报Failed to obtain terrain tile地形完全不显示。原因layer.json里的format字段写成了heightmap-1.0但实际瓦片是.terrain二进制。 解决确认切片格式和format字段一致quantized-mesh对应quantized-mesh-1.0。现象二地形只加载了低层级放大后变空白。原因available数组只包含了低层级瓦片坐标高层级没写进去。 解决重新生成layer.json确保available覆盖所有实际存在的瓦片。可以用脚本遍历目录自动生成。现象三地形显示但高度明显不对像被压扁了。原因DEM 的高程单位是英尺但 Cesium 按米解析。 解决切片前用gdalwarp -s_srs或gdal_translate -scale把高程转成米或者在layer.json里加heightOffset校正。现象四Cesium 报Request has failed但浏览器直接访问瓦片 URL 能下载。原因Cesium 请求时带了Accept: application/vnd.quantized-mesh头静态服务返回的 MIME 类型不对。 解决给.terrain文件配置正确的 MIME 类型application/vnd.quantized-mesh或者用支持自定义 MIME 的服务。现象五切片过程内存溢出机器卡死。原因ctb-tile默认一次性读入整个 DEM大文件直接撑爆内存。 解决先用gdalwarp裁剪到最小必要范围或者用-z限制最大层级减少瓦片数量。5. 装完怎么验证跑通三层检查法和一个习惯装完不代表跑通我一般走三层检查从数据到前端逐层确认。第一层文件层。进terrain_tiles目录确认layer.json存在且available数组长度和实际瓦片数量对得上。用find . -name *.terrain | wc -l数一下瓦片总数再和layer.json里available的条目数对比差太多就是生成有问题。第二层服务层。起本地服务后用curl模拟 Cesium 的请求头访问一个瓦片。curl -H Accept: application/vnd.quantized-mesh -I http://localhost:8080/0/0/0.terrain看返回状态是不是200Content-Type是不是application/vnd.quantized-mesh。如果是404说明路径或available有问题如果是200但类型不对Cesium 可能解析失败。第三层渲染层。打开 Cesium 页面把相机飞到地形区域打开浏览器开发者工具的 Network 面板过滤.terrain请求。正常情况应该看到一系列200响应层级随缩放递增。如果看到大量404或请求中断回到第二层查。最后说一个习惯每次生成完瓦片我都会先用ctb-tile自带的ctb-info工具检查一下瓦片元数据确认层级范围和投影信息。这个命令很多人不知道但它能在切片阶段就暴露问题比等到 Cesium 里报错再回头查省事得多。从那以后我每次部署离线地形都强制走一遍“文件数 → curl 头 → Network 面板”这三步再也没出现过地形加载到一半崩掉的情况。希望帮到你。本文还有配套的精品资源点击获取