前端界面中显示GeoTIFF文件
GeoTiff VS TIFF
TIFF文件适用于在应用程序之间和计算机平台之间的交换文件,它的出现使得图像数据交换变得简单。
- TIFF保存的文件比较大,而且也是许多印刷品选择保存的格式。
- TIFF它保存的图失真度极小,而且TIFF格式可以保存分层和透明信息。
GeoTIFF 是对TIFF格式的一种扩展, 在其基础上定义了一些GeoTag(地理标签)。
- 各种坐标系统;
- 椭球基准;
- 投影信息等;
进行定义和存储, 使图像数据和地理数据存储在同一图像文件中, 这样就为广大用户制作和使用带有地理信息的图像提供了方便的途径。
前端显示TIFF格式
对于前端显示TIFF格式,通常的思路有:
- 转换为Base64注入到img中;
- 进行格式转换;
转换TIFF为Base64
进行该项操作通常使用tiff.js
库
访问地址为:https://github.com/seikichi/tiff.js
该库提供了基本的TIFF前端显示方法,不过也有一些问题。
- 比如文件的大小有限制,若文件大小超出10MB,需要其他处理;
- 部分版本有内存泄露的问题;
- 该库的最后一次更新,在2017年。
官网给出的例子如下:
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', "url/of/a/tiff/image/file.tiff");
xhr.onload = function (e) {
var tiff = new Tiff({buffer: xhr.response});
var canvas = tiff.toCanvas();
document.body.append(canvas);
};
xhr.send();
其含义为,以arraybuffer的形式接受文件数据,并通过Canvas进行绘制,再插入到页面中。但是通常不满足大家的需求。
解决方案
基于HTML的方案
由于前文提及的img支持Base64可知,我们可以改造代码的使用方式,转为直接使用将TIFF文件转换后的Base64传递给img的src。
1)首先引入tiff库
<img style="width: 500px; height: 500px;" alt="文件为TIFF格式">
<script src="js/tiff.min.js"></script>
2)在script中插入如下代码
window.onload = function () {
getTiffData()
}
async function getTiffData () {
const response = await fetch('img/XXX.tif')
const buffer = await response.arrayBuffer()
const tiff = new Tiff({ buffer })
const img = document.querySelector('img')
img.src = tiff.toDataURL()
}
基于Vue的方案(本例使用Vue2)
1)引入tiff.js
// pnpm
pnpm i tiff.js
// npm (推荐)
npm install tiff.js
2)在合适的位置加入如下代码:
<template>
<div class="tiffView">
<img :src="dataUrl" style="width: 500px; height: 500px;" alt="">
</div>
</template>
import Tiff from 'tiff.js'
export default {
name: 'Tiff',
data () {
return {
dataUrl: ''
}
},
mounted () {
this.getTiffData()
},
methods: {
async getTiffData() {
const response = await fetch('@assets/img/xxx.tif')
const buffer = await response.arrayBuffer()
const tiff = new Tiff({ buffer: buffer })
this.dataUrl = tiff.toDataURL()
}
}
不过该方法在简单案例中运行通过,但是在实际项目中出现找不到fs或其他对象的报错。若您解决该问题,欢迎评论留言指导。
进行格式转换
可以使用使用photoshop
类图片处理工具,进行格式转换,将TIFF格式转换为适合的(常规的)图片格式进行处理,例如png、jpg等。
至于png、jpg的显示就不在赘述。
若项目中,必须使用TIFF格式,请参考前一方案。
当然若后端的童鞋善心大发可以帮你完成格式转换记得请他/她喝奶茶。
接下来就是GeoTIFF的问题了。
前端界面中显示GeoTIFF
由前文可知,GeoTIFF比TIFF文件多了地理信息类的标签,并且通道数也多了不少,并且体积也非常大。
因此在前端显示GeoTIFF简直是困难重重。
基本思路如下:
- 在WebGIS中显示GeoTIFF;
- 通过转换gdal-js完成数据转换为Base64;
- 通过Python/Java 使用gdal进行图片转换;
在WebGIS中显示GeoTIFF(本文使用Leaflet,其他WebGIS应当具有类似功能)
Leaflet是前端项目中常用的WebGIS框架,完成普通的大屏背景图等较为方便,且本身小巧。其功能的扩充依赖插件,总体来说还是不错的。
https://leafletjs.com/
在Leaflet官网的plugins页面中选择Overlay data formats
,并使用control+F
查询geotiff
。
可以实现该功能的插件主要有:
- GeoRasterLayer
- Leaflet.CanvasLayer.Field
- leaflet-geotiff
我主要使用第三个,其demo链接为https://stuartmatthews.github.io/leaflet-geotiff/
从插件3的github项目readme.md中可以得到详细的解释https://github.com/stuartmatthews/leaflet-geotiff
核心代码为:
const layer = L.leafletGeotiff(url, options).addTo(map);
其中的url为GeoTIFF的地址,options为配置参数。
对于GeoTIFF的配置重点在与renderer
对象,比如让我深恶痛绝的配置色标colorScale。(若默认色标满足你的要求,简直就太棒了,否则需要阅读Plotty代码,创建自己的色标)。
引入plotty后,根据自己的GeoTIFF图的数值分布,创建色标。我个人主要通过Python生成指定范围的颜色序列来逐步调试,如果有更优的办法,请评论指导。
plotty.addColorScale(
"myscale",
['#3aff00', '#4aff00', '#5bff00', '#6bff00', '#7cff00', '#8cff00', '#9dff00', '#adff00', '#bdff00', '#ceff00', '#deff00', '#efff00', '#ffff00', '#ffea00', '#fed500', '#fec000', '#feaa00', '#fd9500', '#fd8001', '#fd6b01', '#fc5601', '#fc4101', '#fc2b01', '#fb1601', '#fb0101'],
[0.0, 0.0020833333333333333, 0.004166666666666667, 0.00625, 0.008333333333333333, 0.010416666666666666, 0.0125, 0.014583333333333334, 0.016666666666666666, 0.01875, 0.020833333333333332, 0.022916666666666665, 0.025, 0.027083333333333334, 0.029166666666666667, 0.03125, 0.03333333333333333, 0.035416666666666666, 0.0375, 0.03958333333333333, 0.041666666666666664, 0.04375, 0.04583333333333333, 0.04791666666666666, 0.05])
基于Leaflet的实现
加载的核心代码如下:
const plottyRenderer = L.LeafletGeotiff.plotty({
displayMin: 0.005,
displayMax: 10,
clampLow: false,
clampHigh: false,
colorScale: "myscale",
// noDataValue: 0.0001,
});
this.layer = L.leafletGeotiff(tifUrl, {
renderer: plottyRenderer,
opacity: 0.4,
});
this.layer.addTo(this.map)
由于GeoTIFF文件携带有坐标系等信息,因此可以直接投到地图上。至此基于WebGIS框架的显示就完成了。
通过转换gdal-js完成数据转换为Base64
gdal-js是大佬Derek Dohler的作品,是gdal库的js版本实现。
感受一下大佬的更新频率。
https://github.com/ddohler/gdal-js
基于Vue的实现(Vue2)
1)安装gdal-js
pnpm i gdal-js
2)Vue中的实现
import gdal from 'gdal-js'
const geoData=gdal.open('@assets/img/xxx.tif')
const data=ge0Data.bands.map(band=>band.pixels.read(0,0,geoData.rasterSize.x,geoData.rasterSize.y))
const this.base64=btoa(String.fromCharCode(...new Unit8Array(data))
base64变量存储图片显示所需的代码,绑定如下:
<img :src="'data:image/tiff;base64,'+base64">
通过Python/Java 使用gdal进行图片转换
在Python中使用gdal进行GeoTIFF数据的读取,gdal包含在OSGeo库中,请自行安装。
# 选择版本
conda/pip search gdal
# conda
conda install gdal
# pip
pip install gdal
# 若安装不成功,请在安装指令后附加版本
如果安装出现问题https://www.lfd.uci.edu/~gohlke/pythonlibs/
,可在上述路径查找预先编译的轮子。
pip install GDAL-3.3.3-cp39-cp39-win_amd64.whl
通常在GeoTIFF中有多个通道,具体使用哪些通道,需要查看影像资料。例如在GF-2影像中选择波段[4,3,2]生成png。
from osgeo import gdal
def tif2png(sourcePath,targetPath):
options=gdal.TranslateOptions(format='PNG',bandList=[4,3,2])
gdal.Translate(targetPath,sourcePath,options=options)
若需要了解options中更多的设置,请自行查阅translateoptions文件源码。
总结
GeoTIFF和TIFF本质上是不同的两种文件。
因此在处理方式上需要区别处理。WebGIS方向的童鞋需要更多的了解GeoTIFF格式。前端开发的同学可能遇到TIFF格式的概率会大一些。
虽然代码都经过验证,但是难免个人理解有偏差,若有错误,请在评论区留言告知。如有更方便的方法,也请告知。致谢。