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
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...