起因是我的vue项目中需要增加一个cesium三维渲染功能,将遥感影像数据添加到地球上,作为新手小白,一路上踩了不少坑。
目录
一、cesium中添加tif影像的一般流程
1.1 将tif格式转为png格式加载
通过查阅相关博文,发现cesium一般不支持直接加载本地geotiff格式的数据,除非是通过在线服务器端进行加载。故要加载tif格式的数据,常规的做法是将之转换为png格式进行加载。
1.2 具体加载流程
①切片处理加载
对于处理大范围、精细化的影像,一般采用此种方法,将一整张影像切割成一块一块小瓦片,再行加载。
具体来说,遥感影像的切片操作可以通过cesiumlab进行(西部世界官网),操作流程比较easy,可参考:【cesium知识梳理】2.cesium加载各类数据 - 掘金 (juejin.cn)。
cesium发布切片数据的代码如下:
// 添加影像数据
const localImage = viewer.scene.imageryLayers.addImageryProvider(
new Cesium.UrlTemplateImageryProvider({
url: 'yourImagePath/{z}/{x}/{y}.png',
fileExtension: 'png',
})
);
// 设置图层透明度
localImage.alpha = 0.8;
更为详细的代码可参照:Cesium数据加载_cesium加载csv数据-CSDN博客
②单瓦片加载
如果不想进行切片处理的话,可以直接采用单瓦片发布的方法,代码如下:
// 添加影像数据
const rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north);
const imageryProvider = new Cesium.SingleTileImageryProvider({
url: 'yourPath/imageName.png',
rectangle: rectangle,
});
const imageryLayer = viewer.imageryLayers.addImageryProvider(imageryProvider, 1);
// 设置图层透明度
imageryLayer.alpha = 0.7;
相关方法见cesium官网(SingleTileImageryProvider - Cesium Documentation)
west、south、east、north表示所影像覆盖的经纬度范围,如果不确定,可通过ArcGIS的图层属性获取相关信息。
二、cesium中添加极地影像
2.1 切片过程中碰到的问题
使用工具进行切片的过程是相当顺利的,但是将之加载到cesium上时,出现了显示问题,具体来说可以分为以下两种情形:
①未修改坐标系时
不对影像的坐标系做任何处理:
②修改了坐标系后
将影像的投影坐标系改为了WGS-1984地理坐标系,再进行切片处理:
可以看到,在修改了坐标系以后,情形有所改善,这就说明,在cesium中加载影像时更改坐标系这一步是必要的。但是另一方面,cesium自始至终都没能加载出影像信息,也许是切片过程存在问题,然而小白的能力实在有限,这就宣告了使用切片处理加载影像的方法行不通,于是后续我们探索使用单瓦片加载影像的方法。
2.2 极地影像的特殊性
对于将tif格式的影像转换为png格式,一开始的想法是用ArcGIS的数据导出为png格式,出现了如下报错:
于是后来改成了直接在ArcGIS上进行地图导出的方法(问就是gpt出的馊主意),这一操作为后续的跌坑埋下了伏笔。
cesium加载得到的结果如下:
当时我一度以为自己已经成功了,仔细一看真是壮士扼腕,不仅图片形变严重,而且北极点的位置都不对呀。此外多提一句,cesium加载png图像有像素限制,最大支持16384 x 16384。
(黄色为理论上的北极点位置,红色为实际上的北极点位置)
出现上述问题是由于极地遥感影像的特殊性导致的,这一区域涵盖了所有的经度线,横跨了北纬30°~90°的大片区域,因此直接添加png的方式会产生较大的形变。
2.3 重要的坐标转换
被上面这个问题困扰了许久,由于转换png的方式是错误的,致使我认为没有更改坐标系的必要。后来请教了专业人员,得到的回答是尝试更改坐标系以后再进行加载。于是我首先在ArcGIS中将影像的原始的投影坐标系(epsg为3413)改为了地理坐标系WGS-1984(epsg为4326)(这一操作应该可以在cesium中实现),接着换用更科学的方法将tif影像转为png格式,我这里使用的是Python的GDAL和PIL库。
在cesium中加载得到如下结果:
(可以依稀看到有影像信息,但是颜色对比不够明显)
2.4 tif 转 png 可能遇到的坑
为了让结果得到更良好的呈现,我决定在将数据保存为png格式之前多做一步渲染的操作,这里采用的是matplotlib库的colormap功能来实现,对图像进行伪彩色渲染,将单波段图像转化为彩色图像的方式(它通过将图像的灰度值映射到RGB颜色空间来实现),色带选择可参照python官网:Choosing Colormaps in Matplotlib — Matplotlib 3.8.3 documentation。
当然在这个地方也是遇到了一个坑,后来挣扎许久发现是因为没有对影像数据进行预处理。下面直接给出预处理操作前后的加载结果对比:
我这里的特殊值处理是去掉NaN值,并将负值替换为0,下附一段简单的tif转png的python代码:
from osgeo import gdal
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
def convert_tiff_to_png(tiff_filepath, png_filepath):
# 使用GDAL打开TIFF文件
dataset = gdal.Open(tiff_filepath)
# 读取TIFF文件的第一个波段
band = dataset.GetRasterBand(1)
arr = band.ReadAsArray()
# 去除NaN值
arr = np.nan_to_num(arr)
# 将小于0的元素值重新赋值为0
arr[arr < 0] = 0
# 将数据规范化到0-1的范围(也可以用对数变换或百分值法等)
arr = (arr - arr.min()) * 1.0 / (arr.max() - arr.min())
# 使用matplotlib的colormap进行伪彩色渲染
arr = cm.coolwarm(arr)
# 将数组转换为PIL图像
img = Image.fromarray((arr * 255.0).astype(np.uint8))
# 保存为PNG
img.save(png_filepath)
convert_tiff_to_png('yourTifPath/tifImageName.tif', 'yourPngPath/PngImageName.png')
三、总结
总的来说,在cesium中加载tif格式的极地遥感影像的一般步骤是:
①将极地影像的投影转换为地理坐标系WGS-1984;
②将极地影像数据格式由tif转换为png;
③在cesium中使用单瓦片加载的方式(SingleTileImageryProvider)加载png。