Web Dev #WebGL #Web Development #Data Visualization #JavaScript

Deck.gl 大規模 3D 地理數據視覺化框架與 MapLibre 整合實戰

想要在網頁上渲染數以萬計的數據點,同時保持流暢的 3D 互動?Deck.gl 是處理地理空間資料視覺化的絕佳利器。

為什麼選擇 Deck.gl?

在網頁地圖開發中,如果只是加上幾個標記,傳統的 Leaflet 或 Mapbox 已經綽綽有餘。但當你需要渲染數十萬甚至數百萬個數據點,並且需要 3D 視角、流暢的縮放與動畫時,效能往往會成為最大的瓶頸。

Deck.gl 是由 Uber 開源的 WebGL 數據視覺化框架。它專為大規模數據集設計,能夠輕鬆與 Mapbox GL JS 或 MapLibre GL 結合,讓你以極高的效能渲染 3D 散點、路線、多邊形甚至是六邊形熱力圖。它不依賴特定的地圖庫,效能極佳,且支援高度自定義的 Shader。


實作展示 (Live Demo)

下面是我使用 Vue 3 結合 Deck.gl 與 MapLibre 製作的台灣景點 3D 互動地圖,實現了平滑的視角切換與資料渲染:

線上體驗:如果你對這個實作感興趣,可以點擊這裡前往 Live Demo 體驗實際的 3D 互動效果。


如何在你的專案中使用?

1. 安裝套件

你可以使用 npm 或 pnpm 安裝 Deck.gl 以及作為底圖的 MapLibre:

bash
npm install @deck.gl/core @deck.gl/layers maplibre-gl

2. 核心代碼實作

在 Vue 3 或 Nuxt 3 中,我們需要確保程式碼在客戶端執行(因為涉及 Canvas API 與 WebGL)。這裡展示一個基礎的 3D 散點圖配置:

vue
<script setup>
import { onMounted } from 'vue'
import { Deck } from '@deck.gl/core'
import { ScatterplotLayer } from '@deck.gl/layers'
import maplibregl from 'maplibre-gl'
import 'maplibre-gl/dist/maplibre-gl.css'

onMounted(() => {
  // 1. 初始化底圖 (MapLibre)
  const map = new maplibregl.Map({
    container: 'map-container',
    style: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
    center: [121.5654, 25.0330], // 台北 101
    zoom: 11,
    pitch: 45,
  })

  // 2. 準備數據與 Deck.gl 圖層
  const layer = new ScatterplotLayer({
    id: 'scatterplot-layer',
    data: [
      { position: [121.5654, 25.0330], size: 100 }
    ],
    getPosition: d => d.position,
    getFillColor: [255, 140, 0],
    getRadius: d => d.size,
    radiusMinPixels: 5,
    radiusMaxPixels: 50
  })

  // 3. 初始化 Deck.gl 並與底圖同步視角
  new Deck({
    canvas: 'deck-canvas',
    initialViewState: {
      longitude: 121.5654,
      latitude: 25.0330,
      zoom: 11,
      pitch: 45,
      bearing: 0
    },
    controller: true,
    onViewStateChange: ({ viewState }) => {
      // 同步地圖視角
      map.jumpTo({
        center: [viewState.longitude, viewState.latitude],
        zoom: viewState.zoom,
        bearing: viewState.bearing,
        pitch: viewState.pitch
      })
    },
    layers: [layer]
  })
})
</script>

<template>
  <div style="position: relative; width: 100%; height: 600px;">
    <!-- 底圖容器 -->
    <div id="map-container" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>
    <!-- Deck.gl 畫布 -->
    <canvas id="deck-canvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;"></canvas>
  </div>
</template>

核心圖層 (Layers) 說明

Deck.gl 提供了豐富的內建圖層讓你調整地圖資料的外觀:

圖層名稱適用場景視覺效果
ScatterplotLayer點狀數據分佈2D 圓點,可依據數據調整半徑與顏色
PathLayer路線與軌跡繪製有粗細的折線,適合路線規劃
PolygonLayer行政區劃、建築物可將多邊形數據擠壓成 3D 柱狀體 (Extruded)
HexagonLayer密集數據的熱力圖將海量點數據聚合為 3D 六邊形柱體
GeoJsonLayer通用 GeoJSON 渲染結合了點、線、面的複合式圖層

個人心得

在使用 Deck.gl 開發互動地圖時,最大的感受是它對於海量數據的處理能力真的無可挑剔

如果你只是想為你的專案加入幾個地圖標記,使用一般的地圖庫就足夠了。但如果像我一樣,想要在地圖上繪製具有強烈「科技感」與「專業感」的 3D 地理資訊,同時又不能犧牲畫面幀數,Deck.gl 的 CP 值真的非常高。雖然學習如何與底圖完美同步視角需要一點時間,但只要掌握了核心概念,幾十行程式碼就能渲染出驚艷的視覺效果。

小建議:Deck.gl 通常與 Mapbox 配合得最好,但為了避免 Mapbox 的高額收費與 token 限制,強烈建議使用完全開源的 MapLibre GL JS 作為底圖替代方案!


相關連結:


本文部分底層整合邏輯參考自 Deck.gl 官方文件,並針對 Vue 3 環境進行組件化封裝。