用開放資料,感受臺灣的脈動——Mini Taiwan Pulse

用開放資料,感受臺灣的脈動——Mini Taiwan Pulse







網友 Migu 開發的 Mini Taiwan Pulse,是一項結合開放資料、即時資訊與三維地圖視覺化的開源專案。天空中的航班劃出一道道弧形光軌,海面上的船舶穿梭往返,鐵道列車依照時刻表在島嶼上奔馳,臺北與新北市的公車也化為移動光點,讓原本分散於不同平台與資料庫中的交通資訊,匯聚在同一幅 3D 臺灣地圖上。

這個專案不只是呈現交通工具的位置與軌跡,也整合機場、港口、車站、燈塔、公路、自行車道、國道壅塞、氣象觀測、人口與人流、地震及災害示警等多元圖層。使用者可以自由切換圖層、調整三維效果,並透過時間軸觀察不同時段的變化。專案運用 Mapbox GL JSThree.jsH3 六角網格等技術,把抽象的座標、時刻表與即時感測資料,轉化為具有節奏與生命感的動態景觀。

Mini Taiwan Pulse 展現了開放資料不只是表格與檔案,更能成為理解臺灣社會運作的窗口。透過流動的光點與軌跡,使用者可以直觀看見人流、物流與交通網絡如何交織,感受這座島嶼從天空、海洋到地面,每一刻都在持續跳動的脈搏。專案原始碼亦公開於 GitHub,讓更多開發者得以研究、交流及延伸應用。

https://mini-taiwan-pulse.itsmigu.com/

https://github.com/ianlkl11234s/mini-taiwan-pulse

 

Mini Taiwan Pulse

用開放資料,感受台灣的脈動。

天空中的航班劃出弧線、海面上的船舶穿梭往返、軌道上的列車準時奔馳這座島嶼每一刻都在呼吸。Mini Taiwan Pulse 將這些交通運輸的即時動態,以 3D 光球、光軌、拖尾線呈現在同一張地圖上,讓你看見台灣的脈搏。

Screenshots

全台總覽 — 航班・船舶・軌道・燈塔・風場 全圖層展開 — 車站光柱・公車站・國道省道 北台灣近景 — 3D 軌道・列車光球・車站光柱 南台灣 — H3 人口密度 3D 柱狀圖・國道 衛星底圖 — 公車站・軌道・自行車道

三種脈動

脈動

視覺呈現

開放資料來源

天空航班

3D 弧線 + 光球 + 彗尾光軌

FlightRadar24 API

海洋船舶

InstancedMesh 光球 + 拖尾線

AIS 船舶位置資料

大地軌道列車

3D 軌道線 + 列車光球 + 拖尾線

公開時刻表 + OSM 軌道

街道公車

GPS snap 路線光球 + 歷史回放

TDX 公車即時定位 API

天空的脈動航班

  • 光軌:彗尾狀漸層光軌,additive blending 疊加自然增亮
  • 光球:多層發光球體標示當前位置,呼吸動畫 + 紅色防撞閃爍燈
  • 靜態軌跡:暗色主題依高度著色(暖橘冷藍),亮色主題隨機配色
  • 涵蓋全台 14 座機場、1,500+ 航班

海洋的脈動船舶

  • 光球:青藍色 InstancedMesh,視口剔除,呼吸動畫
  • 拖尾線per-vertex color gradient30 分鐘遞延)
  • 資料過濾:台灣周邊海域 bounding boxGPS 異常跳躍點排除、無效 MMSI 過濾

大地的脈動軌道列車

6 個軌道系統同步運行:

系統

說明

台鐵(TRA

265 OD 軌道、333 列火車,依車種 6 色分類

高鐵(THSR

南北主線 + 支線

台北捷運(TRTC

8 條路線

高雄捷運(KRTC

/ 橘線

高雄輕軌(KLRT

環狀輕軌

台中捷運(TMRT

/ 藍線

  • 列車光球per-instance color,各系統不同顏色
  • 拖尾線:台鐵 + 高鐵專屬(3 分鐘遞延)
  • 台鐵專用引擎:處理 OD 軌道、golden track、彰化三角線等複雜路線

街道的脈動公車

台北 + 新北 ~5,700 輛公車即時追蹤:

  • GPS → Route Snap:即時 GPS 座標投影到路線幾何上,沿道路平滑移動
  • 光球InstancedMesh 3000 instances12 palette 依路線 hash 配色
  • 雙模式Live 模式 30 polling GPS / Replay 模式載入歷史軌跡(5 分鐘降采樣)
  • 路線配對率99.9%5746/5751),2293 條路線幾何預處理
  • 資料管線TDX A1 RealTimeByFrequency → data-collectors → Supabase bus_current / bus_trails_daily

地標與基礎設施

標記

渲染方式

來源

機場邊界(14 座)

fill + line + glow

OSM Overpass API

大站 Polygon

fill + glow

OSM Overpass API

小站 + 捷運站(491 站)

circle glow 圓環

車站 GeoJSON

車站光柱(535 站)

Three.js 3D 光柱(高度 = 停靠次數正規化)

預計算靜態 JSON

港口邊界

fill + line + glow

OSM Overpass API

燈塔(36 座)

circle dot + 3D 旋轉錐形光束

交通部航港局

市區公車站

circle dot + glow

TDX 公共運輸資料

公路客運站

circle dot + glow

TDX 公共運輸資料

公共腳踏車站

circle dot + glow

TDX 公共運輸資料

國道路網

line(紅色,zoom 自適應寬度)

交通部公路局

省道路網

line(橘色,zoom 自適應寬度)

交通部公路局

自行車道

line(綠色,zoom 自適應寬度)

交通部

國道壅塞

line(色彩編碼壅塞等級,依 timeline 動態回放)

交通部公路局 TDX → Supabase realtime.freeway_sections

氣象觀測站

circle dot + glow

中央氣象署

離岸風場範圍

fill + line + glow

經濟部能源局

人流模擬六角格

Mapbox fill / fill-extrusionPlasma / Viridis 色階)

內政部最小統計區人流

人口數六角格

Mapbox fill / fill-extrusionInferno 色階)

SEGIS 村里人口統計

人口指標六角格

Mapbox fill / fill-extrusionInferno 色階,9 項指標)

SEGIS 村里人口統計

溫度波浪曲面

Three.js BufferGeometryRdBu 發散色盤,vertex lerp 動畫)

中央氣象署 0.03° 格點

新聞事件標記

Mapbox circle + glow(依 timeline 累積 + 15min ripple

RSS ×29CNA/自由/ETtodayGoogle News geo ×22 縣市)→ Gemini Flash-Lite 地名抽取(368 鄉鎮白名單,LLM 不吐座標)→ Supabase realtime.news_eventsgeom 由鄉鎮界圖 centroid trigger 補);每 20 分鐘一輪

地震事件

circle(規模/深度色階 + ripple 動畫,依 timeline 累積)

CWA 地震目錄 → Supabase realtime.earthquake_events

災害示警 ×5 群組

fill + line + centroid circleevent_term 分色、severity 驅動透明度,依 timeline 動態 active 過濾)。拆為民生中斷/水文防汛/氣象特報/交通阻斷/安全環境五個獨立 toggle,共用單一 source

NCDR CAP feedpolygon + circle 解析)→ Supabase realtime.disaster_alertsgeocode 行政區代碼由 migration 161 解析成村里/鄉鎮/縣市界圖(99.8% 命中),無代碼者 fallback area_desc 文字比對

功能

圖層面板(LayerSidebar

十分類側邊欄,共 23 個圖層可獨立 toggle 開關,面板可收合為側邊窄條(點擊 收合、點窄條展開),固定高度並支援捲動:

分類

圖層

MOVING

航班 Flight、船舶 Ship、鐵道 Rail、公車即時 Bus Live(可展開參數面板)

NEWS

新聞事件 News(可展開)

STATION

高鐵站 THSR · 台鐵站 TRA · 捷運站 Metro · 市區公車站 City Bus · 公路客運站 Intercity · 公共腳踏車 Bike(可展開)

ROUTE

國道 Highway · 省道 Prov.Road · 自行車道 Cycling(可展開)

INFRA

碼頭 Port · 機場 Airport · 燈塔 Lighthouse(可展開)

ANALYTICS

人流模擬 Pop. Flow · 人口數 Population · 人口指標 Indicators(可展開)

MONITOR

國道壅塞 Congestion(可展開)

ENVIRON

氣象站 Weather(可展開)· 風場範圍 Wind Farm · 溫度波 Temperature(可展開)

HAZARD

活動斷層 Fault(可展開)· 地震 Earthquake · 民生中斷 Lifeline(停水/停電/斷訊/停班停課)· 水文防汛 Flood · 氣象特報 Weather · 交通阻斷 Transit · 安全環境 Safety(火災/海污/空品/森林)

  • MOVING 展開面板含 Live Status / Trails 模式切換(航班專用)+ 視覺參數 slider
  • 鐵道面板含 Train 列車開關 + Track 2D/3D 切換(互斥:2D 平面軌道 / 3D 立體軌道)
  • 車站面板含 Pillar 光柱開關 + Height 光柱高度調整
  • 運具按鈕顯示活躍數量(航班數、船舶數、列車數、公車數)
  • 收合狀態以彩色小點顯示各圖層啟用狀態

即時參數調整

航班(Flights

控制項

範圍

說明

Alt ×

1~5

高度誇張倍率,數值越大弧線越高聳

Z offset

0~200m

基準高度偏移,避免低空航線貼地

Opacity

0.02~0.5

靜態軌跡線不透明度

Orb

飛行光球大小

APT

0~0.3

機場區域填充不透明度

Glow

0~2

機場光暈強度

船舶(Ships

控制項

範圍

說明

Ship Orb

船舶光球大小

Ship Trail

0.05~1

船舶拖尾線透明度

鐵道(Rail

控制項

類型

說明

Train

toggle

列車光球顯示開關

Track

select

軌道渲染模式(2D 平面 / 3D 立體)

Rail Z

0~500m

軌道 Z 軸偏移高度

Rail Orb

列車光球大小

Rail Trk

0.05~1

軌道線透明度

車站(THSR / TRA / Metro 各自獨立)

控制項

範圍

說明

Stn

0.3~3x

車站圓環大小縮放

Pillar

toggle

3D 光柱顯示開關

Height

0.2~3x

光柱高度倍率

燈塔(Lighthouses

控制項

範圍

說明

LH

0.3~3x

燈塔標記大小

Beam

toggle

3D 旋轉光束開關

Dist

0.2~3

光束投射距離

Opa

0.05~0.8

光束透明度

人流模擬(Population Flow Simulation

控制項

範圍

說明

Opacity

0.1~1

六角格填充不透明度

Contrast

0.5~4

Gamma 對比度(越大高人流越突出、低人流越暗)

3D

toggle

2D 平面填充 / 3D 柱狀高度切換

Height

10~200

3D 柱狀高度倍率

Metric

Day / Night

日間人流 / 夜間人流切換(共用高度基準)

  • 色階:日間 = Plasma(深靛桃紅亮黃),夜間 = Viridis(深紫青綠亮黃)
  • 正規化:log1p + gamma,感知均勻、色盲友善
  • Zoom 自動切換網格精度
  • 渲染:Mapbox 原生 fill / fill-extrusion layer(正確跟隨相機 pitch/bearing

人口數(Population Count

控制項

範圍

說明

Opacity

0.1~1

六角格填充不透明度

Contrast

0.5~4

Gamma 對比度

3D

toggle

2D 平面 / 3D 柱狀切換

Height

10~200

3D 柱狀高度倍率

  • 資料:SEGIS 村里人口統計(114 6 月),7,748 村里
  • 色階:Inferno(深黑紫紅橘黃亮黃白)
  • 正規化:log1p + gamma(總人口重尾分布適用)
  • Pipelinetaipei-gis-analytics/pipelines/demographics/population/08_h3_village_demographics.py
  • 解析度res7~8K cells+ res8~56K cells),不產 res9村里 polygon 較大res9 無意義

人口指標(Population Indicators

控制項

範圍

說明

Category

Count / Struct / Burden

指標分類選擇

Metric

動態切換

Category 顯示對應指標

Opacity

0.1~1

六角格填充不透明度

Contrast

0.5~4

Gamma 對比度

3D

toggle

2D 平面 / 3D 柱狀切換

Height

10~200

3D 柱狀高度倍率

Category → Metric 對應

Category

Metric

數量 Count

戶數 HH · M · F

結構 Struct

性別比 Sex · 每戶 PPH

負擔 Burden

扶養 Dep · 扶幼 Child · 扶老 Elder · 老化 Aging

  • 數量指標(hh/m/f):log1p + gamma 正規化
  • 比率指標(sr/pph/dr/cd/ed/ai):linear + gamma 正規化
  • 比率欄位 H3 聚合方式:人口加權平均(非簡單平均)

其他圖層

圖層

控制項

範圍

說明

公車即時(Bus Live

Bus Orb

公車光球大小

公車站(City / Intercity

Bus

0.3~3x

圓點大小

公共腳踏車

Bike

0.3~3x

圓點大小

自行車道

Cycling

0.3~3x

線條寬度倍率

國道壅塞

Freeway

0.3~3x

線條寬度倍率

氣象站

Weather

0.3~3x

標記大小

溫度波浪(Temperature Wave

控制項

類型

範圍

說明

3D

toggle

3D 波浪曲面 / 2D 平面色圖切換

Height

slider

0~400

溫度造成的高低起伏振幅

Z Offset

slider

0~1000

整塊曲面的 Z 軸抬升高度

Opacity

slider

0.1~1

曲面透明度

Grid

toggle

網格線覆蓋開關

  • 資料:中央氣象署小時溫度觀測分析格點(O-A0038-003),0.03° 解析度(~3.3km),120×67 格網
  • 色盤:RdBu 發散(深藍深紅),正規化使用實際資料 tempMin/tempMax
  • 動畫:binary search 找兩個 bracketing framesvertex lerp 平滑過渡
  • 時間同步:直接使用 timeline unix timestamp,與航班/船舶/列車完全同步

載入畫面(LoadingScreen

首頁載入時同步等待 4 項資料完成,並以獨立進度列顯示各項狀態:

資料項

大小

完成顯示

航班 Flights

~84MB

航班數量

船舶 Ships

~54MB

船舶數量

鐵道 Rail

~53MB

系統數量

溫度場 Temperature

~941KB

  • 各項獨立打勾:完成(藍色 + 數量)、 進行中(旋轉動畫)、等待中(灰色)
  • 底部真實進度條(done/total),非假動畫
  • 全部完成後才顯示地圖 UI 並自動播放時間軸,確保「一打開就是可使用狀態」

全域 Loading 指示器(LoadingIndicator

所有透過 Supabase 的非同步資料載入(船舶 / 航班 / 國道壅塞 / 災害示警 / 地震 / 溫度 / YouBike / H3 人口)會自動在右上角顯示進度浮層,告知使用者目前正在撈取哪些資料:

  • src/lib/loadingRegistry.ts — 全域 task store + withLoading() 包裝函式
  • src/hooks/useLoadingTasks.ts — useSyncExternalStore 訂閱
  • src/components/LoadingIndicator.tsx — 半透明 HUD 浮層(spinner + 任務列表)
  • loader 透過 withLoading(id, label, supabase.rpc(...)) 自動 start/endcache hit 不會觸發

其他功能

  • 6 Mapbox 底圖樣式(Dark / Light / Satellite / Satellite Streets / Navigation Night / Streets
  • 開場全台總覽視角(23.43°N, 121.12°E, z7.9, pitch 48°
  • 14 座台灣機場預設視角 + 飛行動畫跳轉(2 秒)
  • 日期導航( 日期 + 即時模式)+ 時間軸播放控制(30x~3600x 加速)+ 多日範圍瀏覽(1d/3d/7d
  • Capture 拍攝模式(暗角 vignette + 標題 + 時間標記,按 ESC 退出)
  • 顯示模式切換:Live Status(即時位置)/ Trails(軌跡線)
  • 點擊飛機 / 列車光球顯示浮動資訊卡
  • 768px 以下自動切換手機版 UI(底部拖曳面板)
  • 底部資訊列:即時運具計數 + 相機 pitch / bearing / zoom 參數
  • Info Modal5 頁多分頁說明面板(操作指南 · 功能圖例 · 資料來源 · 關於 · 個人)
  • Data Calendar 面板:月曆格顯示各資料源涵蓋日期,Dots/Heat 雙模式 + 來源篩選

技術棧

層級

技術

用途

框架

React 19 + TypeScript + Vite

應用骨架

地圖

Mapbox GL JS v3

3D terrain、底圖、相機控制

空間索引

H3 (h3-js)

六角形網格(人流模擬 + 村里人口指標)

3D 渲染

Three.js r172

光軌、光球、InstancedMesh

Shader

GLSL

光軌漸層材質

雲端

AWS S3

資料增量同步

容器

Docker + Nginx

生產部署

架構

Overlay Registry 模式

所有 Mapbox GL 靜態圖層(機場、車站、港口、燈塔、道路、風場等)透過配置驅動 Overlay Registry 統一管理:

overlayRegistry.ts  宣告式 config 陣列(sourceUrl + paint 函式)

overlayManager.ts   通用 CRUDaddOverlay / updateTheme / setVisible

MapView.tsx         一個 useEffect 控制所有 overlay 可見性 + 主題

新增一個 overlay 只需改 3 個檔案:

  1. src/types/index.ts — LayerVisibility 加一個 key
  2. src/map/overlayRegistry.ts — 加一筆 OverlayConfig
  3. src/components/LayerSidebar.tsx — 在對應 section 加一列

Data Source Registry 模式

多資料源時間管理透過 Data Source Registry 統一註冊:

useDataRegistry.ts  中央註冊表(sources Map + register/getTimelineRange/hasDataOnDate

useTimeline.ts      日期導向時間軸(selectedDate + rangeDays + windowStart/windowEnd

TimelineControls.tsx — 日期導航 UI 日期 + Live/Replay + 1d/3d/7d 範圍)

DataCalendarPanel.tsx — 月曆格資料可用性視覺化(Dots/Heat 模式 + 來源篩選)

每個資料源宣告自己的時間類型:

  • track:航班、船舶、公車歷史軌跡(有明確的起訖時間範圍)
  • snapshot:溫度場(每小時一個 frame,跨多天)
  • cyclic:鐵道(每日循環的時刻表)
  • static:機場、車站等不隨時間變化的圖層

Supabase RPC pre-aggregate pattern

所有高頻時序 RPC 都走「普通 table + per-day refresh function + pg_cron + SELECT RPCpattern,避開 Supabase pooler 強制的 2 分鐘 statement_timeout,並讓前端讀取穩定落在百毫秒級:

RPC

預聚合 table

Cron

Before → After

get_ship_trails

realtime.ship_trails_daily

*/10

timeout → 123ms

get_flight_trails

realtime.flight_trails_daily

*/10

timeout → 126ms

get_freeway_congestion_day

realtime.freeway_congestion_daily

*/10

60s 邊緣 → 302ms

get_youbike_h3_snapshots

realtime.youbike_h3_daily

*/10

6.4s → 82ms

get_temperature_frames

realtime.temperature_frames_daily

*/30

551ms → 107ms

get_temperature_dates

realtime.temperature_dates_cache

*/15

1.9s → 72ms

get_temperature_grid_info

reference.temperature_grid_cells(靜態)

1.08s → 269ms

get_disaster_alerts_day

realtime.disaster_alerts_daily(含已解析 geom

*/10

13.2s → 110ms

get_cwa_imagery_frames_batch

批次 RPC 取代 N 次並發 fetch

TypeError: Failed to fetch → 57MB/1.7s

get_bus_current_taipei

realtime.bus_currentupsert 表)

即時 ~100ms

get_bus_trails

realtime.bus_trails_daily5 分鐘降采樣)

*/15

pre-agg → ~200ms

get_ship/flight/bus_dates

*_trails_days_summary

4s → 47ms

SQL 檔位於 data-collectors/docs/sql/matview_*.sql。盤點報告見 docs/supabase_rpc_audit.md

Three.js CustomLayer 架構

透過 Mapbox CustomLayer 在同一個 WebGL context 中嵌入 Three.js 場景,六個獨立 CustomLayer 各自管理動態渲染,常駐地圖、由 getIsVisible 控制渲染開關:

Mapbox GL JS(底圖 + 3D terrain + 相機控制)

  ── CustomLayer: flight-3d     FlightSceneGLSL 光軌 + 光球 + 閃爍燈)

  ── CustomLayer: ship-3d       ShipSceneInstancedMesh + 拖尾線)

  ── CustomLayer: rail-3d       RailScene(靜態軌道 + 列車光球 + 拖尾)

  ── CustomLayer: bus-3d        BusSceneGPS snap 公車光球,3000 instances

  ── CustomLayer: lighthouse-3d LighthouseScene(旋轉錐形光束)

  ── CustomLayer: station-pillar-3d StationPillarScene(車站 3D 光柱)

  ── CustomLayer: temperature-wave-3d TemperatureWaveScene(溫度 3D 波浪曲面)

  ── Population Flow LayerMapbox native fill / fill-extrusion

  ── Demographics Layers(人口數 + 人口指標,Mapbox native fill / fill-extrusion

  └── Overlay RegistryMapbox GL Layers

        ── 機場邊界(fill + glow

        ── 車站標記(polygon + circle glow

        ── 港口邊界(fill + glow

        ── 燈塔(circle + glow

        ── 市區公車站 / 公路客運站 / 腳踏車站(circle + glow

        ── 國道 / 省道 / 自行車道(line

        ── 國道壅塞(line,色彩編碼)

        ── 氣象站(circle + glow

        └── 離岸風場(fill + line + glow

專案結構

mini-taiwan-pulse/

── public/

   ── aviation_data.json          # 航班軌跡(gitignored

   ── ship_data.json              # 船舶軌跡(gitignored

   ── airports.geojson            # 台灣 14 座機場邊界

   ── station_polygons.geojson    # 大站 Polygon

   ── station_points.geojson      # 小站 + 捷運站 Point491 站)

   ── station_pillars.json        # 車站光柱預計算資料(535 站)

   ── station_daily_stops.json    # 站點每日停靠次數

   ── bus_stations_city.geojson   # 市區公車站

   ── bus_stations_intercity.geojson # 公路客運站

   ── bike_stations.geojson       # 公共腳踏車站(gitignored

   ── cycling_routes.geojson      # 自行車道(gitignored

   ── freeway_congestion.geojson  # 國道壅塞(gitignored

   ── weather_stations.geojson    # 氣象站(gitignored

   ── port_polygons.geojson       # 港口邊界

   ── lighthouse.geojson          # 燈塔位置(36 座)

   ── national_highway.geojson    # 國道路網(gitignored

   ── provincial_road.geojson     # 省道路網(gitignored

   ── wind_plan.geojson           # 離岸風場範圍

   ── h3_population_res7.json    # 人流模擬 res7~8K cells

   ── h3_population_res8.json    # 人流模擬 res8~56K cells

   ── h3_demographics_res7.json  # 村里人口指標 res7~8K cells

   ── h3_demographics_res8.json  # 村里人口指標 res8~56K cells

   ── temperature_grid.json      # 溫度格點時序(S3 逐時快照,~941KB

   ── news_events.geojson          # 新聞事件 fallback(主來源已改 Supabase RPC 按日載入)

   ── bus/                        # 公車路線幾何(預處理產出,gitignored

      └── taipei_bus_routes.json  # 台北+新北 2293 條路線(~17MB

   └── rail/                       # 軌道時刻表 + GeoJSONgitignored

       ── tra/                    # 台鐵

       ── thsr/                   # 高鐵

       ── trtc/                   # 台北捷運

       ── krtc/                   # 高雄捷運

       ── klrt/                   # 高雄輕軌

       └── tmrt/                   # 台中捷運

── scripts/                        # 資料準備腳本(Python + TypeScript

── src/

   ── App.tsx                     # 主應用 + 狀態協調

   ── types/index.ts              # 型別定義(含 OverlayConfig

   ── components/

      ── LoadingScreen.tsx       # 全資料預載進度畫面(4 項獨立打勾 + 真實進度條)

      ── InfoModal.tsx           # 多分頁 Info Modal5 頁)

      ── LayerSidebar.tsx        # 十分類圖層面板(MOVING / NEWS / STATION / ROUTE / INFRA / ANALYTICS / MONITOR / ENVIRON / HAZARD

      ── TimelineControls.tsx    # 時間軸播放控制

      ── DataCalendarPanel.tsx   # 月曆格資料可用性視覺化

      ── AirportSelector.tsx     # 地點跳轉

      ── StyleSelector.tsx       # 底圖樣式選擇

      └── MobileBottomSheet.tsx   # 手機版底部面板

   ── map/

      ── MapView.tsx             # Mapbox 地圖容器(overlay 生命週期管理)

      ── overlayRegistry.ts      # Overlay 配置陣列(宣告式)

      ── overlayManager.ts       # Overlay CRUD 通用函式

      ── customLayer.ts          # Three.js CustomLayer 橋接(flight/ship/rail

      ── busCustomLayer.ts       # 公車 CustomLayerGPS snap 光球)

      ── lighthouseCustomLayer.ts # 燈塔 3D 光束 CustomLayer

      ── stationPillarCustomLayer.ts # 車站光柱 CustomLayer

      ── temperatureWaveCustomLayer.ts # 溫度波浪 CustomLayer

      ── staticTrails.ts         # 2D 靜態航線軌跡

      ── railTracks.ts           # Mapbox native 軌道線

      ── h3LayerFactory.ts       # 人流六角格 Mapbox fill/fill-extrusion 工廠

      ── demographicsLayerFactory.ts # 人口數/指標六角格工廠(Inferno 色階)

      └── cameraPresets.ts        # 機場預設視角

   ── three/                      # Three.js 3D 場景

      ── FlightScene.ts          # 航班光軌 + 光球 + GLSL shader

      ── ShipScene.ts            # 船舶 InstancedMesh + 拖尾線

      ── RailScene.ts            # 軌道列車光球 + 拖尾 + 靜態軌道

      ── BusScene.ts             # 公車 GPS snap 光球(InstancedMesh 3000

      ── StationPillarScene.ts   # 車站 3D 光柱(InstancedMesh

      ── LighthouseScene.ts      # 燈塔旋轉錐形光束

      ── TemperatureWaveScene.ts # 溫度 3D 波浪曲面(RdBu 色盤 + vertex lerp

      ── LightOrb.ts             # 光球共用元件

      ── LightTrail.ts           # 光軌 GLSL 材質

      └── BlinkingLight.ts        # 防撞閃爍燈

   ── engines/                    # 列車運動插值引擎

      ── RailEngine.ts           # 通用軌道引擎(THSR/MRT

      ── TraTrainEngine.ts       # 台鐵專用引擎(OD 軌道)

      └── railUtils.ts            # 軌道工具函式

   ── hooks/                      # React Custom Hooks

      ── useTransportParams.ts   # 運具視覺參數 state + refs + sliders

      ── useRailEngine.ts        # 軌道引擎 + rAF tick loop

      ── useLayerVisibility.ts   # 圖層可見性 state + toggle

      ── useThreeJsLayers.ts     # Three.js CustomLayer 建立與管理

      ── useMapInteraction.ts    # 飛機/列車點擊互動

      ── useFlightData.ts        # 航班資料載入

      ── useShipData.ts          # 船舶資料載入

      ── useRailData.ts          # 軌道資料載入

      ── useTemperatureData.ts    # 溫度格點資料載入

      ── useH3Data.ts            # 人流資料載入 + 快取

      ── useDemographicsH3.ts    # 村里人口指標資料載入 + 快取

      ── useTimeline.ts          # 時間軸播放邏輯

      ── useDataRegistry.ts      # 資料源中央註冊表

      └── useIsMobile.ts          # 響應式斷點偵測

   ── data/                       # 資料載入器

      ── flightLoader.ts         # 航班 JSON 解析 + 時間窗過濾

      ── shipLoader.ts           # 船舶 JSON 解析

      ── railLoader.ts           # 軌道時刻表 + GeoJSON 載入

      ── temperatureLoader.ts     # 溫度格點 JSON 載入

      ── h3Loader.ts             # 人流 JSON 載入(local + S3 fallback

      └── s3Loader.ts             # S3 增量同步

   ── constants/                  # 常數定義

   └── utils/                      # 座標轉換、軌跡插值

── Dockerfile                      # Multi-stage build

── docker-compose.yml              # Port 3721

└── nginx.conf

資料準備

所有資料皆來自開放資料源,透過腳本擷取與轉換:

航班資料

來源:FlightRadar24 API

npm run fetch:flights              # 取得航班清單

npm run fetch:tracks -- --date 2026-02-18   # 擷取飛行軌跡

船舶資料

來源:AIS 船舶位置資料(經 ship-gis SQLite 匯出)

python3 scripts/export-ship-data.py                     # 預設日期

python3 scripts/export-ship-data.py 2026-02-18 2026-02-19  # 指定日期範圍

軌道資料

來源:公開時刻表 + OSM 軌道 GeoJSON

python3 scripts/export-rail-data.py          # 匯出 6 個系統的時刻表 + 軌道

python3 scripts/build-station-points.py      # 合併 491 Point GeoJSON

python3 scripts/fetch-station-polygons.py    # 下載大站 OSM Polygon

npm run pillars:generate                     # 預計算車站光柱資料(停靠次數高度)

溫度格點資料

來源:中央氣象署 小時溫度觀測分析格點(O-A0038-003),經 data-collectors 每小時收集存入 S3

# S3 下載(自動偵測航班時間範圍)

python3 scripts/fetch-temperature-s3.py

 

# 指定日期範圍

python3 scripts/fetch-temperature-s3.py 2026-02-17 2026-02-20

S3 上傳

npm run s3:upload          # 航班資料

npm run s3:upload:ships    # 船舶資料

npm run s3:upload:rail     # 軌道資料

開發

npm install

cp .env.example .env       # 填入 VITE_MAPBOX_TOKEN

npm run dev                # 開發模式

npm run build              # 正式建置

Docker 本地部署

docker-compose up -d       # http://localhost:3721

大檔案透過 docker-compose.yml volumes 掛載到 /data/nginx 會從該路徑讀取。

Zeabur 部署

1. Zeabur Dashboard → 建立服務 GitHub repo

2. 設定環境變數:

   - VITE_MAPBOX_TOKENbuild timeVite 嵌入靜態檔)

   - S3_ACCESS_KEYruntimepull 腳本用)

   - S3_SECRET_KEY

   - S3_REGION=ap-southeast-2

   - S3_BUCKET=migu-gis-data-collector

3. 建立 Volume → Mount path: /data

4. 部署完成後,進 Web Terminal 執行:

   sh /usr/local/bin/pull-deploy-assets.sh

5. 確認輸出包含所有檔案 + "rail/ extracted to /data/rail/"

注意:每次重新部署後 Volume 資料仍在,但新 container 啟動時不會自動拉取。 若資料有更新,需手動再跑一次 pull 腳本。

資料管理(S3 deploy-assets

大檔案(~200MB)不進 git,統一託管到 S3 deploy-assets/ prefix私密存取(需認證):

S3 檔案

大小

說明

對應 /data/ 路徑

aviation_data.json

44MB

航班軌跡

/data/aviation_data.json

ship_data.json

54MB

船舶軌跡

/data/ship_data.json

rail.tar.gz

~8MB

軌道資料(6 系統個別檔案打包)

/data/rail/ 解壓

provincial_road.geojson

44MB

省道路網

/data/provincial_road.geojson

national_highway.geojson

7.9MB

國道路網

/data/national_highway.geojson

bus_stations_city.geojson

19MB

市區公車站

/data/bus_stations_city.geojson

bus_stations_intercity.geojson

5.8MB

公路客運站

/data/bus_stations_intercity.geojson

bike_stations.geojson

4.4MB

公共腳踏車站

/data/bike_stations.geojson

cycling_routes.geojson

自行車道

/data/cycling_routes.geojson

freeway_congestion.geojson

國道壅塞

/data/freeway_congestion.geojson

weather_stations.geojson

氣象站

/data/weather_stations.geojson

temperature_grid.json

~941KB

溫度格點時序

/data/temperature_grid.json

# 本地 → S3(需 .env 中的 S3 credentials

bash scripts/upload-deploy-assets.sh

 

# Container S3 → /data/(需環境變數 S3_ACCESS_KEY 等)

sh /usr/local/bin/pull-deploy-assets.sh

資料更新完整流程

# 1. 本地產生/更新資料(視需要)

python3 scripts/export-rail-data.py       # 軌道

 

# 2. 上傳到 S3 deploy-assets/(統一用這個腳本!)

bash scripts/upload-deploy-assets.sh

 

# 3. Zeabur Web Terminal 拉取

sh /usr/local/bin/pull-deploy-assets.sh

⚠️ 易錯提醒

  • npm run s3:upload:rail 上傳到的是 rail-data/ 路徑(前端 fallback 用),不是 deploy-assets
  • 部署用的上傳一律用 upload-deploy-assets.sh,不要混用
  • pull 腳本路徑是 /usr/local/bin/,不是 /usr/share/nginx/html/
  • 前端請求的是 /rail/krtc/krtc_schedules.json 等個別檔案,不是 rail_bundle.json

環境需求

  • Node.js 22+
  • Python 3(資料匯出腳本)
  • Mapbox Access Token

開放資料來源

資料

來源

航班軌跡

FlightRadar24

船舶位置

AISAutomatic Identification System

軌道時刻表

台鐵、高鐵、各捷運公開時刻表(TDX

機場 / 車站 / 港口邊界

OpenStreetMap via Overpass API

燈塔位置

交通部航港局

公車站位 / 腳踏車站

TDX 公共運輸資料

國道 / 省道 / 自行車道

交通部公路局

國道壅塞

交通部公路局

氣象觀測站

中央氣象署

溫度格點(0.03° 網格,每小時快照)

中央氣象署 O-A0038-003

新聞事件地標

中央通訊社 CNA自由時報ETtodayGoogle News RSS + Gemini Flash-Lite 地名抽取(座標由鄉鎮界圖 centroid 解析)

離岸風場範圍

經濟部能源局

日夜間人流

內政部最小統計區人流統計(六角形網格化)

村里人口指標

社會經濟統計地理資訊網 (SEGIS)114 6

地圖底圖

Mapbox

H3 統計圖層開發注意事項

新增 H3 六角格統計圖層時,以下是已踩過的 pitfall,務必避免重蹈覆轍:

1. map.isStyleLoaded() 不可用於 useEffect 條件判斷

問題isStyleLoaded()  Mapbox 載入 tiles 期間也會回傳 false(不只是初始 style parse),導致在時間軸播放、底圖切換等場景下,useEffect 永遠跳過更新。

解法:改用 map.getSource("source-id") 檢查 source 是否已存在。Source  style.load / load 事件中由 ensureXxxLayers() 建立後就持續存在,不受 tile loading 影響。

// BAD — 會在 tile loading 時持續 skip

if (!map.isStyleLoaded()) return;

 

// GOOD — source 存在就可以操作

if (!map.getSource("h3-pop-count-src")) return;

2. useEffect deps 中的物件必須 useMemo

問題{ opacity, contrast, extruded, elevationScale } 每次 render 都是新物件,導致 useEffect 每幀都觸發,搭配上面的 isStyleLoaded skip 造成無限 console spam

解法:所有傳入 useEffect dependency params 物件用 useMemo 包裝。

3. PipelineGeoDataFrame MultiPolygon 時需 explode

問題polyfill_and_distribute() 內部使用 polygon.exterior.coords,但 MultiPolygon 沒有 .exterior 屬性。

解法:在呼叫前先 gdf.explode(index_parts=False).reset_index(drop=True)

4. 比率欄位 H3 聚合必須人口加權

問題:簡單平均會讓小村里(人口 10)與大村里(人口 10,000)的比率權重相同。

解法weighted_ratio = population × ratio → distribute → ratio = sum(weighted) / sum(population)

5. 共用資料、分開 Source

兩個圖層(人口數 + 人口指標)共用同一份 JSON 但使用獨立的 Mapbox source + layer,才能各自控制 visibilityopacitymetric。不要試圖讓兩個圖層共享同一個 source — 會造成 GeoJSON 互相覆蓋。

License

MIT License. 詳見 LICENSE

 

 

資料來源:

中央研究院人社中心地理資訊科學研究專題中心

https://github.com/ianlkl11234s/mini-taiwan-pulse?fbclid=IwY2xjawTeig9leHRuA2FlbQIxMABicmlkETF1MXNCYjZLU1l4NDF5bEdEc3J0YwZhcHBfaWQQMjIyMDM5MTc4ODIwMDg5MgABHgczgl8cCYtkVHc-3VQ5PEBXZPtr-pGylIz6MW4fsYelIJO8XlAJxpiTpzAV_aem_sHkffuTCm6S62Ud6qsbfeA

 

Share this:

CONVERSATION

0 意見:

張貼留言