mapboxgl載入tiff

来源:https://www.cnblogs.com/gisarmory/archive/2022/09/29/16740563.html
-Advertisement-
Play Games

一、項目優化 1.去掉列印console 需求:在開發環境中,保留列印console;在生產上線環境,自動去掉列印console 使用步驟: 第一步:在項目根目錄下,創建如下圖兩個配置文件 在.env.development中(開發環境變數) NODE_ENV=development 在.env.p ...


緣起

近期在項目中遇到這麼一個需求,需要在地圖上展示一組格網數據,格網大小為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文件解析

既然mapboxglimage圖層不支持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 = 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導出圖片添加到地圖。

總結

  1. mapboxglimage圖層無法直接添加tiff柵格數據
  2. mapboxgl添加fill圖層時,地圖層級縮小到一定程度,面數據所占像素值過小無法顯示
  3. tiff數據可以使用geotiff.js+canvas解析,得到base64的圖片,添加到mapboxglimage圖層
  4. 在解析tiff數據時,需註意它的坐標系、波段個數等信息
  5. 在做展示時可以image圖層和fill圖層結合展示,效果較好

參考資料:

  1. https://geotiffjs.github.io/geotiff.js/
  2. https://github.com/stuartmatthews/leaflet-geotiff
  3. 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/),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JavaScript排序 — sort()方法 ——解決null、undefined、0之間的排序(混亂)問題 一、普通的數組排序 ​ JavaScript中用方法sort()為數組排序。sort()方法有一個可選參數,是用來確定元素順序的函數。如果這個參數被省略,那麼數組中的元素將按照ASCII字 ...
  • ##vue路由守衛用於登錄驗證許可權攔截 ###vue路由守衛 - 全局(router.beforeEach((to, from, next) =>來判斷登錄和路由跳轉狀態) ###主要方法: to:進入到哪個路由去 from:從哪個路由離開 next:路由的控制參數,常用的有next(true)和n ...
  • LRU 是 Least Recently Used 的縮寫,即最近最少使用。作為一種經典的緩存策略,它的基本思想是長期不被使用的數據,在未來被用到的幾率也不大,所以當新的數據進來時我們可以優先把這些數據替換掉。 一、基本要求 固定大小:限制記憶體使用。 快速訪問:緩存插入和查找操作應該很快,最好是 O ...
  • 一、前言 前段時間碰到了一個 Keybinding 相關的問題,於是探究了一番,首先大家可能會有兩個問題:Monaco Editor 是啥?Keybinding 又是啥? Monaco Editor: 微軟開源的一個代碼編輯器,為 VS Code 的編輯器提供支持,Monaco Editor 核心代 ...
  • 語法&關鍵字與保留字 本章篇幅較長故分成幾個小節來講 語法 區分大小寫 這個沒啥好講的,a和A是兩個變數。 標識符 標識符,就是變數、函數、屬性或函數參數的名稱。 標識符的組成規範,如下: 第一個字元必須是一個字母、下劃線( _ )或者美元符號( $ ); 剩下的其他字元可以使字母、下劃線、美元符號 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 第一問:安全類型檢測——typeof和instanceof 區別以及缺陷,以及解決方案 這兩個方法都可以用來判斷變數類型 區別:前者是判斷這個變數是什麼類型,後者是判斷這個變數是不是某種類型,返回的是布爾值 (1)typeof 缺陷: 1 ...
  • 隨著NFC讀寫器在BS架構下的需求越來越多,使用JS語言在web瀏覽器下操作NFC讀寫器就變得尤其重要.但是web瀏覽器不允許其顯示內容直接操作硬體,所以我們必須使用IC卡讀卡器web插件來實現這個功能.作為web前端工程師,我們首先要瞭解在web中實現操作NFC讀寫器的步驟:1、下載友我科技IC卡 ...
  • NullPointerException在開發過程中經常遇到,稍有不慎小BUG就出現了,如果避免這個問題呢,Optional就是專門解決這個問題的類,那麼Optional如何使用呢?讓我們一起探索一下吧! ...
一周排行
    -Advertisement-
    Play Games
  • public static void GetRegistData() { string name = "huishuangzhu"; //搜索到註冊表根目錄 RegistryKey hkml = Registry.ClassesRoot; //搜索到註冊表根目錄下的XXX文件夾。 RegistryK ...
  • 用acme.sh自動部署功能變數名稱證書 安裝ACME 目前使用量最大的免費SSL證書就是Let’s Encrypt,自2018-03開始,Let’s Encrypt官方發佈上線了免費的SSL泛功能變數名稱證書,目前通過DNS方式獲取比較快,國內可以通過鵝雲的DNSPod功能變數名稱API或者貓雲功能變數名稱API自動簽發Let’ ...
  • 經常看到有群友調侃“為什麼搞Java的總在學習JVM調優?那是因為Java爛!我們.NET就不需要搞這些!”真的是這樣嗎?今天我就用一個案例來分析一下。 昨天,一位學生問了我一個問題:他建了一個預設的ASP.NET Core Web API的項目,也就是那個WeatherForecast的預設項目模 ...
  • 1、環境搭建 1.1 依賴 <!-- nacos註冊中心 註解 @EnableDiscoveryClient --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba- ...
  • ULID:Universally Unique Lexicographically Sortable Identifier(通用唯一詞典分類標識符) UUID:Universally Unique Identifier(通用唯一標識符) 為什麼不選擇UUID UUID 目前有 5 個版本: 版本1: ...
  • 虛基類/抽象類 抽象類:有純虛函數的類 虛繼承 通過修飾繼承方式, 如代碼2是虛繼承,被虛繼承的類稱為虛基類 虛繼承派生類的記憶體佈局方式 先是vbptr => 派生類的數據 =>基類的數據 , 對比代碼1和代碼2,發現原本基類數據在前面,派生類數據在後面,但是在虛繼承的時候 基類數據方式放到了後面, ...
  • 下麵給出 Kafka 一些重要概念,讓大家對 Kafka 有個整體的認識和感知,後面還會詳細的解析每一個概念的作用以及更深入的原理 • Producer:消息生產者,向 Kafka Broker 發消息的客戶端。 • Consumer:消息消費者,從 Kafka Broker 取消息的客戶端。 • ...
  • 前面介紹了對稱加密演算法,本文將介紹密碼學中另一類重要應用:消息摘要(Digest),什麼是消息摘要?簡單的定義是:對一份數據,進行一個單向的Hash函數,生成一個固定長度的Hash值,這個值就是這份數據的摘要,也稱為指紋。 ...
  • 弟弟最近要考試,臨時抱佛腳在網上找了一堆學習資料複習,這不剛就來找我了,說PDF上有水印,影響閱讀效果,到時候考不好就怪資料不行,氣的我差點當場想把他揍一頓! 算了,弟弟長大了,看在打不過他的份上,就不打他了~ 稍加思索,我想起了Python不是可以去水印?說搞就搞! 去除水印原理 去除方法: 用 ...
  • 作者:陳昌浩 1 導讀 if…else…在代碼中經常使用,聽說可以通過Java 8的Function介面來消滅if…else…!Function介面是什麼?如果通過Function介面介面消滅if…else…呢?讓我們一起來探索一下吧。 2 Function介面 Function介面就是一個有且僅有 ...