緣起
近期在專案中遇到這么一個需求,需要在地圖上展示一組格網資料,格網大小為2m*2m,地圖api用的mapboxgl,起初拿到這個需要感覺很easy,在地圖上添加一個fill圖層就好啦,把格網面資料添加到地圖上之后,在大比例尺下顯示正常,但是當地圖層級小于15級時,渲染出的結果會消失,

簡單理一下原因,應該是在地圖縮小后,每個網格所占的像素太小,所以就消失了,
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檔案決議
既然mapboxgl的image圖層不支持tiff格式,那是不是可以把tiff資料匯出成png呢,于是使用arcmap打開了tiff資料,匯出資料格式也支持png,但是在保存時又報錯了,

經過分析,發現是tiff資料波段數量的原因,我的這份資料波段數為1,從網上下載了一份測驗資料,波段數為3,可以成功匯出,

在查找相關解決方案的時候,看到這么個工具,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.cnblogs.com/gisarmory/p/await image.readRasters({
samples: rgbBands // 波段數量,一個波段:[0],三個波段:[2,1,0]
});
let base64Image = getBase64Image(data)
addToMapboxgl(base64Image)
}
function getBase64Image(data) {
let thumbnailPixelHeight = data.height
let thumbnailPixelWidth = data.width
let canvas = document.createElement('canvas')
canvas.width = thumbnailPixelWidth
canvas.height = thumbnailPixelHeight
let ctx = canvas.getContext("2d")
let totalPixelCount = 0
for (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 = colour
ctx.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
}
});
}
本以為到這里問題已經解決,但是在查看地圖時,發現圖片圖層資料疊加到底圖有不小的偏移,
經過一番對比分析,發現原來是tiff資料的坐標系與地圖坐標系不一致的導致的,我拿到的tiff資料坐標系為西安80的投影坐標系,在展示時配置的為wgs84地理坐標系,所以會有偏差,既然是坐標系問題,那就通過工具對tiff檔案做下投影轉換,這里用的是arcmap,打開ArcToolbox–>Data Management Tools–>Projections and Transformations–>Raster–>Project Raster

轉換之后會發現,資料的行列值也會發生變化,也就是tiff圖片的大小和形狀都有所變化,
轉換前:

轉換后:

使用轉換后的資料再次決議,然后疊加到地圖,位置完全匹配,
最終展示方案
通過嘗試發現,單獨的圖片展示時,由于圖片解析度固定,當地圖等級放大到一定程度圖片會被放大很多導致圖片模糊不清,展示效果不理想;單獨的格網面展示時,當地圖等級縮小到一定程度,面圖層則會消失,也就是文章開頭提到的問題,
綜上,根據自己的格網資料大小,判斷在哪個等級格網面資料會消失,小于這個等級使用圖片展示,大于這個等級用格網面展示,就可以完美的展示出想要的效果,
處理前效果:

處理后效果:

以上為有
tiff柵格資料情況的解決方案,針對于只有格網面資料,而沒有tiff柵格資料的情況要怎么解決呢?如果在這組格網資料中,每個網格的屬性中有他所在原始
tiff資料的像素位置,以及原始tiff資料像素大小,就可以寫一個類似上文中的getBase64Image方法,遍歷每個網格,在網格對應的像素位置上繪制顏色,然后再通過canvas匯出圖片添加到地圖,
總結
mapboxgl的image圖層無法直接添加tiff柵格資料mapboxgl添加fill圖層時,地圖層級縮小到一定程度,面資料所占像素值過小無法顯示tiff資料可以使用geotiff.js+canvas決議,得到base64的圖片,添加到mapboxgl的image圖層- 在決議
tiff資料時,需注意它的坐標系、波段個數等資訊 - 在做展示時可以
image圖層和fill圖層結合展示,效果較好
參考資料:
- https://geotiffjs.github.io/geotiff.js/
- https://github.com/stuartmatthews/leaflet-geotiff
- https://www.cnblogs.com/arxive/p/6746570.html
原文地址:http://gisarmory.xyz/blog/index.html?blog=mapboxgl-geotiff
歡迎關注《GIS兵器庫》

本文章采用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可,歡迎轉載、使用、重新發布,但務必保留文章署名《GIS兵器庫》(包含鏈接: http://gisarmory.xyz/blog/),不得用于商業目的,基于本文修改后的作品務必以相同的許可發布,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/509813.html
標籤:JavaScript
上一篇:Java Script 回圈,陣列,物件,判斷,階乘,查找-綜合運用合集
下一篇:Optional原始碼決議與實踐
