用開放資料,感受台灣的脈動。
天空中的航班劃出弧線、海面上的船舶穿梭往返、軌道上的列車準時奔馳 — 這座島嶼每一刻都在呼吸。Mini Taiwan Pulse 將這些交通運輸的即時動態,以 3D 光球、光軌、拖尾線呈現在同一張地圖上,讓你看見台灣的脈搏。
| 脈動 | 視覺呈現 | 開放資料來源 |
|---|---|---|
| 天空 — 航班 | 3D 弧線 + 光球 + 彗尾光軌 | FlightRadar24 API |
| 海洋 — 船舶 | InstancedMesh 光球 + 拖尾線 | AIS 船舶位置資料 |
| 大地 — 軌道列車 | 3D 軌道線 + 列車光球 + 拖尾線 | 公開時刻表 + OSM 軌道 |
| 街道 — 公車 | GPS snap 路線光球 + 歷史回放 | TDX 公車即時定位 API |
- 光軌:彗尾狀漸層光軌,additive blending 疊加自然增亮
- 光球:多層發光球體標示當前位置,呼吸動畫 + 紅色防撞閃爍燈
- 靜態軌跡:暗色主題依高度著色(暖橘→冷藍),亮色主題隨機配色
- 涵蓋全台 14 座機場、1,500+ 航班
- 光球:青藍色 InstancedMesh,視口剔除,呼吸動畫
- 拖尾線:per-vertex color gradient(30 分鐘遞延)
- 資料過濾:台灣周邊海域 bounding box、GPS 異常跳躍點排除、無效 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 instances,12 色 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-extrusion(Plasma / Viridis 色階) | 內政部最小統計區人流 |
| 人口數六角格 | Mapbox fill / fill-extrusion(Inferno 色階) | SEGIS 村里人口統計 |
| 人口指標六角格 | Mapbox fill / fill-extrusion(Inferno 色階,9 項指標) | SEGIS 村里人口統計 |
| 溫度波浪曲面 | Three.js BufferGeometry(RdBu 發散色盤,vertex lerp 動畫) | 中央氣象署 0.03° 格點 |
| 新聞事件標記 | Mapbox circle + glow(依 timeline 累積 + 15min ripple) | RSS ×29(CNA/自由/ETtoday/Google News geo ×22 縣市)→ Gemini Flash-Lite 地名抽取(368 鄉鎮白名單,LLM 不吐座標)→ Supabase realtime.news_events(geom 由鄉鎮界圖 centroid trigger 補);每 20 分鐘一輪 |
| 地震事件 | circle(規模/深度色階 + ripple 動畫,依 timeline 累積) | CWA 地震目錄 → Supabase realtime.earthquake_events |
| 災害示警 ×5 群組 | fill + line + centroid circle(event_term 分色、severity 驅動透明度,依 timeline 動態 active 過濾)。拆為民生中斷/水文防汛/氣象特報/交通阻斷/安全環境五個獨立 toggle,共用單一 source | NCDR CAP feed(polygon + circle 解析)→ Supabase realtime.disaster_alerts;geocode 行政區代碼由 migration 161 解析成村里/鄉鎮/縣市界圖(99.8% 命中),無代碼者 fallback area_desc 文字比對 |
十分類側邊欄,共 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 光柱高度調整
- 運具按鈕顯示活躍數量(航班數、船舶數、列車數、公車數)
- 收合狀態以彩色小點顯示各圖層啟用狀態
| 控制項 | 範圍 | 說明 |
|---|---|---|
| Alt × | 1~5 | 高度誇張倍率,數值越大弧線越高聳 |
| Z offset | 0~200m | 基準高度偏移,避免低空航線貼地 |
| Opacity | 0.02~0.5 | 靜態軌跡線不透明度 |
| Orb | — | 飛行光球大小 |
| APT | 0~0.3 | 機場區域填充不透明度 |
| Glow | 0~2 | 機場光暈強度 |
| 控制項 | 範圍 | 說明 |
|---|---|---|
| Ship Orb | — | 船舶光球大小 |
| Ship Trail | 0.05~1 | 船舶拖尾線透明度 |
| 控制項 | 類型 | 說明 |
|---|---|---|
| Train | toggle | 列車光球顯示開關 |
| Track | select | 軌道渲染模式(2D 平面 / 3D 立體) |
| Rail Z | 0~500m | 軌道 Z 軸偏移高度 |
| Rail Orb | — | 列車光球大小 |
| Rail Trk | 0.05~1 | 軌道線透明度 |
| 控制項 | 範圍 | 說明 |
|---|---|---|
| Stn | 0.3~3x | 車站圓環大小縮放 |
| Pillar | toggle | 3D 光柱顯示開關 |
| Height | 0.2~3x | 光柱高度倍率 |
| 控制項 | 範圍 | 說明 |
|---|---|---|
| LH | 0.3~3x | 燈塔標記大小 |
| Beam | toggle | 3D 旋轉光束開關 |
| Dist | 0.2~3 | 光束投射距離 |
| Opa | 0.05~0.8 | 光束透明度 |
| 控制項 | 範圍 | 說明 |
|---|---|---|
| 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)
| 控制項 | 範圍 | 說明 |
|---|---|---|
| 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(總人口重尾分布適用)
- Pipeline:
taipei-gis-analytics/pipelines/demographics/population/08_h3_village_demographics.py - 解析度:res7(~8K cells)+ res8(~56K cells),不產 res9(村里 polygon 較大,res9 無意義)
| 控制項 | 範圍 | 說明 |
|---|---|---|
| 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 | 標記大小 |
| 控制項 | 類型 | 範圍 | 說明 |
|---|---|---|---|
| 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 frames,vertex lerp 平滑過渡
- 時間同步:直接使用 timeline unix timestamp,與航班/船舶/列車完全同步
首頁載入時同步等待 4 項資料完成,並以獨立進度列顯示各項狀態:
| 資料項 | 大小 | 完成顯示 |
|---|---|---|
| 航班 Flights | ~84MB | 航班數量 |
| 船舶 Ships | ~54MB | 船舶數量 |
| 鐵道 Rail | ~53MB | 系統數量 |
| 溫度場 Temperature | ~941KB | — |
- 各項獨立打勾:✓ 完成(藍色 + 數量)、⟳ 進行中(旋轉動畫)、○ 等待中(灰色)
- 底部真實進度條(done/total),非假動畫
- 全部完成後才顯示地圖 UI 並自動播放時間軸,確保「一打開就是可使用狀態」
所有透過 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/end,cache 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 Modal:5 頁多分頁說明面板(操作指南 · 功能圖例 · 資料來源 · 關於 · 個人)
- 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 | 生產部署 |
所有 Mapbox GL 靜態圖層(機場、車站、港口、燈塔、道路、風場等)透過配置驅動的 Overlay Registry 統一管理:
overlayRegistry.ts — 宣告式 config 陣列(sourceUrl + paint 函式)
overlayManager.ts — 通用 CRUD(addOverlay / updateTheme / setVisible)
MapView.tsx — 一個 useEffect 控制所有 overlay 可見性 + 主題
新增一個 overlay 只需改 3 個檔案:
src/types/index.ts—LayerVisibility加一個 keysrc/map/overlayRegistry.ts— 加一筆OverlayConfigsrc/components/LayerSidebar.tsx— 在對應 section 加一列
多資料源時間管理透過 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:機場、車站等不隨時間變化的圖層
所有高頻時序 RPC 都走「普通 table + per-day refresh function + pg_cron + 薄 SELECT RPC」pattern,避開 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_current(upsert 表) |
— | 即時 ~100ms |
get_bus_trails |
realtime.bus_trails_daily(5 分鐘降采樣) |
*/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。
透過 Mapbox CustomLayer 在同一個 WebGL context 中嵌入 Three.js 場景,六個獨立 CustomLayer 各自管理動態渲染,常駐地圖、由 getIsVisible 控制渲染開關:
Mapbox GL JS(底圖 + 3D terrain + 相機控制)
├── CustomLayer: flight-3d ← FlightScene(GLSL 光軌 + 光球 + 閃爍燈)
├── CustomLayer: ship-3d ← ShipScene(InstancedMesh + 拖尾線)
├── CustomLayer: rail-3d ← RailScene(靜態軌道 + 列車光球 + 拖尾)
├── CustomLayer: bus-3d ← BusScene(GPS snap 公車光球,3000 instances)
├── CustomLayer: lighthouse-3d ← LighthouseScene(旋轉錐形光束)
├── CustomLayer: station-pillar-3d ← StationPillarScene(車站 3D 光柱)
├── CustomLayer: temperature-wave-3d ← TemperatureWaveScene(溫度 3D 波浪曲面)
├── Population Flow Layer(Mapbox native fill / fill-extrusion)
├── Demographics Layers(人口數 + 人口指標,Mapbox native fill / fill-extrusion)
└── Overlay Registry(Mapbox 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 # 小站 + 捷運站 Point(491 站)
│ ├── 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/ # 軌道時刻表 + GeoJSON(gitignored)
│ ├── tra/ # 台鐵
│ ├── thsr/ # 高鐵
│ ├── trtc/ # 台北捷運
│ ├── krtc/ # 高雄捷運
│ ├── klrt/ # 高雄輕軌
│ └── tmrt/ # 台中捷運
├── scripts/ # 資料準備腳本(Python + TypeScript)
├── src/
│ ├── App.tsx # 主應用 + 狀態協調
│ ├── types/index.ts # 型別定義(含 OverlayConfig)
│ ├── components/
│ │ ├── LoadingScreen.tsx # 全資料預載進度畫面(4 項獨立打勾 + 真實進度條)
│ │ ├── InfoModal.tsx # 多分頁 Info Modal(5 頁)
│ │ ├── 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 # 公車 CustomLayer(GPS 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
所有資料皆來自開放資料源,透過腳本擷取與轉換:
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-20npm 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-compose up -d # http://localhost:3721大檔案透過 docker-compose.yml volumes 掛載到 /data/,nginx 會從該路徑讀取。
1. Zeabur Dashboard → 建立服務 → 選 GitHub repo
2. 設定環境變數:
- VITE_MAPBOX_TOKEN(build time,Vite 嵌入靜態檔)
- S3_ACCESS_KEY(runtime,pull 腳本用)
- 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 腳本。
大檔案(~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 |
| 船舶位置 | AIS(Automatic Identification System) |
| 軌道時刻表 | 台鐵、高鐵、各捷運公開時刻表(TDX) |
| 機場 / 車站 / 港口邊界 | OpenStreetMap via Overpass API |
| 燈塔位置 | 交通部航港局 |
| 公車站位 / 腳踏車站 | TDX 公共運輸資料 |
| 國道 / 省道 / 自行車道 | 交通部公路局 |
| 國道壅塞 | 交通部公路局 |
| 氣象觀測站 | 中央氣象署 |
| 溫度格點(0.03° 網格,每小時快照) | 中央氣象署 O-A0038-003 |
| 新聞事件地標 | 中央通訊社 CNA/自由時報/ETtoday/Google News RSS + Gemini Flash-Lite 地名抽取(座標由鄉鎮界圖 centroid 解析) |
| 離岸風場範圍 | 經濟部能源局 |
| 日夜間人流 | 內政部最小統計區人流統計(六角形網格化) |
| 村里人口指標 | 社會經濟統計地理資訊網 (SEGIS),114 年 6 月 |
| 地圖底圖 | Mapbox |
新增 H3 六角格統計圖層時,以下是已踩過的 pitfall,務必避免重蹈覆轍:
問題: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;問題:{ opacity, contrast, extruded, elevationScale } 每次 render 都是新物件,導致 useEffect 每幀都觸發,搭配上面的 isStyleLoaded skip 造成無限 console spam。
解法:所有傳入 useEffect dependency 的 params 物件用 useMemo 包裝。
問題:polyfill_and_distribute() 內部使用 polygon.exterior.coords,但 MultiPolygon 沒有 .exterior 屬性。
解法:在呼叫前先 gdf.explode(index_parts=False).reset_index(drop=True)。
問題:簡單平均會讓小村里(人口 10)與大村里(人口 10,000)的比率權重相同。
解法:weighted_ratio = population × ratio → distribute → ratio = sum(weighted) / sum(population)。
兩個圖層(人口數 + 人口指標)共用同一份 JSON 但使用獨立的 Mapbox source + layer,才能各自控制 visibility、opacity、metric。不要試圖讓兩個圖層共享同一個 source — 會造成 GeoJSON 互相覆蓋。
MIT License. 詳見 LICENSE。




