博客

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 地形服务的完整流程:

  1. Cesium 中 TerrainProvider 的基本类型;
  2. Cesium 支持的地形数据格式;
  3. CesiumTerrainProvider 的使用方式;
  4. Cesium Terrain Builder(CTB)的作用;
  5. Windows 下 zlib、GDAL、CTB 的编译思路;
  6. 使用 ctb-tile 生成 Quantized Mesh 地形瓦片;
  7. 生成 layer.json;
  8. 使用 Nginx / Tomcat 发布地形瓦片;
  9. gzip terrain 与非 gzip terrain 的发布差异;
  10. 常见问题与排查方法。

2. Cesium 中的 TerrainProvider#

Cesium 使用 TerrainProvider 抽象加载地形数据。不同 Provider 对应不同的数据来源、接口形式和瓦片组织方式。

2.1 CesiumTerrainProvider#

CesiumTerrainProvider 用于访问 Cesium terrain 格式的地形服务。Cesium 官方文档说明,它支持 Quantized Mesh 与 Height Map 这两类 Cesium terrain format。

js
const terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl( "http://localhost:8081/terrain" ); const viewer = new Cesium.Viewer("cesiumContainer", { terrainProvider });

旧版本常见写法:

js
const 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 常见格式主要有两类:

  1. Heightmap
  2. Quantized Mesh

两者文件后缀通常都是 .terrain,但内部结构不同。

3.1 Heightmap Terrain#

Heightmap 使用规则格网存储高程。特点:

  • 结构简单;
  • 数据组织直观;
  • 适合基础地形;
  • 在 Cesium 中通常封装为 HeightmapTerrainData。

典型 URL:

text
http://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 服务根目录通常需要包含:

text
layer.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#

下载地址:

text
https://zlib.net/

解压后进入 zlib 根目录。

6.2 打开 VS x64 本机工具命令提示符#

打开:

text
VS2015 x64 Native Tools Command Prompt

进入 zlib 解压目录。

6.3 编译命令#

zlib 的 win32/Makefile.msc 中提供了 nmake 编译方式。

标准编译:

bat
nmake -f win32/Makefile.msc

x64 ASM 编译示例:

bat
nmake -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。

常见方式:

  1. 自行源码编译 GDAL;
  2. 使用 OSGeo4W;
  3. 使用 vcpkg;
  4. 使用已有企业级 GDAL 编译包。

CMake 中通常需要配置:

text
GDAL_INCLUDE_DIR = D:/gdal/include GDAL_LIBRARY = D:/gdal/lib/gdal.lib

8. CTB 编译流程#

8.1 下载 CTB#

CTB 参考仓库:

text
https://github.com/geo-data/cesium-terrain-builder

Quantized Mesh 支持可参考相关 fork 或 Docker 项目。

8.2 使用 CMake GUI 配置#

打开 CMake GUI:

text
Source code: CTB 源码目录 Build path : CTB build 目录

勾选:

text
Advanced

配置:

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,选择:

text
Visual Studio 14 2015 Win64

或对应 Visual Studio 版本,然后点击 Generate,生成 Visual Studio 解决方案。

8.4 命令行编译#

管理员方式打开 VS x64 Native Tools Command Prompt,进入 build 目录:

bat
msbuild ALL_BUILD.vcxproj /p:Configuration="Release"

安装:

bat
msbuild INSTALL.vcxproj /p:Configuration="Release"

注意:INSTALL 可能会把编译成果复制到 C:/Program Files/Cesium Terrain Builder,通常需要管理员权限。

8.5 Visual Studio 编译#

也可以直接打开:

text
Cesium Terrain Builder.sln

选择 Release / x64,编译 ALL_BUILD,需要安装时再编译 INSTALL。


9. CTB 验证#

执行:

bat
ctb-info.exe --version

如果返回版本号,说明安装成功。

也可执行:

bat
ctb-tile.exe --help

查看命令参数。


10. 使用 ctb-tile 生成地形瓦片#

10.1 生成 Quantized Mesh#

bat
ctb-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#

bat
ctb-tile -o D:/tile -f Mesh -l D:/test/dtm.tif -c 1

说明:

  • -l 用于生成 layer metadata;
  • 通常生成速度较快;
  • 使用 1 个 CPU 即可。

10.3 指定层级#

bat
ctb-tile -o D:/tile -f Mesh -s 13 -e 0 D:/test/dtm.tif

注意:

text
CTB 中 start zoom 通常大于 end zoom

即:

text
-s 13 -e 0

表示从高层级向低层级生成。


11. Terrain 数据发布结构#

输出目录通常类似:

text
terrain/ ├─ 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#

nginx
server { 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 压缩后的二进制数据,服务端必须返回:

text
Content-Encoding: gzip

否则 Cesium 会把 gzip 数据当作未压缩 terrain 解析,导致加载失败。

13.1 Nginx 发布 gzip Terrain#

nginx
server { 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 机制:

nginx
gzip_static always; gunzip on;

但对于 CTB 默认直接输出的 gzip .terrain 文件,常见方式是直接为 .terrain 响应增加:

nginx
add_header Content-Encoding gzip;

14. Tomcat 发布 gzip Terrain#

如果使用 Tomcat 发布 gzip terrain,需要为 .terrain 响应增加:

text
Content-Encoding: gzip

并设置跨域。思路如下:

  1. 在 web.xml 中配置 CORS Filter;
  2. 为 *.terrain 配置自定义 GZipFilter;
  3. 在 GZipFilter 中写入响应头:Content-Encoding: gzip;
  4. 为 .terrain 配置 MIME 类型:application/vnd.quantized-mesh;
  5. 将编译后的 cesium.GZipFilter 放到 WEB-INF/classes/cesium/GZipFilter.class 或打包到 jar。

14.1 GZipFilter 示例#

java
package 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 前端加载示例#

js
const terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl( "http://localhost:8081/terrain" ); const viewer = new Cesium.Viewer("cesiumContainer", { terrainProvider });

如果是旧版本 CesiumJS:

js
const viewer = new Cesium.Viewer("cesiumContainer"); viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ url: "http://localhost:8081/terrain" });

16. 常见问题排查#

16.1 terrain 加载失败#

检查:

  1. layer.json 是否存在;
  2. {z}/{x}/{y}.terrain 路径是否正确;
  3. Content-Type 是否为 application/vnd.quantized-mesh;
  4. gzip terrain 是否返回 Content-Encoding: gzip;
  5. 跨域头是否正确;
  6. 浏览器 Network 面板是否有 404 / 403 / CORS 错误。

16.2 gzip terrain 无法解析#

如果 terrain 文件本身是 gzip 压缩数据,但响应头没有:

text
Content-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 错误#

需要增加:

text
Access-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 示例:

text
Con(IsNull("xxxx.tif"),0,"xxxx.tif")

作用:

text
NoData -> 0

18.2 PixelType 转换#

建议根据业务精度要求,将 DEM 从 float 转为合适的整数类型,例如:

text
float -> int16 / int32

需要注意:

  • 转换前应确认高程精度损失是否可接受;
  • 如果 DEM 有小数高程,直接转 int 会丢失小数;
  • 可根据业务选择缩放后转 int。

19. 生产发布建议#

  1. layer.json 与瓦片目录保持同一根目录;
  2. gzip terrain 必须正确返回 Content-Encoding: gzip;
  3. Quantized Mesh terrain 建议返回 application/vnd.quantized-mesh;
  4. 必须配置 CORS;
  5. 大范围地形建议分层级、分区域生成;
  6. 内网或本地服务可评估非 gzip terrain;
  7. 公网服务建议使用 gzip 或 CDN;
  8. 发布前用浏览器 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 文件,还包括:

  1. 正确的地形格式;
  2. 正确的 layer.json;
  3. 正确的目录结构;
  4. 正确的 MIME 类型;
  5. gzip terrain 必须有 Content-Encoding: gzip;
  6. 非 gzip terrain 不能错误添加 gzip 响应头;
  7. DEM 需要提前处理 NoData;
  8. CTB 编译时需正确配置 GDAL 与 zlib。

如果业务场景是公网或弱网,建议保持 gzip terrain;如果是内网、本机或高带宽低延迟环境,且浏览器解压 CPU 成本明显,可以考虑修改 CTB 源码输出非压缩 terrain。