博客

Cesium Terrain Builder(CTB)功能扩展与源码修改实践

本文基于 Cesium Terrain Builder(CTB)源码,系统讲解了 Quantized Mesh Terrain 的结构与生成流程,并实现了非 gzip Terrain 输出及指定瓦片范围生成等生产级功能扩展与编译实践。

基于 Cesium Terrain Builder(CTB)源码实现:

  1. 支持输出非 gzip 压缩 Quantized Mesh Terrain
  2. 支持按指定瓦片范围输出 Terrain Tile

一、项目背景

Cesium Terrain Builder(CTB)是一个用于生成 Cesium Terrain Tile 的开源工具。

官方项目:

  • geo-data/cesium-terrain-builder
  • quantized-mesh terrain specification

CTB 可以:

  • 从 DEM(GeoTIFF)生成 Terrain Tile
  • 输出:
    • heightmap-1.0
    • quantized-mesh-1.0

二、业务问题

CTB 默认输出:

text
gzip 压缩 terrain tile

即:

text
*.terrain

文件实际为:

text
gzip(binary terrain data)

默认 gzip 的问题#

在某些业务场景:

  • Terrain 为内网访问
  • 本地 SSD
  • 局域网
  • 浏览器缓存命中高

此时:

text
gzip 解压增加浏览器 CPU 消耗

因此:

希望支持输出非 gzip terrain tile。


三、CTB 源码结构分析

1. Terrain 数据对象#

heightmap-1.0#

text
TerrainTile

quantized-mesh-1.0#

text
MeshTile

官方 quantized-mesh 规范:

text
Terrain tiles are served gzipped.

2. Terrain 生成器#

TerrainTiler#

生成:

text
TerrainTile

MeshTiler#

生成:

text
MeshTile

3. Terrain 序列化器#

核心:

text
CTBFileTileSerializer

负责:

text
tile -> 文件

四、功能扩展:支持非 gzip 输出

1. 增加 gzip 参数#

文件:

text
tools/ctb-tile.cpp

TerrainBuild 增加:

cpp
bool gzib;

构造函数默认:

cpp
gzib(false)

2. 增加命令行参数#

cpp
static void setGzib(command_t* command) { static_cast(Command::self(command))->gzib = true; }

main 注册:

cpp
command.option( "-G", "--gzip", "use gzip compress file(default uncompress)", TerrainBuild::setGzib );

3. 修改 serializeTile#

文件:

text
CTBFileTileSerializer.cpp

增加:

cpp
bool gzibFlag

核心代码:

cpp
if (gzibFlag) { CTBZFileOutputStream ostream(temp_filename.c_str()); tile->writeFile(ostream, writeVertexNormals); ostream.close(); } else { FILE *fp = fopen(temp_filename.c_str(), "wb"); CTBFileOutputStream ostream(fp); tile->writeFile(ostream, writeVertexNormals); fclose(fp); }

4. buildMesh 传递参数#

cpp
serializer.serializeTile( tile, writeVertexNormals, command->gzib );

五、功能扩展:指定范围输出

1. 增加参数#

TerrainBuild 增加:

cpp
int layerStartX; int layerStartY; int layerEndX; int layerEndY;

默认:

cpp
layerStartX(-1), layerStartY(-1), layerEndX(-1), layerEndY(-1)

2. 注册参数#

cpp
command.option("-A", "--start-x <sx>", "tile start x", TerrainBuild::setLayerStartX); command.option("-D", "--end-x <ex>", "tile end x", TerrainBuild::setLayerEndX); command.option("-W", "--start-y <sy>", "tile start y", TerrainBuild::setLayerStartY); command.option("-S", "--end-y <ey>", "tile end y", TerrainBuild::setLayerEndY);

3. buildMesh 范围过滤#

核心逻辑:

cpp
if ( coordinate->x >= layerStartX && coordinate->x <= layerEndX && coordinate->y >= layerStartY && coordinate->y <= layerEndY )

六、编译

Linux#

bash
sudo apt install build-essential cmake libgdal-dev zlib1g-dev bash mkdir build cd build cmake .. make -j8

七、命令行说明

默认 terrain#

bash
ctb-tile -o terrain dem.tif

quantized mesh#

bash
ctb-tile -f Mesh -o terrain dem.tif

指定范围#

bash
ctb-tile.exe -o D:/test/tile D:/test/dem.tif -f Mesh -s 6 -e 6 -A 105 -D 107 -W 45 -S 47

gzip 输出#

bash
ctb-tile -G -f Mesh -o terrain dem.tif

八、完整参数

参数 说明
-V 输出版本
-h 帮助
-o 输出目录
-p geodetic / mercator
-f Terrain / Mesh
-c 线程
-t tile size
-s start zoom
-e end zoom
-r 重采样
-R 不覆盖
-G gzip
-A start x
-D end x
-W start y
-S end y

九、DEM 预处理

NoData 处理#

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

作用:

text
NoData -> 0

PixelType#

推荐:

text
float -> int16/int32

十、生产建议

非 gzip 适合:#

  • 内网
  • SSD
  • 本地 terrain
  • 高并发

大范围 terrain#

推荐:

  • 分块生成
  • 局部更新
  • 分 zoom 生产

十一、相关项目

  • geo-data/cesium-terrain-builder
  • ahuarte47/cesium-terrain-builder
  • CesiumGS/quantized-mesh
  • tum-gis/ctb-quantized-mesh

十二、结论

本次源码扩展实现:

1. 非 gzip terrain 输出#

解决:

text
浏览器解压 CPU 开销

问题。


2. 指定范围 terrain 输出#

支持:

text
局部 terrain 更新

降低:

  • 生成时间
  • 磁盘占用
  • 更新成本