cesium直接加载TIFF地图影像

文章讲述了在Cesium中使用TIFF影像时,如何处理uint16类型数据并将其转换为uint8以适应canvas渲染的问题,介绍了坐标转换和数据格式调整的过程,以及存在的颜色偏差和优化方法。

项目场景:

遇到了一个需求: 就是用cesium加载TIFF影像,本来主流的方式是后台切片发布服务,前端直接调用服务加载就行,但是 后台直接给了我一个tif文件,就需要前端来进行绘图渲染了


问题描述

做之前先上网查看了一下有没有相关的文档,查到了某大佬写的文https://blog.youkuaiyun.com/lovefengruoqing/article/details/115306876
给了我思路,但是 执行下来就会遇到各种各样的问题,以下详细说一下:

  const tiff = await fromBlob(blob);
  let image = await tiff.getImage();
  let [west, south, east, north] = image.getBoundingBox();
  const code = image.geoKeys.ProjectedCSTypeGeoKey || image.geoKeys.GeographicTypeGeoKey;
  // 我这里拿到的epsg是32651

所以需要对 方位坐标(west, south, east, north) 的数据进行转换为4326格式的数据,这里用的是proj4 库进行的转换

// 定义 EPSG32651 和 EPSG4326 的投影信息
  proj4.defs('EPSG:32651', '+proj=utm +zone=51 +datum=WGS84 +units=m +no_defs');
  proj4.defs('EPSG:4326', '+proj=longlat +datum=WGS84 +no_defs');

  // 进行坐标转换
  const utmCoords = [west, south]; // 组合坐标
  const utmCoords1 = [east, north]; // 组合坐标

  // 注意这两条数据 后面绘制图片区域时要用!!!
  const convertedCoords = proj4('EPSG:32651', 'EPSG:4326', utmCoords);
  const convertedCoords1 = proj4('EPSG:32651', 'EPSG:4326', utmCoords1);

接下来就是要用canvas绘制图像了


  // 注意这一步,有坑***1.0
  const [red = [], green = [], blue = []] = await image.readRasters();
  // 将像素信息写入canvas
  const canvas = document.createElement('canvas');
  let width = image.getWidth();
  let height = image.getHeight();
  canvas.width = width;
  canvas.height = height
<think> 我们正在讨论Cesium加载GeoTIFF影像的方法。根据引用内容,Cesium本身可能不支持直接加载.tif格式的文件,但我们可以通过解析GeoTIFF文件并利用Cesium的API来实现加载。 解决方案有两种思路: 1. 如果Cesium支持直接加载tif文件(实际上通常不支持),则只需获取GeoTIFF的坐标范围,然后使用Cesium加载单张图片的API。 2. 如果Cesium不支持,则需要先解析GeoTIFF文件,提取地理信息和像素信息,再将像素信息转换成Cesium支持的图像格式(如Canvas或ImageData),然后利用Cesium的API加载。 根据引用[1]和[3][4],我们可以使用geotiff.js库来解析GeoTIFF文件。步骤如下: 步骤1:使用geotiff.js读取GeoTIFF文件,获取图像数据和地理参考信息(如坐标范围、投影等)。 步骤2:将图像数据转换为Cesium可以识别的图像格式,比如使用Canvas绘制图像,或者转换为其他格式(如PNG)的DataURL。 步骤3:利用Cesium的`ImageryProvider`(特别是`SingleTileImageryProvider`)加载转换后的图像,并设置正确的坐标范围。 具体实现: 1. 安装geotiff.js: ```bash npm install geotiff ``` 2. 读取GeoTIFF文件并解析: ```javascript import * as GeoTIFF from 'geotiff'; async function loadGeoTiff(url) { const response = await fetch(url); const arrayBuffer = await response.arrayBuffer(); const tiff = await GeoTIFF.fromArrayBuffer(arrayBuffer); const image = await tiff.getImage(); // 获取第一个图像 const rasters = await image.readRasters(); // 读取栅格数据 const tiepoint = image.getTiePoints()[0]; // 获取连接点(通常包含左上角坐标) const pixelScale = image.getFileDirectory().ModelPixelScale; const width = rasters.width; const height = rasters.height; // 计算地理范围 const [west, north] = [tiepoint.x, tiepoint.y]; // 左上角坐标 const east = west + pixelScale[0] * width; const south = north - pixelScale[1] * height; return { rasters, west, south, east, north, width, height }; } ``` 3. 将栅格数据绘制到Canvas上(假设是RGB图像): ```javascript function createCanvasFromRasters(rasters, width, height) { const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); const imageData = ctx.createImageData(width, height); const data = imageData.data; // 假设rasters有三个波段(红、绿、蓝) const red = rasters[0]; const green = rasters[1]; const blue = rasters[2]; for (let i = 0; i < width * height; i++) { data[i * 4] = red[i]; // R data[i * 4 + 1] = green[i]; // G data[i * 4 + 2] = blue[i]; // B data[i * 4 + 3] = 255; // A } ctx.putImageData(imageData, 0, 0); return canvas; } ``` 4. 使用Cesium加载Canvas作为图层: ```javascript async function addGeoTiffToCesium(viewer, geotiffUrl) { const { rasters, west, south, east, north, width, height } = await loadGeoTiff(geotiffUrl); const canvas = createCanvasFromRasters(rasters, width, height); // 将Canvas转换为图片URL const dataURL = canvas.toDataURL('image/png'); // 使用Cesium的SingleTileImageryProvider加载 viewer.imageryLayers.addImageryProvider( new Cesium.SingleTileImageryProvider({ url: dataURL, rectangle: Cesium.Rectangle.fromDegrees(west, south, east, north) }) ); } ``` 注意:如果GeoTIFF文件很大,上述方法可能会消耗大量内存。对于大型GeoTIFF,可能需要分块处理或使用其他优化方法。 另外,引用[4]提到坐标转换,这是因为GeoTIFF可能使用不同的坐标系(如UTM),而Cesium使用WGS84经纬度。因此,在获取坐标范围后,可能需要进行坐标转换。示例代码中假设坐标已经是经纬度(WGS84)。如果不是,则需要使用proj4等库进行转换。 引用[2]提供了关于GeoTIFF格式的详细信息,以及在其他地图库(如OpenLayers、Leaflet)中加载GeoTIFF的示例,这些思路可以借鉴。 总结:通过geotiff.js解析GeoTIFF文件,提取图像数据和地理范围,然后将图像数据转换为Cesium可以加载的格式(如Canvas),并利用Cesium的`SingleTileImageryProvider`加载,同时设置正确的地理范围。 如果用户需要处理多波段数据(如高程数据),则可能需要不同的处理方式,例如生成地形或单波段数据可视化。 相关问题: 1. 如何优化Cesium加载大型GeoTIFF文件的性能? 2. GeoTIFF文件包含高程数据时,如何在Cesium中显示为地形? 3. 如何处理非WGS84坐标系的GeoTIFF文件在Cesium中的显示? </think> ### Cesium加载GeoTIFF格式数据的方法 Cesium本身**不支持直接加载GeoTIFF文件**(.tif后缀),但可通过以下两种核心方案实现加载: #### 方案1:通过JavaScript解析GeoTIFF(推荐) 1. **使用geotiff.js库解析** ```javascript npm install geotiff // 或CDN引入<script src="https://cdn.jsdelivr.net/npm/geotiff"></script> ``` 2. **解析地理坐标范围** ```javascript const tiff = await GeoTIFF.fromArrayBuffer(arrayBuffer); const image = await tiff.getImage(); const bbox = image.getBoundingBox(); // 获取坐标范围 [minX, minY, maxX, maxY] ``` 3. **转换图像数据** ```javascript const rasters = await image.readRasters(); const canvas = renderToCanvas(rasters); // 将像素转为Canvas/PNG ``` 4. **Cesium加载** ```javascript viewer.imageryLayers.addImageryProvider( new Cesium.SingleTileImageryProvider({ url: canvas.toDataURL("image/png"), rectangle: Cesium.Rectangle.fromDegrees(west, south, east, north) }) ); ``` #### 方案2:服务端预转换 1. **格式转换** - 使用GDAL将GeoTIFF转瓦片地图服务(TMS): ```bash gdal_translate -of GTiff input.tif output.tif gdal2tiles.py -z 0-10 output.tif ./tiles ``` 2. **Cesium加载瓦片** ```javascript viewer.imageryLayers.addImageryProvider( new Cesium.UrlTemplateImageryProvider({ url: "./tiles/{z}/{x}/{y}.png" }) ); ``` #### 关键注意事项 1. 坐标系需统一为WGS84(EPSG:4326) 2. 大型文件需切片处理避免性能问题 3. 多波段数据需要额外处理(如RGB合成) > 引用说明:方案基于geotiff.js解析原理[^1][^3],坐标转换方法参考cesium示例[^4],GeoTIFF结构知识详见格式文档[^2]。
评论 11
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值