1.背景
本地有自己的影像跟地形,影像是 .png,地形是 .tif。
2.加载影像
影像被分为两个文件夹:world1_out、world2_out,分别存放北半球跟南半球的数据。
在nginx服务器上发布影像数据:
server {
listen 8089;
server_name localhost;
# 配置北半球影像路径
location /world1/ {
alias C:/Users/Administrator/Documents/earth/world_mark/world1_out/;
autoindex on;
add_header Access-Control-Allow-Origin *;
add_header Cache-Control "no-cache,must-revalidate";
}
# 配置南半球影像路径
location /world2/ {
alias C:/Users/Administrator/Documents/earth/world_mark/world2_out/;
autoindex on;
add_header Access-Control-Allow-Origin *;
add_header Cache-Control "no-cache,must-revalidate";
}
}
在 Cesium 中添加影像图层:
this.viewer = new Cesium.Viewer('cesiumContainer', {});
// 添加北半球影像图层
this.viewer.imageryLayers.addImageryProvider(
new Cesium.TileMapServiceImageryProvider({
url: process.env.VUE_APP_MAP_API_Image + '/world1',
fileExtension: 'png',
minimumLevel: 0,
maximumLevel: 8,
rectangle: Cesium.Rectangle.fromDegrees(-180.0, 0.0, 180.0, 90.0), // 北半球范围
})
);
// 添加南半球影像图层
this.viewer.imageryLayers.addImageryProvider(
new Cesium.TileMapServiceImageryProvider({
url: process.env.VUE_APP_MAP_API_Image + '/world2',
fileExtension: 'png',
minimumLevel: 0,
maximumLevel: 8,
rectangle: Cesium.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 0.0),
})
);
3.加载地形
地形是 .tif 文件,在 Cesium 中,当你使用 Cesium.CesiumTerrainProvider 来加载地形数据时,Cesium 默认期望你提供的数据是 Quantized Mesh 或 Heightmap 格式。这两种格式是 Cesium 支持的标准地形格式(.terrain文件)。
可以使用 Cesium Terrain Builder(CTB) 来生成 .terrain 文件。我这里用的是 docker 版的CTB(github.com/tum-gis/ces… )。
①.下载 docker 并启动
地址:docs.docker.com/desktop/ins…
(补充说明:docker里的 container 翻译为“容器”,image 翻译为“镜像”。)
②.打开CMD执行以下命令
方案一:
- 拉取CTB镜像:
docker pull tumgis/ctb-quantized-mesh
- 运行容器:
docker run --name ctb -t -i tumgis/ctb-quantized-mesh:latest /bin/bash
(这一步如果不想运行命令,那么在 docker 图形界面直接点击启动就行,好像是这样的,我也是刚接触,不太确定) - 上一步成功后,命令行会进入容器内部,由于还有命令需要在外面执行,所以需要在命令行退出容器。而我目前不知道怎么退出,索性直接关闭cmd再重新打开
- 将地形原始文件(elevation.tif,约7GB)copy到容器里:
docker cp C:/path/to/elevation.tif ctb:/data
- 在命令行执行这条命令来进入容器内部:
docker exec -it ctb bash
- 上条命令执行完后,默认进入到容器的data目录,此时可以通过
ls
查看当前目录下的文件,有个 elevation.tif - 通过
mkdir -p terrain
创建一个名为terrain的文件夹,用来存放切片后的数据 - 切片:
ctb-tile -f Mesh -C -N -o terrain elevation.tif
,如果tif文件较大,这一过程会比较漫长,而且CPU、内存占用较高,此时做其他操作很卡。我这个文件约有7GB,好像过了两三个小时执行结束了,只不过并不是以成功结束的,而是失败了。不过我通过cd terrain
进入terrain文件夹,执行ls
查看文件后,发现生成了2~10级的地形文件,只不过0级、1级没生成,而且2级文件不全,我索性把2级文件删了rm -r 2
- 生成 layer.json 说明文件:
ctb-tile -f Mesh -C -N -l -o terrain elevation.tif
,这条命令只是多了个-l
的参数 - 将terrain文件copy到本机:找一个合适的文件夹,运行cmd,执行
docker cp ctb:/data/terrain .
,这会将docker容器里data文件夹下的terrain文件夹copy到当前目录下。这个过程也比较漫长,我用了两三个小时。
上述方案是我对照其他博客做的,我在写这篇文章的时候,看到docker CTB的readme里的方法好像不一样,于是也试了一下。他介绍的方法免去了将文件从本地复制到容器内这一过程,取而代之的是将本地文件“挂载”到容器中,这样能节省一部分时间。
方案二(docker CTB readme):
- 拉取CTB镜像:
docker pull tumgis/ctb-quantized-mesh
- 运行容器并将本机地形文件夹挂载到容器的data目录下:
docker run -it --name ctb -v "C:/path/to/terrainFolder:/data" tumgis/ctb-quantized-mesh
(这里 -v的参数是<本地路径>:<容器内路径>
,后面跟着一个空格再加上<镜像名>
,本地路径必须是绝对路径),这个操作,我理解的是,就像本机跟虚拟机共享文件一样。如果只需要挂载一个文件(如merge.tif
)而非整个文件夹,可以直接指定文件路径:docker run -it --name ctb -v "C:/Users/Administrator/Documents/earth/merge.tif:/data/merge.tif" tumgis/ctb-quantized-mesh
- 这里跟方案一中第6~10步相同。
至此拿到了Cesium可加载的地形文件,目录是这样的:
- terrain
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- layer.json
由于0~2级的terrain文件没有生成,所以我又尝试着单独生成一下。由于有用的文件我已经copy到本机了,所以我在docker上把这些文件都删除了rm -r 3
、rm layer.json
生成0~2级的terrain文件:ctb-tile -f Mesh -C -N -o terrain elevation.tif -s 2 -e 0
,如果不明白参数的意义,可以通过ctb-tile --help
来查看。我执行上述命令时报错了,这里官方文档也提到了(github.com/tum-gis/ces… ), 他说,在处理大型数据集时,在低缩放级别上可能有溢出问题,建议使用 gdal_translate 由现在的7G的tif文件创建出一个低分辨率的、所占空间更小的tif文件,而后在新的tif文件上执行切片。
说干就干,执行gdal_translate -outsize 5% 5% elevation.tif elevation_lower.tif
将原文件的宽高各缩小至5%,再用新的tif生成低缩放级别的terrain文件:ctb-tile -f Mesh -C -N -o terrain elevation_lower.tif -s 2 -e 0
,之后再将生成的文件夹一个一个copy到本机上。
③.在nginx服务器上发布地形数据:
server {
listen 8090;
server_name localhost;
root C:/Users/Administrator/Documents/earth/terrainFiles;
autoindex on;
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
location ~* .terrain$ {
add_header Content-Disposition 'attachment;filename=$arg_filename';
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Content-Encoding "gzip";
add_header Content-Type "application/octet-stream";
}
}
}
④.在 Cesium 中添加地形:
this.viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: new Cesium.CesiumTerrainProvider({
url: process.env.VUE_APP_MAP_API_Terrain, // 自定义地形服务的 URL
}),
});
vue项目 .env.xxx 文件里:
VUE_APP_MAP_API_Image = 'http://localhost:8089'
VUE_APP_MAP_API_Terrain = 'http://localhost:8090'