iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0
Modern Web

教練看不到的那六天|從 FIT 檔到 3D 軌跡,馬拉松訓練資料 Dashboard系列 第 28

Day 28|3D 路線的垂直軸:不一定要放海拔

  • 分享至 

  • xImage
  •  

昨天講的是海拔那條資料不穩定:兩條流差 8 公尺、逐秒抖動差 4.9 倍、同一個地點隔 75 分鐘再經過會低 4.8 公尺。

所以昨天的結論是:3D 圖的垂直方向只拿來看「這裡有起伏」,不在軸上標高度刻度,因為誤差大到「這裡比那裡高幾公尺」這些數字如果被呈現出來會失準。

今天要問的是另一件事:就算它完全準確,海拔真的是那個軸最好的選擇嗎?

用一筆田徑場的資料來回答。


一條完全重疊的跑道

素材是 8/13 的間歇課表:1600m×3,組間 180 秒,松山區田徑場。總共 5.22 公里、28 分鐘。

這種資料畫成 3D 路線,會出現一個地圖類視覺化不太處理的情況:軌跡完全重疊。

四百公尺的跑道繞十幾圈,經緯度上就是同一個橢圓畫十幾次。

第一版:海拔軸,真實比例

跑道是平的。所以整條軌跡壓成一個扁環—所有圈數疊在一起,什麼都看不出來。

https://ithelp.ithome.com.tw/upload/images/20260913/20183598N71DZqNhzf.jpg

看不出跑了幾圈,更看不出哪幾圈是主課表、哪幾段是組間的恢復慢跑。

第二版:把海拔放大 14 倍

直覺的解法是加誇張倍率,讓起伏看得見。

結果更糟—那條線變成一根實心的綠柱,比扁環還難讀。

https://ithelp.ithome.com.tw/upload/images/20260913/20183598lKcbtgbYk3.jpg

因為那筆資料的海拔,實測起伏是 14.8 公尺(−1.8 到 13.0)。

而那是一個四百公尺的田徑場 ,二十八分鐘之內,同一個平面上跑了十幾圈。

所以那 14.8 公尺全部是誤差—而且不是逐秒的雜訊(那筆資料逐秒只抖 0.014 公尺),是整段時間的漂移。跟昨天量到的「同一個地點隔 75 分鐘差 4.8 公尺」是同一件事,只是這裡二十八分鐘就飄了十幾公尺。

放大 14 倍,等於把那十幾公尺的誤差畫成兩百公尺的假山。


換掉那個軸

問題不在倍率,在選錯了維度

跑道是平的,所以海拔在這筆資料裡沒有任何資訊量—它記到的全是誤差。而真正把每一圈區分開來的東西是時間—第一圈和第十圈是同一個地點的不同時刻。

所以垂直軸改用「起跑後幾秒」:

// 垂直座標:海拔(誇張倍率)或時間(把重疊的圈數展開成螺旋)
const yOf = (p: { ele: number; t: number }) =>
  vert === 'ele' ? p.ele * ex : (p.t / maxT) * extent * (ex / 18);

extent 是路線在水平方向的最大跨度,ex 是介面上那個倍率滑桿;除以 18 是手調出來的常數,讓預設倍率 14 之下螺旋的總高度大約是路線寬度的八成,看起來不會太扁也不會太高。

一行三元運算,但畫出來完全是另一張圖:所有圈數展開成一條螺旋。

https://ithelp.ithome.com.tw/upload/images/20260913/20183598bqAEJpV0nU.jpg

而且一眼可見的東西變多了:

  • 圈數—數螺旋幾圈就好
  • 每組間歇的配速差異—螺距密的地方跑得快,鬆的地方慢
  • 組間休息——那 180 秒是站著不動的,所以會出現三段在同一個位置垂直往上跳的紅色短線:人沒移動,時間繼續走

同一筆資料、同一個渲染流程,只換了 yOf 那一行。


所以那個軸不是資料給的

地圖類的 3D 視覺化幾乎都預設把海拔當高度,因為在地理的語境裡那是唯一合理的答案。

但一旦資料是「同一條路徑走很多次」—田徑場、折返跑、多圈賽事、通勤路線—海拔軸會把資訊疊掉,因為它表達的是「這裡是哪裡」,而不是「這是第幾次經過這裡」。

第三個維度是設計決策。它問的是:你想把什麼東西分開?

想分開的東西 垂直軸
地形起伏 海拔
同一地點的不同時刻 時間
一圈一圈本身 距離

時間軸的問題:螺距在講兩件事

時間軸有一個問題:它不是等距的。

螺旋的高度是 t / maxT,整場時間被均勻攤在垂直方向上。可是快的圈佔的時間少、慢的圈佔得多,所以螺距的疏密同時混合了「配速」和「時間佔比」兩件事。

而配速這件事,顏色已經在講了(colorMode 預設就是配速)。畫面上兩個通道在講同一件事,其中一個還講得不乾淨,看圖的人會不知道該信哪一個。

解法是讓兩個通道各講一件事:垂直軸改用累積距離。

const yOf = (p: { ele: number; t: number; dist: number }) =>
  vert === 'ele' ? p.ele * ex
    : vert === 'time' ? (p.t / maxT) * extent * (ex / 18)
    : (p.dist / maxD) * extent * (ex / 18);

資料裡本來就有累積距離(Day 4 列 record 欄位時的 distance),所以只是多一個分支。

https://ithelp.ithome.com.tw/upload/images/20260913/201835989VubbY5F2D.jpg

每 400 公尺一圈,所以每一圈等高,螺距是固定的。形狀只剩「跑了幾圈、在哪裡」,配速完全交給顏色。

代價也要說清楚:

  • 站著休息的那 180 秒消失了。 距離沒有前進,所以那段在圖上縮成一個點。時間軸看得到休息、距離軸看不到。
  • 這場的顏色差異很小。 三組 1600 公尺在 3:40 到 4:10 之間,暖身收操也只有 4:11 和 4:26,整條看起來都是綠的。這不是上色壞了,是這場從頭到尾都沒有慢跑的區段。

所以三個軸各有用途,不是距離軸最好。要看休息,切回時間軸就好,三個按鈕都留著。


兩種資料,兩個預設

所以 dataset 的設定裡,垂直軸是跟著資料走的:

tokyo: {
  vertical: 'ele',
  // 這場高度誤差實測中位數 4.6m(同一地點兩次經過),倍率開太大會放大成假高低差
  exaggeration: 5,
},
interval: {
  // 不用時間軸:快的圈佔的時間少、慢的佔得多,螺距會同時混到配速與時間佔比,
  // 跟顏色講的配速打架。距離軸每 400 m 一圈等高,形狀只剩圈數,配速全交給顏色。
  vertical: 'dist',
  exaggeration: 14,
},

東京馬用海拔—四十二公里幾乎不重複經過同一個地方,海拔軸表達的是真實地形。但倍率壓在 5,註解寫著原因:昨天量到的誤差中位數就是 4.6 公尺,倍率開大會把它放大成假的高低差。

https://ithelp.ithome.com.tw/upload/images/20260913/20183598knRi1kuoeO.jpg

這就是 Day 1 開頭放的那張圖。當時它只是一張截圖,現在它是儀表板裡的一個分頁,垂直軸和倍率都可以自己調。

間歇用距離—因為那條跑道是平的,海拔軸沒有東西可以表達;時間軸會跟顏色打架;距離軸把圈數和配速分給兩個通道。

兩個預設不一樣,不是因為偏好,是因為那兩筆資料要回答的問題不一樣


明天:教練還是看不到

3D 畫面、六場全馬圖有了,回放和計算機也準備好。

但這些畫面到今天為止,全部只跑在本地 localhost:5173—Day 20 盤點的第一項,到現在還是沒處理。

要上線,第一件事就撞到:打開總覽頁,瀏覽器會先把整個 Three.js 載下來,即使那個人根本不會點進 3D 那頁,明天處理這件事!



上一篇
Day 27|Three.js 的高度軸:FIT 檔裡兩條海拔,該用哪一條
下一篇
Day 29|拆 bundle 上線:把 Three.js 從首屏拿掉之後
系列文
教練看不到的那六天|從 FIT 檔到 3D 軌跡,馬拉松訓練資料 Dashboard30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言