解决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;