前言

最近碰到了一个需求,需要通过 cesium 直接加载 geotiff 影像图。

咋一听,这个需求好像蛮奇怪,cesium 本身本来就支持加载 tile 影像图,也就是所谓的切片地图。原理其实就是,通过 geoserver 等工具,按照一定的规则和坐标系规则,切好对应的切片。

而 cesium 里面,加载瓦片地图也很简单,想要显示哪个区域的地图,就根据对应的规则,去 geoserver 里请求对应的切片。这些逻辑在 cesium 里面,也已经封装好了,直接调用就好了。

但是如果不想发布到 geoserver,想直接通过 cesium,加载 geotiff 影像文件,来预览影像图呢?

说实话,刚开始碰到这个需求,内心也是没底的,毕竟翻遍了 cesium 的 api,也没有发现,其能支持这种加载方式。

而且,geotiff 影像图的格式,对于我来说,也是一片未知的领域。要不是去年开始接触 cesium 和 geoserver,我根本不知道它的存在。

当然,碰到问题,还是得发挥一个程序员的 geek 精神,先搜索下,看有没有人碰到同样的烦恼。

虽然这种方式不常见,但是,还是有同道中人的,但是结果多不理想,甚至有人直接回复,说不支持这种加载方式。

就在我一度想要放弃的时候,忽然有了灵感。

几个小问题

既然 geotiff 本质上是一张图片,文件不太大的图,甚至直接用一些常见的看图软件就能打开,那么想要贴在 cesium 的 globe 上,又有何难呢?

现在摆在面前的有几个问题:

  1. 如果 cesium 支持贴 tif 后缀的图,那么皆大欢喜,只要想方设法解析到 geotiff 的坐标范围信息,然后调用 cesium 提供的加载单张图作为图层的 api,再传入范围信息,即可正常的加载该 geotiff 图。
  2. 如果很不幸,cesium 不支持贴 tif 后缀的图,那么我们就得先解析 geotiff 文件,想办法获取到相关的地理信息和像素信息,拿到像素信息和地理信息以后,像第一种情形一样处理,无非就是多了一步将像素信息处理成 cesium 可以支持的图像信息而已。
  3. 我们该如何解析出 geotiff 内部的信息呢?

接下来,就让我们对提出的问题,一个个尝试解决方案,如果能够迎刃而解,那么用 cesium 加载影像图,不是如同探囊取物么!

尝试寻找解决方案

我们先来找找,看能否找到前端解析 geotiff 的解决方案。

我们知道,如果用桌面软件,查看 geotiff 图像,很多常见的软件都能支持,大到像 arcgis,小到像 windows 看图,都能查看。

但是前端,是否有现成的工具,可以用来解析 geotiff 图像呢?

带着这样的疑问,开始了我们的探寻之旅。

经过一番尝试以后,发现前端有个开源的库—— geotiff.js ,可以用来解析 .tif 格式的文件。

具体 geotiff.js 的 api,在这里就不做过多的介绍了,有兴趣了解的,可以去看下官方提供的 readme 文件,上面有用法的详细说明。

原始影像图

假设现在有个 geotiff 文件,用 IrfanView 文件打开,是这个样子的:

解析 geotiff 文件

geotiff.js 提供了几种写入读取 二进制文件的方式,为了方便使用,我们就尝试采用 fromBlob 的方式。

我们先调用 geotiff.js 提供的 api,将文件读取成 js 对象,再通过对象提供的 getImage api,获取到图像的相关信息。

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;

为了准确的把图贴到 cesium 的球面上去,我们必须要先获取到图像的范围,并且要获取到图像采用的是哪种坐标系。

我们测试的这张图,打印出上述信息,发现采用的是 4527 坐标系,范围如图示:

转换坐标的方法

现在问题是,cesium 目前已知的,只支持月球、标准的球体和 WGS84 体系的坐标体系,不支持我们的 CGCS2000 坐标系。

怎么办呢?我们必须能找到一个换算的方式,将我们的坐标换算成 WGS84 坐标体系里的点。

可是,由于本身对 gis 专业相关的基础知识的匮乏,对于坐标体系转换,毫无经验,根本不知道怎么转换该如何是好?

虽然,怎么转化,论文里都有,但是等学会那些,再来解决这个问题,都不知道要等到猴年马月去呀。

不过不要着急,我发现了一个网站支持这种服务,提供了这种转换的接口。

不用自己写转换坐标的算法,岂不是很舒服!

http://epsg.io/

首页长这样:

点击进入这个 transform coordinates 页面:

我们试着输入一个坐标:

返现返回了我们想要的结果,点进去看下位置:

现在问题是,虽然我们能在页面上获取转换结果,但是总不能每次都打开页面,输入地址,来获取转换后的坐标吧?

无妨,我们打开控制台看一下,转换的过程到底经历了写什么。

我们点一下 transform,发现页面发了一个 ajax 请求,里面包含了一些相关的信息

而返回的结果,正是在 4326 体系下,的经纬度坐标信息:

既然有了转换方式,可以转换坐标,那么接下来要做的就很简单了。

通过接口,获取该影像图所表示的地理区域的范围:

let { x: w, y: n } = await (await fetch(`//epsg.io/trans?x=${west}&y=${north}&s_srs=${code}&t_srs=4326`)
).json();
let { x: e, y: s } = await (await fetch(`//epsg.io/trans?x=${east}&y=${south}&s_srs=${code}&t_srs=4326`)
).json();

将 geotiff 像素信息写入 canvas

按理说,走到了这一步后,如果 cesium 支持直接加载 geotiff 图为静态的 图层,是最理想的状态,可惜的是,它并不支持。

既然它不支持,我们就要想办法另辟蹊径了。

// 读取像素信息
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;
let ctx = canvas.getContext("2d");
let imageData = ctx.createImageData(width, height);console.time("写入像素");
for (var i = 0; i < imageData.data.length / 4; i += 1) {imageData.data[i * 4 + 0] = red[i];imageData.data[i * 4 + 1] = green[i] || 0;imageData.data[i * 4 + 2] = blue[i] || 0;imageData.data[i * 4 + 3] = red[i] === 0 ? 0 : 255;
}ctx.putImageData(imageData, 0, 0);console.timeEnd("写入像素");

我们可以通过 image 对象提供的 readRasters 接口,将像素信息读取出来,然后写入 canvas,形成一张前端可以操控的图。

在 cesium 中加载

遗憾的是,cesium 的 SingleTileImageryProvider 接口,并不支持对 canvas 的直接载入,需要转换成图片才能进行操作。

我们可以调用 canvas 自带的 toDataURL 将 canvas 转换成图片,然后传进去即可。

let rectangle = Cesium.Rectangle.fromDegrees(w, s, e, n);
let du = canvas.toDataURL();viewer.imageryLayers.addImageryProvider(new Cesium.SingleTileImageryProvider({url: du,rectangle,})
);viewer.camera.setView({destination: rectangle,
});

这样,我们就成功的将该 geotiff 影像图,直接加载到 cesium 里面去了。

调整颜色

到了这一步,我们要做的差不多就结束了。

但是细心的同学可能会发现,加载到 cesium 里的影像图的颜色跟我们前面用软件打开的时候不太一样。

这是为什么呢?

要理解这个问题,可能需要童鞋们去了解下颜色的构成方式。

这里我们采用的是 rgb 的表示方法。

当我们运行代码的时候,进入调试模式,你会发现,

默认这个影像里面,只存储了 R 的信息,G、B 的信息并没有。

那么怎么处理呢?

其实很简单,只需要改一行代码即可:

const [red = [], green = red, blue = red] = await image.readRasters();

将 green 和 blue 均赋值一个初始值,等于 red 即可。

然后,我们再次尝试运行一下代码,就会得到下图所示的场景了:

此刻,细心的童鞋就会发现,这与我们之前打开的图一般无二了。

后记

当然,有一些情况,我们这里并没有考虑到,有兴趣的同学可以自己研究下:

  1. 一般情况下,geotiff 影像图都非常的大,我们的示例并未考虑到影像图的大小对系统的影响。
  2. 我们这里只考虑了单文件的情况,有时候,geotiff 的表示形式,存在多文件的情况。
  3. 可以尝试对配色进行修改,从而调出不同的风格的影像图,这是个很酷的功能。

cesium 直接加载 geotiff 影像图相关推荐

  1. cesium离线加载瓦片影像图和DEM高程图

    最近准备使用Cesium+WPF来进行项目开发,学习了一下Cesium,由于项目需要离线所以先研究一下如何离线使用吧. 首先展示一下效果图: 离线高德影像瓦片地图的加载方法: 1.我使用的是太乐地图下 ...

  2. 第四章 Cesium学习入门之加载离线影像图(tif)

    从0开始的Cesium 第一章 Cesium学习入门之搭建Vite+Vue3+Cesium开发环境 第二章 Cesium学习入门之搭建Cesium界面预览和小控件隐藏 第三章 Cesium学习入门之地 ...

  3. cesium首次加载gltf模型成功

    接此: https://blog.csdn.net/bcbobo21cn/article/details/111305160 根据资料gltf格式模型可以直接在cesium上加载: 网上搜索gltf, ...

  4. cesium 3dtiles 加载本地数据_记一次Cesium地形数据生成过程

    问题描述 有一小块带高程值的点状数据,需要根据该数据生成Cesium支持的3dtiles数据,在Cesium中显示.经过一周多时间的摸索,终于能够在Cesium中加载成功.现将数据处理流程做个记录,以 ...

  5. Cesium Primitives加载大量图标点

    Cesium Primitives加载大量图标点 前言 效果 关键代码 前言 使用entity的方式加载大量图标点会出现卡顿现象,cesium提供了BillboardCollection可以实现大量图 ...

  6. Cesium本地加载地形(dem高程)数据

    cesium本地加载dem数据,首先需要下载地区的高程数据,一般通常在地理空间数据云里面下载:http://www.gscloud.cn/ 这里一般可以下载到90m和30m精度的数据,当然也可以用自己 ...

  7. Cesium|xt3d加载中国地形

    Cesium|xt3d加载中国地形 效果 代码 预览地址 效果 代码 <!DOCTYPE html> <html lang="zh-CN"><head ...

  8. Cesium 无法加载出地球

    Cesium 无法加载出地球 问题描述 Cesium 无法加载出地球,在控制台中看到这样一条报错信息: 401错误,不可用的token值. 解决方案 1.注册一个免费的账户 网址:https://ce ...

  9. Cesium Billboard加载Gif图片

    Cesium Billboard加载Gif图片 实现效果 关键代码 loadGif(img) {this.superGif = new SuperGif({gif: img});this.superG ...

最新文章

  1. spark+openfire即时通讯工具二次开发参考文档
  2. 动态更新 AGS Cache
  3. 效率提升多倍, 推荐值得收藏40 个命令总结
  4. 指定ASP .NET Core Web应用端口
  5. DL之DNN之BP:神经网络算法简介之BP算法/GD算法之不需要额外任何文字,只需要八张图讲清楚BP类神经网络的工作原理
  6. 文件从头开始读函数_如何从头开始编写自己的Promisify函数
  7. display:none和visibility:hidden区别
  8. KubeEdge 1.2.0 部署
  9. Codeforces Round #342 (Div. 2) D. Finals in arithmetic(想法题/构造题)
  10. git命令行常用操作及在linux下push到github项目中遇到的问题
  11. 资金盘FairWin漏洞系统详解:项目方可以撇开“作恶”嫌疑了?
  12. 场景编辑器开发第五天,设计架构重回flash,很多问题不是出在技术上而是策划上
  13. 第二章 信息化规划与组织
  14. Android9设备打开WIFI热点,Android9.0Wifi热点开启流程梳理
  15. EOSIO流服务Dfuse
  16. 【SpringBoot】十八、拦截器 interceptor
  17. 全网最新-扶风视频解析计费系统,2022优化免授权版(赠接口轮询插件)
  18. 如何组织一场安全、可靠、高效的网络实战攻防演习?
  19. 任天堂官网在哪里修改服务器,任天堂服务器设置
  20. ogg转mp3格式转换器

热门文章

  1. 一款非常棒的开源微社区轻论坛类源码
  2. 重磅!上海出落户新政:双一流应届硕士可直接落户!
  3. 图像处理学习笔记(一)
  4. Redisson框架官方介绍
  5. 爱快中的虚拟机不能获取IPV4地址
  6. 我的世界服务器怎么设置自动拾取,自动拾取Auto Pickup Mod
  7. 第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛 G 旋转矩阵(模拟)
  8. 常用二极管IN4148和单片机驱动的一些关系
  9. spring clude ---服务网关组件Netflix Zuul
  10. 全志平台Android系统wifi模组rtl8188eu移植调试记录