3D GIS 應用開發 —— 基於 Mapbox GL 的實踐

創宇前端發表於2018-01-11

最近在折騰的 web 端的視覺化專案,由於相關業務的需要,用到了 Mapbox 這一地圖開發的神器。在此先奉上一個基於mapbox-gl實現的demo短片(來源:uber的deck.gl專案):

3D GIS 應用開發 —— 基於 Mapbox GL 的實踐

下面我們從這個專案一步步來介紹 Mapbox 的前端 GIS 引擎 Mapbox GL JS.

一、簡單瞭解

首先,Mapbox 在地圖領域是一家很 Newbee 的公司。已為 Foursquare、Pinterest、Evernote、金融時報、天氣頻道、優步科技 等公司的網站提供了訂製線上地圖服務。

自2010年起,該公司快速地擴充了訂製地圖的市場地位,以迴應 Google地圖 等地圖供應商提供的有限選擇。Mapbox 是一些開放原始碼地相簿及應用程式的建立者或最大的貢獻者,其中包含了MBTiles 規範、TileMill 製圖 IDE、Leaflet JavaScript 庫,以及 CartoCSS 地圖格式化語言與語法分析器等。

該公司的資料同時從開放與專有的來源獲取,開放的資料來源如 開放街圖(OSM, Open Street Map) 以及 NASA 等,而專有的資料來源則包含了 DigitalGlobe。其技術奠基於 Node.js、CouchDB、Mapnik、GDAL 與 Leafletjs。

3D GIS 應用開發 —— 基於 Mapbox GL 的實踐

Mapbox 針對不同平臺均開發了相應的 GIS 引擎以滿足開發者或相關使用者的需要,如:iOS SDK(用於iOS端開發)、Android SDK(用於Andriod端開發)、Navigation SDK(用於Navigation端開發)、Unity SDK(用於Unity端開發)、GL JS(用於web端開發)。不同平臺的SDK,除使用方式不同外,功能特性上也多多少少存在不同。此外,Uber還針對react開發了 react-map-gl。總的來說,Mapbox的開源技術棧是非常全面的。

二、輕鬆上手

3D GIS 應用開發 —— 基於 Mapbox GL 的實踐

mapbox-gl 的 文件 由 API、Style Specification、Example、Plugins 四部分內容組成。

顧名思義,API 是一般框架(類庫)提供給使用者的全部介面(方法)的說明書;Style Specification 是 Mapbox 地圖的樣式規範;Example 是一些常用功能或常見業務的程式碼示例,囊括了使用 Mapbox 所能實現的大部分功能效果;Plugins 則是官方推薦的可與 mapbox-gl 一同使用的一些增效外掛和開源專案,如一些第三方的UI控制元件、顯示類外掛、框架整合工具、開發輔助工具、實用工具類庫等等。

對於初瞭解 Mapbox 的童鞋,建議先從官網的 Example 入手,能夠較快掌握 mapbox-gl 的使用並投入開發實踐。

三、快速實踐

下面以文章開頭展示的專案為主,介紹其實戰步驟。

1. 載入地圖:

由於使用線上地圖服務和 style 時需要驗證使用者 token,所以在使用 mapboxgl 時需要先配置使用者 token(在Mapbox官網註冊使用者即可獲取)。

import mapboxgl from 'mapbox-gl';
mapboxgl.accessToken = '<Your Token Here>';
複製程式碼

接下來使用建立地圖例項。主要配置項如下:

const myMap = new mapboxgl.Map({
  container: '<Id of Container Element>',
  style: '<Your Style Here>',
  center: [112.508203125, 37.97980872872457],
  zoom: 4,
  pitch: 0,
  bearing: 0,
});
複製程式碼

其中,container 是地圖容器的元素 idstyle 是地圖樣式的 url,或者你自己定義的 style(需遵循Mapbox樣式規範),center 是地圖載入後預設的中心點位置,用以定位地圖載入時的位置。zoom pitch bearing 分別指縮放級別、地面法線偏移角、地軸偏移角等,用以確定當前視窗所顯示的地圖區域和空間關係。配置項的意義均可檢視官網文件。

2. 繪製圖形

這裡主要介紹視訊中的3D建築、飛線動畫等是如何實現的。這裡以相關程式碼片段來介紹實踐的方法。

在Mapbox中繪製圖形時, layersource 是最重要的一組概念,後者用於儲存圖形的資料內容,前者則是圖形在3D場景中的表現(圖層)。在Mapbox中,圖層一旦被建立,與其同名(id相同)的資料來源源(即source)也必然被建立。反之,也可以在建立source後再建立一個圖層使用這個已建立的資料來源,這時資料來源與圖層間並不要求同名。而我們通過改變資料來驅動圖形變化,便是才去的第二種方式:

// 建立id為buildings的資料來源
myMap.addSource('buildings', {
  type: 'geojson',
  data: '<GeoJson Contents>',
});

// 使用buildings的資料來繪製id為building_layer的圖形
myMap.addLayer({
  id: 'building_layer',
  type: 'fill-extrusion',
  source: 'buildings',
  ...<Other Options>,
});
複製程式碼

基於上面的方式,當資料改變時,我們只需要重設資料來源的資料,即可驅動圖層重繪:

if (myMap.getLayer('building_layer')) {
  myMap.getSource('buildings').setData(<New GeoJson Contents>);
}
複製程式碼

至於3D效果及動畫的具體實現,這裡給出兩個官網上的示例,相信大家能一目瞭然:

* i. 用3D形式呈現建築

* ii. 給路徑中的一個點新增動畫效果

3. 圖形互動

Mapbox提供的互動方法是比較靈活的,活學活用API文件便能實現各種炫酷、實用的互動效果。比如:使用 myMap.on('zoom', callback) 可以將圖形與地圖的縮放相繫結,當縮放係數小於某個值時,可以隱藏掉一些圖形元素:

myMap.on('zoom', () => {
  if (myMap.getZoom() <= 4) {
    myMap.setLayoutProperty('building_layer', 'visibility', 'none');
  } else {
    myMap.setLayoutProperty('building_layer', 'visibility', 'visible');
  }
});
複製程式碼

再比如,連續呼叫 myMap.flyTo() 的方法使檢視在地圖上按照一定的軌跡緩慢移動,可以給使用者一種模擬飛行的體驗。視訊中的自動巡視的效果正是這樣實現的。

諸如 click mouseover popup 等效果,官網文件中的示例已經具體呈現,這裡就不詳細展開了。

4. tiles-server的本地化

由於 Mapbox 地圖服務使用 MBTiles 儲存資料,目前很多地圖服務都接受了這套標準(如:OSM,Open Street Map)。所以可以通過搭建自己的 tiles-server 以替代直接使用 Mapbox 的線上地圖服務。

這樣做的好處是顯而易見的:一是可以通過負載均衡等手段提高資料介面的訪問速度,有效提高資料的載入速度;一是保障應用能在零頻寬的環境下仍能有效部署和使用。

這裡牆裂安利一個docker開源映象:openmaptiles-server ,在其 官網dockerhub 上均可下載。個人認為其最大的亮點在於——即使不瞭解內部實現,也不影響其使用。

3D GIS 應用開發 —— 基於 Mapbox GL 的實踐

執行 tiles-server 服務的 docker 命令如下:

$ docker run --rm -it -v $(pwd):/data -p 8080:80
複製程式碼

然後剩下來需要做的事情就是開啟其導航頁面 http://localhost:8080/(埠號取決於你的啟動命令),然後跟著頁面上的提示一步一步設定就好了(最後一步設定後會從OSM走動下載地圖,所以一開始你不用擔心資料從哪來),完全是傻瓜式的部署。

四、效能調優

在 Mapbox GL 實踐的過程中,發現了一些影響應用整體效能的因素,故而在此陳述一番,為之後填坑的童鞋提供一些經驗:

  1. 使用geo資料(如 GeoJson 格式資料)來定義圖形的時候,若資料量過大,則會拖慢資料載入的速度,此時可考慮:
  • i. 在 http 請求前後對資料進行合理的壓縮和解壓,以儘可能節省 http 請求傳輸的資料量

  • ii. 條件允許的情況下,可將一組資料分片載入,以空間換時間

  1. 在 Mapboox 中繪製的圖層不宜過多,一是不方便管理(當然,github上有很多管理Mapbox圖層的第三方工具),一是圖層過多會明顯降低GL的渲染和響應效能。所以在繪製圖形前,可以先考慮一下圖層的劃分,以最少的圖層實現儘可能多的效果。
  1. 資料量相同的情況下,使用 mapboxgl.Marker 來新增標記,其效能不如使用 typesymbol 型別的圖層來新增標記。原因在於前者生成的標記是一個個 DOM 元素,如果你可以想象在一個 web 頁面中同時操作成百上千個 DOM 節點會是什麼結果,那麼你或許能明白我的建議。

五、一點總結

最後,在此總結下個人對 Mapbox 的一些感觀。

Mapbox 的產品定位是隨時隨地的 GIS(跨平臺、應用),它為我們提供了一系列的簡單操作的 API,使得 GIS 開發變得靈活而有趣。尤其對於開發 GIS 型別的資料視覺化應用,Mapbox 是絕佳的選擇。

然而,如果你只是為了那些絢麗的 3D 效果的話,或許選擇專門的框架更為合適。


關注微信公眾號:創宇前端(KnownsecFED),碼上獲取更多優質乾貨!

3D GIS 應用開發 —— 基於 Mapbox GL 的實踐

相關文章