解决Cesium Terrain Builder 在Windows下编译不成功问题
由于最近公司需要制作离线GIS数据转换工具。故要放弃Cesiumlab工具。
首先,参考下面CTB在windows下安装指南。可以编译Windows版本的CTB。
但实际过程里面发现不少小坑,导致编译不成功。群里有许多小伙伴也遇到类似问题。以此我描述以上链接为基础在编译会遇到的问题进行解决,并结合可以生成Layer.json文件,不压缩.terrain文件也可查看地形数据。
一、解决编译问题。
1.解决问题。lib\gdal_i.lib : warning LNK4272: 库计算机类型“x64”与目标计算机类型“x86”冲突。

红线内一定选择x64,不然在编译时出链接错误。
2.解决问题。Unable to open EPSG support file gcs.csv。
需要设置环境变量:

如果设置之后还是出现“Unable to open EPSG support file gcs.csv”问题。则在cmd输入set gdal_data查看路径是否正确、有没有带其它符号(我遇到过最后带一个;(分号),反复出现,导到不能正常运行)。
3.解决问题。不能使用中文目录问题。
在GDALAllRegister();下一行加入CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
二、生成Layer.json。首先打ctb-tile.cpp文件。
1.在main()函数中加入:
command.option("-l", "--layer", "only output the layer.json metadata file", TerrainBuild::setMetadata);
2.添加函数
static void setMetadata(command_t *command) { static_cast<TerrainBuild *>(Command::self(command))->metadata = true; }
3.添加成员变量,并在构造函数初始化
bool metadata;
4.添加函数
static void buildMetadata(const RasterTiler &tiler, TerrainBuild *command, TerrainMetadata *metadata) { const string dirname = string(command->outputDir) + osDirSep; i_zoom startZoom = (command->startZoom < 0) ? tiler.maxZoomLevel() : command->startZoom, endZoom = (command->endZoom < 0) ? 0 : command->endZoom; const std::string filename = concat(dirname, "layer.json"); RasterIterator iter(tiler, startZoom, endZoom); int currentIndex = incrementMetaDatainIterator(iter, 0); setIteratorSize(iter); while (!iter.exhausted()) { const TileCoordinate *coordinate = iter.GridIterator::operator*(); if (metadata) metadata->add(tiler.grid(), coordinate); currentIndex = incrementMetaDatainIterator(iter, currentIndex); showProgress(currentIndex, filename); } }
5.修改runTiler函数
static int runTiler(const char *inputFilename, TerrainBuild *command, Grid *grid, TerrainMetadata *metadata) { GDALDataset *poDataset = (GDALDataset *)GDALOpen(inputFilename, GA_ReadOnly); if (poDataset == NULL) { cerr << "Error: could not open GDAL dataset" << endl; return 1; } // Metadata of only this thread, it will be joined to global later TerrainMetadata *threadMetadata = metadata ? new TerrainMetadata() : NULL; // Choose serializer of tiles (Directory of files, MBTiles store...) CTBFileTileSerializer serializer(string(command->outputDir) + osDirSep, command->resume); try { serializer.startSerialization(); if (command->metadata) { const RasterTiler tiler(poDataset, *grid, command->tilerOptions); buildMetadata(tiler, command, threadMetadata); } if (strcmp(command->outputFormat, "Terrain") == 0) { const TerrainTiler tiler(poDataset, *grid); buildTerrain(serializer, tiler, command, threadMetadata); } else if (strcmp(command->outputFormat, "Mesh") == 0) { const MeshTiler tiler(poDataset, *grid, command->tilerOptions, command->meshQualityFactor); buildMesh(serializer, tiler, command, threadMetadata, command->vertexNormals); } else { // it's a GDAL format const RasterTiler tiler(poDataset, *grid, command->tilerOptions); buildGDAL(serializer, tiler, command, threadMetadata); } } catch (CTBException &e) { cerr << "Error: " << e.what() << endl; } serializer.endSerialization(); GDALClose(poDataset); // Pass metadata to global instance. if (threadMetadata) { static std::mutex mutex; std::lock_guard<std::mutex> lock(mutex); metadata->add(*threadMetadata); delete threadMetadata; } return 0; }
以上五步可以解决生成数据,没有layer.json的问题。
三、由于ctb生成的文件是经过gzip压缩过的,需要在docker环境下搭建terrain-server很是麻烦。所以使ctb生成文件直接发布使用更为方便。
1.在main()函数下添加
command.option("-G", "--gzip", "use zib compress file(defalut uncompress)", TerrainBuild::setGzib);
2.添加函数
static void setGzib(command_t * command) { static_cast<TerrainBuild *>(Command::self(command))->gzib = true; }
3.添加成员,并在构造函数内初始化
bool gzib;
4.修改buildMesh函数内方法
static void buildMesh(MeshSerializer &serializer, const MeshTiler &tiler, TerrainBuild *command, TerrainMetadata *metadata, bool writeVertexNormals = false) { i_zoom startZoom = (command->startZoom < 0) ? tiler.maxZoomLevel() : command->startZoom, endZoom = (command->endZoom < 0) ? 0 : command->endZoom; // DEBUG Chunker: #if 0 const string dirname = string(command->outputDir) + osDirSep; TileCoordinate coordinate(13, 8102, 6047); MeshTile *tile = tiler.createMesh(tiler.dataset(), coordinate); // const string txtname = CTBFileTileSerializer::getTileFilename(&coordinate, dirname, "wkt"); const Mesh &mesh = tile->getMesh(); mesh.writeWktFile(txtname.c_str()); // CRSBounds bounds = tiler.grid().tileBounds(coordinate); double x = bounds.getMinX() + 0.5 * (bounds.getMaxX() - bounds.getMinX()); double y = bounds.getMinY() + 0.5 * (bounds.getMaxY() - bounds.getMinY()); CRSPoint point(x, y); TileCoordinate c = tiler.grid().crsToTile(point, coordinate.zoom); // const string filename = CTBFileTileSerializer::getTileFilename(&coordinate, dirname, "terrain"); tile->writeFile(filename.c_str(), writeVertexNormals); delete tile; return; #endif MeshIterator iter(tiler, startZoom, endZoom); int currentIndex = incrementMeshIterator(iter, 0); setIteratorSize(iter); GDALDatasetReaderWithOverviews reader(tiler); while (!iter.exhausted()) { const TileCoordinate *coordinate = iter.GridIterator::operator*(); if (metadata) metadata->add(tiler.grid(), coordinate); if (serializer.mustSerializeCoordinate(coordinate)) { MeshTile *tile = iter.operator*(&reader); if (serializer.mustSerializeCoordinate(coordinate)) { MeshTile *tile = iter.operator*(&reader); serializer.serializeTile(tile, writeVertexNormals, command->gzib); delete tile; } } currentIndex = incrementMeshIterator(iter, currentIndex); showProgress(currentIndex); } }
以上四步可以解决在生成.terrain不会被压缩。
可以打开cmd使用以下命令进行测试 ctb-tile.exe -o ./terrain-tiles ./DEMYB-02.tif -f Mesh -l
最后如果大家遇到问题,请留言,我会解答。