Cesium TerrainProvider、CTB 编译与地形瓦片发布完整技术文档
本文系统梳理了 Cesium 地形数据从 DEM 预处理、CTB 生成 quantized-mesh 瓦片、Web 服务发布到前端加载与问题排查的完整技术流程。
适用场景:CesiumJS 地形加载、Cesium Terrain Builder(CTB)编译、Quantized Mesh 地形瓦片生成与发布。
重点内容:TerrainProvider、地形数据格式、Windows 下 zlib / GDAL / CTB 编译、.terrain瓦片生成、layer.json、Nginx / Tomcat 发布方式、gzip 与非 gzip 数据发布差异。
1. 文档目标#
本文整理 Cesium 地形服务的完整流程:
- Cesium 中 TerrainProvider 的基本类型;
- Cesium 支持的地形数据格式;
- CesiumTerrainProvider 的使用方式;
- Cesium Terrain Builder(CTB)的作用;
- Windows 下 zlib、GDAL、CTB 的编译思路;
- 使用
ctb-tile生成 Quantized Mesh 地形瓦片; - 生成
layer.json; - 使用 Nginx / Tomcat 发布地形瓦片;
- gzip terrain 与非 gzip terrain 的发布差异;
- 常见问题与排查方法。
2. Cesium 中的 TerrainProvider#
Cesium 使用 TerrainProvider 抽象加载地形数据。不同 Provider 对应不同的数据来源、接口形式和瓦片组织方式。
2.1 CesiumTerrainProvider#
CesiumTerrainProvider 用于访问 Cesium terrain 格式的地形服务。Cesium 官方文档说明,它支持 Quantized Mesh 与 Height Map 这两类 Cesium terrain format。
jsconst terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl( "http://localhost:8081/terrain" ); const viewer = new Cesium.Viewer("cesiumContainer", { terrainProvider });
旧版本常见写法:
jsconst viewer = new Cesium.Viewer("cesiumContainer"); const terrainProvider = new Cesium.CesiumTerrainProvider({ url: "http://localhost:8081/terrain" }); viewer.terrainProvider = terrainProvider;
2.2 ArcGisImageServerTerrainProvider#
ArcGisImageServerTerrainProvider 用于从 Esri ArcGIS Image Server 的高度图服务中生成 Cesium 可用的地形数据,适合已有 ArcGIS 影像服务体系的项目。
2.3 VRTheWorldTerrainProvider#
VRTheWorldTerrainProvider 用于访问 VR-TheWorld 服务中的高度图地形数据,现代项目中使用较少。
2.4 EllipsoidTerrainProvider#
EllipsoidTerrainProvider 是 Cesium 默认地形 Provider,表示一个光滑椭球面,没有真实地形起伏,高度接近 0。
3. Cesium 支持的主要地形格式#
Cesium terrain 常见格式主要有两类:
- Heightmap
- Quantized Mesh
两者文件后缀通常都是 .terrain,但内部结构不同。
3.1 Heightmap Terrain#
Heightmap 使用规则格网存储高程。特点:
- 结构简单;
- 数据组织直观;
- 适合基础地形;
- 在 Cesium 中通常封装为
HeightmapTerrainData。
典型 URL:
texthttp://example.com/terrain/{z}/{x}/{y}.terrain
3.2 Quantized Mesh Terrain#
Quantized Mesh 是 Cesium 团队提出的地形网格格式,使用三角网表达地形,并对顶点数据进行量化。
特点:
- 使用三角网表达地形;
- 顶点属性经过量化;
- 支持 vertex normals、水面 mask、metadata 等扩展;
- 在 Cesium 中通常封装为
QuantizedMeshTerrainData。
Cesium 官方 Quantized Mesh 规范说明,terrain tiles 通常以 gzip 方式提供;解压后是小端序二进制数据。
4. layer.json 的作用#
Cesium terrain 服务根目录通常需要包含:
textlayer.json
layer.json 负责描述 terrain 格式、tile URL 模板、version、projection / tiling scheme、available tiles 和 extensions。
缺少或配置错误可能导致 terrain 无法加载、unknown format、unsupported quantized-mesh version 或瓦片路径请求错误。
5. Cesium Terrain Builder(CTB)说明#
Cesium Terrain Builder 是一个 C++ 库与命令行工具,用于从 DEM / GeoTIFF 生成 Cesium Terrain 瓦片。
CTB 通常依赖:
- GDAL:读取 GeoTIFF / DEM;
- zlib:处理 gzip 压缩;
- CMake:生成构建工程;
- C++ 编译器:如 Visual Studio / GCC / Clang。
CTB 可输出 Heightmap terrain、Quantized Mesh terrain、layer.json 和 {z}/{x}/{y}.terrain 瓦片。
6. Windows 下 zlib 编译#
以下以 zlib 1.2.11 + Visual Studio 2015 x64 为例。实际工程中也可以使用新版 zlib 或通过 vcpkg 管理依赖。
6.1 下载 zlib#
下载地址:
texthttps://zlib.net/
解压后进入 zlib 根目录。
6.2 打开 VS x64 本机工具命令提示符#
打开:
textVS2015 x64 Native Tools Command Prompt
进入 zlib 解压目录。
6.3 编译命令#
zlib 的 win32/Makefile.msc 中提供了 nmake 编译方式。
标准编译:
batnmake -f win32/Makefile.msc
x64 ASM 编译示例:
batnmake -f win32/Makefile.msc AS=ml64 LOC="-DASMV -DASMINF -I." OBJA="inffasx64.obj gvmat64.obj inffas8664.obj"
6.4 编译产物#
编译完成后通常生成:
| 文件 | 说明 |
|---|---|
zlib.lib |
静态库 |
zdll.lib |
动态库导入库 |
zlib1.dll |
动态库 |
7. GDAL 准备#
CTB 使用 GDAL 读取 DEM / GeoTIFF,因此需要准备 GDAL 的 include 与 lib。
常见方式:
- 自行源码编译 GDAL;
- 使用 OSGeo4W;
- 使用 vcpkg;
- 使用已有企业级 GDAL 编译包。
CMake 中通常需要配置:
textGDAL_INCLUDE_DIR = D:/gdal/include GDAL_LIBRARY = D:/gdal/lib/gdal.lib
8. CTB 编译流程#
8.1 下载 CTB#
CTB 参考仓库:
texthttps://github.com/geo-data/cesium-terrain-builder
Quantized Mesh 支持可参考相关 fork 或 Docker 项目。
8.2 使用 CMake GUI 配置#
打开 CMake GUI:
textSource code: CTB 源码目录 Build path : CTB build 目录
勾选:
textAdvanced
配置:
| CMake 参数 | 说明 |
|---|---|
GDAL_INCLUDE_DIR |
GDAL include 目录 |
GDAL_LIBRARY |
GDAL .lib 文件 |
ZLIB_INCLUDE_DIR |
zlib include 目录 |
ZLIB_LIBRARY_DEBUG |
zlib debug lib |
ZLIB_LIBRARY_RELEASE |
zlib release lib |
8.3 生成 Visual Studio 工程#
点击 Configure,选择:
textVisual Studio 14 2015 Win64
或对应 Visual Studio 版本,然后点击 Generate,生成 Visual Studio 解决方案。
8.4 命令行编译#
管理员方式打开 VS x64 Native Tools Command Prompt,进入 build 目录:
batmsbuild ALL_BUILD.vcxproj /p:Configuration="Release"
安装:
batmsbuild INSTALL.vcxproj /p:Configuration="Release"
注意:INSTALL 可能会把编译成果复制到 C:/Program Files/Cesium Terrain Builder,通常需要管理员权限。
8.5 Visual Studio 编译#
也可以直接打开:
textCesium Terrain Builder.sln
选择 Release / x64,编译 ALL_BUILD,需要安装时再编译 INSTALL。
9. CTB 验证#
执行:
batctb-info.exe --version
如果返回版本号,说明安装成功。
也可执行:
batctb-tile.exe --help
查看命令参数。
10. 使用 ctb-tile 生成地形瓦片#
10.1 生成 Quantized Mesh#
batctb-tile -o D:/tile -f Mesh D:/test/dtm.tif -c 4
参数说明:
| 参数 | 说明 |
|---|---|
-o |
输出目录 |
-f Mesh |
输出 Quantized Mesh |
-c 4 |
使用 4 个线程 |
D:/test/dtm.tif |
输入 DEM |
10.2 生成 layer.json#
batctb-tile -o D:/tile -f Mesh -l D:/test/dtm.tif -c 1
说明:
-l用于生成 layer metadata;- 通常生成速度较快;
- 使用 1 个 CPU 即可。
10.3 指定层级#
batctb-tile -o D:/tile -f Mesh -s 13 -e 0 D:/test/dtm.tif
注意:
textCTB 中 start zoom 通常大于 end zoom
即:
text-s 13 -e 0
表示从高层级向低层级生成。
11. Terrain 数据发布结构#
输出目录通常类似:
textterrain/ ├─ layer.json ├─ 0/ │ └─ 0/ │ └─ 0.terrain ├─ 1/ │ ├─ 0/ │ └─ 1/ └─ ...
可通过以下方式发布:
- Nginx
- Apache
- Tomcat
- Spring Boot 静态资源
- 对象存储
- CDN
12. 非压缩 Terrain 发布#
如果 .terrain 文件是非 gzip 数据:
text不需要 Content-Encoding: gzip
只需要配置正确 MIME 类型与跨域头。
12.1 Nginx 发布非压缩 Terrain#
nginxserver { listen 8081; server_name localhost; root D:/terrain; location / { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With,Content-Type,Range; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; try_files $uri =404; } location ~ \.terrain$ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With,Content-Type,Range; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; types { application/vnd.quantized-mesh terrain; } default_type application/vnd.quantized-mesh; } }
13. gzip Terrain 发布#
如果 .terrain 文件是 gzip 压缩后的二进制数据,服务端必须返回:
textContent-Encoding: gzip
否则 Cesium 会把 gzip 数据当作未压缩 terrain 解析,导致加载失败。
13.1 Nginx 发布 gzip Terrain#
nginxserver { listen 8081; server_name localhost; root D:/terrain; location / { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With,Content-Type,Range; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; try_files $uri =404; } location ~ \.terrain$ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With,Content-Type,Range; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; add_header Content-Encoding gzip; types { application/vnd.quantized-mesh terrain; } default_type application/vnd.quantized-mesh; } }
13.2 gzip_static 方式#
如果采用 .terrain.gz 文件,也可以使用 Nginx gzip_static 机制:
nginxgzip_static always; gunzip on;
但对于 CTB 默认直接输出的 gzip .terrain 文件,常见方式是直接为 .terrain 响应增加:
nginxadd_header Content-Encoding gzip;
14. Tomcat 发布 gzip Terrain#
如果使用 Tomcat 发布 gzip terrain,需要为 .terrain 响应增加:
textContent-Encoding: gzip
并设置跨域。思路如下:
- 在
web.xml中配置 CORS Filter; - 为
*.terrain配置自定义 GZipFilter; - 在 GZipFilter 中写入响应头:
Content-Encoding: gzip; - 为
.terrain配置 MIME 类型:application/vnd.quantized-mesh; - 将编译后的
cesium.GZipFilter放到WEB-INF/classes/cesium/GZipFilter.class或打包到 jar。
14.1 GZipFilter 示例#
javapackage cesium; public class GZipFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void destroy() { } public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setHeader("Content-Encoding", "gzip"); chain.doFilter(request, httpResponse); } }
14.2 web.xml 关键配置片段#
xml<filter> <filter-name>GZipFilter</filter-name> <filter-class>cesium.GZipFilter</filter-class> </filter> <filter-mapping> <filter-name>GZipFilter</filter-name> <url-pattern>*.terrain</url-pattern> </filter-mapping> <mime-mapping> <extension>terrain</extension> <mime-type>application/vnd.quantized-mesh</mime-type> </mime-mapping>
15. Cesium 前端加载示例#
jsconst terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl( "http://localhost:8081/terrain" ); const viewer = new Cesium.Viewer("cesiumContainer", { terrainProvider });
如果是旧版本 CesiumJS:
jsconst viewer = new Cesium.Viewer("cesiumContainer"); viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ url: "http://localhost:8081/terrain" });
16. 常见问题排查#
16.1 terrain 加载失败#
检查:
layer.json是否存在;{z}/{x}/{y}.terrain路径是否正确;Content-Type是否为application/vnd.quantized-mesh;- gzip terrain 是否返回
Content-Encoding: gzip; - 跨域头是否正确;
- 浏览器 Network 面板是否有 404 / 403 / CORS 错误。
16.2 gzip terrain 无法解析#
如果 terrain 文件本身是 gzip 压缩数据,但响应头没有:
textContent-Encoding: gzip
浏览器不会自动解压,Cesium 会拿到 gzip 二进制并按 terrain 原始结构解析,最终加载失败。
解决:
- Nginx 增加
add_header Content-Encoding gzip; - Tomcat 使用 Filter 增加响应头;
- 或者改 CTB 源码输出非压缩 terrain。
16.3 非压缩 terrain 仍然加载失败#
检查:
- 是否错误加了
Content-Encoding: gzip; - 文件是否确实是非 gzip;
- MIME 类型是否正确;
layer.json中格式、tiles 模板是否正确。
16.4 CORS 错误#
需要增加:
textAccess-Control-Allow-Origin: *
或指定业务域名。
17. 压缩与非压缩选择建议#
| 场景 | 建议 |
|---|---|
| 公网服务 | gzip |
| CDN 分发 | gzip |
| 内网高带宽 | 非 gzip 可考虑 |
| 本机服务 | 非 gzip 可考虑 |
| 弱网环境 | gzip |
| 浏览器 CPU 压力较大 | 非 gzip 可评估 |
| 传输成本敏感 | gzip |
说明:
- Quantized Mesh 官方规范倾向 gzip 传输;
- 非压缩 terrain 属于工程优化选择,需要确保网络传输不是瓶颈;
- 如果采用非压缩 terrain,服务端不要返回
Content-Encoding: gzip。
18. DEM 预处理建议#
18.1 NoData 处理#
CTB 对 NoData 的处理能力有限,建议提前处理 DEM 的 NoData。
ArcGIS Raster Calculator 示例:
textCon(IsNull("xxxx.tif"),0,"xxxx.tif")
作用:
textNoData -> 0
18.2 PixelType 转换#
建议根据业务精度要求,将 DEM 从 float 转为合适的整数类型,例如:
textfloat -> int16 / int32
需要注意:
- 转换前应确认高程精度损失是否可接受;
- 如果 DEM 有小数高程,直接转 int 会丢失小数;
- 可根据业务选择缩放后转 int。
19. 生产发布建议#
layer.json与瓦片目录保持同一根目录;- gzip terrain 必须正确返回
Content-Encoding: gzip; - Quantized Mesh terrain 建议返回
application/vnd.quantized-mesh; - 必须配置 CORS;
- 大范围地形建议分层级、分区域生成;
- 内网或本地服务可评估非 gzip terrain;
- 公网服务建议使用 gzip 或 CDN;
- 发布前用浏览器 Network 面板检查响应头。
20. 参考资料#
- CesiumTerrainProvider 官方文档:
https://cesium.com/learn/ion-sdk/ref-doc/CesiumTerrainProvider.html - Quantized Mesh 官方规范:
https://github.com/CesiumGS/quantized-mesh - Cesium Terrain Builder:
https://github.com/geo-data/cesium-terrain-builder - CTB Quantized Mesh Docker:
https://github.com/tum-gis/cesium-terrain-builder-docker - Nginx Compression 文档:
https://docs.nginx.com/nginx/admin-guide/web-server/compression/
21. 总结#
Cesium 地形发布的关键点不只是生成 .terrain 文件,还包括:
- 正确的地形格式;
- 正确的
layer.json; - 正确的目录结构;
- 正确的 MIME 类型;
- gzip terrain 必须有
Content-Encoding: gzip; - 非 gzip terrain 不能错误添加 gzip 响应头;
- DEM 需要提前处理 NoData;
- CTB 编译时需正确配置 GDAL 与 zlib。
如果业务场景是公网或弱网,建议保持 gzip terrain;如果是内网、本机或高带宽低延迟环境,且浏览器解压 CPU 成本明显,可以考虑修改 CTB 源码输出非压缩 terrain。