mapboxgl加载tiff

缘起近期在项目中遇到这么一个需求,需要在地图上展示一组格网数据,格网大小为2m*2m,地图api用的mapboxgl 。起初拿到这个需要感觉很easy,在地图上添加一个fill图层就好啦 。把格网面数据添加到地图上之后,在大比例尺下显示正常,但是当地图层级小于15级时,渲染出的结果会消失 。

mapboxgl加载tiff

文章插图
简单理一下原因,应该是在地图缩小后,每个网格所占的像素太小,所以就消失了 。
mapboxgl在处理symbol图层的时候,会遇到点位自动避让问题,导致部分点位不显示 。解决方法是把layout中的icon-allow-overlap设置为true,这样就相当于关闭了自动避让功能,所有点图标保持可见状态 。但是针对fill图层却没有这么一个属性 。
但是这种情况又需要查看数据,要如何实现呢?
首先分析下数据,我的原始数据是通过模型导出的tiff格式的栅格数据,然后在后台根据tiff格式数据中每个像素所在行列号以及其灰度值生成带属性的格网数据,其中像素的灰度值就是在渲染时需要分类展示的值 。既然原始tiff数据的灰度值就是所用的属性值,那是不是直接添加到地图就好了 。接下来的解决方案就是看是否能直接用mapboxgl直接加载tiff数据,并渲染出自己想要的效果 。
mapboxgl加载tiff查看mapboxgl文档,可以看到mapboxgl支持image图层,只需传入url和coordinates
// 添加至地图map.addSource('some id', {type: 'image',url: 'https://www.mapbox.com/images/foo.png',coordinates: [[-76.54, 39.18],[-76.52, 39.18],[-76.52, 39.17],[-76.54, 39.17]]});可是,当我把地址换成tiff数据时却报错了 。下面为报错内容:
Could not load image because of The source image could not be decoded.. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported
可以简单理解为不支持tiff格式 。
tiff文件解析既然mapboxglimage图层不支持tiff格式,那是不是可以把tiff数据导出成png呢,于是使用arcmap打开了tiff数据,导出数据格式也支持png,但是在保存时又报错了 。
mapboxgl加载tiff

文章插图
【mapboxgl加载tiff】经过分析,发现是tiff数据波段数量的原因,我的这份数据波段数为1,从网上下载了一份测试数据,波段数为3,可以成功导出 。
mapboxgl加载tiff

文章插图
在查找相关解决方案的时候,看到这么个工具,geotiff.js,可以通过js解析tiff数据并渲染,leaflet有个扩展就是用的这个工具,https://github.com/stuartmatthews/leaflet-geotiff 。查看geotiff.js相关文档,发现其实用起来还是挺方便的,通过简单的代码实现的我的需求 。
先使用geotiff.js解析tiff数据,再配合使用canvas绘制图片导出base64格式数据,然后就可以使用添加到mapboxgl图层了 。
核心代码如下:
async function getData() {GeoTIFF.fromUrl(url).then(tiff => {console.log(tiff)getImage(tiff)});}async function getImage(tiff) {const image = await tiff.getImage();let bbox = await image.getBoundingBox();let data = https://www.huyubaike.com/biancheng/await image.readRasters({samples: rgbBands // 波段数量,一个波段:[0],三个波段:[2,1,0]});let base64Image = getBase64Image(data)addToMapboxgl(base64Image)}function getBase64Image(data) {let thumbnailPixelHeight = data.heightlet thumbnailPixelWidth = data.widthlet canvas = document.createElement('canvas')canvas.width = thumbnailPixelWidthcanvas.height = thumbnailPixelHeightlet ctx = canvas.getContext("2d")let totalPixelCount = 0for (let y = 0; y < thumbnailPixelHeight; y++) {for (let x = 0; x < thumbnailPixelWidth; x++) {let colour = 'rgb(0, 0, 0, 0)' // let the default be no data (transparent)// 根据灰度值所在范围渲染颜色if (data[0][totalPixelCount] > 0) {if (data[0][totalPixelCount] > 50 && data[0][totalPixelCount] <= 55) {colour = `rgb(15, 255, 0, 1)`} else if (data[0][totalPixelCount] > 55 && data[0][totalPixelCount] <= 60) {colour = `rgb(155, 255, 0, 1)`} else if (data[0][totalPixelCount] > 60 && data[0][totalPixelCount] <= 65) {colour = `rgb(255, 255, 0, 1)`} else {colour = `rgb(255, 255, 0, 1)`}}ctx.fillStyle = colourctx.fillRect(x, y, 1, 1)totalPixelCount++}}let canvasImage = canvas.toDataURL("image/png")return canvasImage}// 将图片添加到地图function addToMapboxgl(image) {map.addSource('tiff-source', {"type": "image","url": image,"coordinates": [[114.425597191307, 38.1091563484708],[114.538187627939, 38.1091563484708],[114.538187627939, 37.9627378349512],[114.425597191307, 37.9627378349512]]});map.addLayer({id: 'tiff-layer','type': 'raster','source': 'tiff-source','paint': {'raster-fade-duration': 0}});}

经验总结扩展阅读