![Frame 14 (2)](https://hackmd.io/_uploads/HkagWqTzee.png) 最近為了練習 React,我開發了一個紀錄登山路線的平台。中間歷經規劃、設計與前後端開發,執行許多作業才完成,所以決定把過程寫下成文章記錄下來。 這個網站的具體功能如下: 1. 路線資料的增刪查改 2. 具有登入功能,資料的增刪改需要登入 3. 統計資料頁面,顯示個人成就 4. 搜尋不同用戶資料 5. 手機版、PWA 版 ## 緣起 本身有在爬山的習慣,每次爬山完也會使用 QGIS(一種編輯地理資料的軟體)。不過每次要分享時,總得截圖或匯出靜態地圖,無法互動(例如:縮放、查詢),使用上十分受限。 ![Intro_GIS 1](https://hackmd.io/_uploads/B1HOZqTMgg.png) 若改用 Google 地圖,雖然能快速上傳所有資料,但受限於圖層數量,無法拆開管理每筆路線。更新資料時,也必須重新匯出所有路線再整筆上傳,維護不便。 ![image 5](https://hackmd.io/_uploads/HytNM56fex.png) 為了改善上述問題,我列出了以下目標: 1. 展示所有路線資料,地圖可搜尋、互動 2. 編輯所有路線資料,包含屬性資料(不包含路線形狀) 3. 輸出所有路線資料(額外項目) ## 規劃 一開始我構想這是一個單頁應用,透過多 Panel 呈現資料、細節與地圖。 ![image 1 (1)](https://hackmd.io/_uploads/S1ZwRRCGxe.png) 初版僅能顯示我個人的資料,其他用戶無法互動,易讓人困惑。為了讓網站更加完整,我增加了多用戶的功能,各用戶也能新增自己的路線資料,網站逐漸轉變為登山愛好者的交流平台。 為了釐清流程與介面轉場,我製作了 UI flow 規劃網站的使用流程。 ![image](https://hackmd.io/_uploads/BkDZD56Gll.png) 使用流程說明如下: 1. **首頁**,包含「登山路線」或「用戶資料」的區塊 2. 點入後進入**資料頁**,「登山路線」或「用戶資料」的統計資料 3. 點入「全部軌跡」後,進入**地圖頁**,顯示地圖與表格資料 4. 如果具有帳號,可進入**登入頁面**,登入後在**地圖頁**可編輯資料 6. 其他功能:**搜尋使用者**、**介紹頁面** ## 開發 在這個專案中,我除了開發前端頁面,也設計整體架構與後端 API。雖然很多技術是邊做邊學,但也因此對整個流程有了完整掌握。以下是專案中我實際實作過的重點技術與經驗整理: ### 前端(React + TypeScript + Leaflet) 為了讓使用者能流暢操作地圖並編輯資料,我選擇使用 React 搭配 Leaflet 實作地圖互動,並使用 TypeScript 提升開發穩定度。 - 使用 React 搭配 Context 與自訂 Hooks 管理功能模組(例如:地圖、表格、全螢幕、登入狀態等)。 - 使用 TypeScript 撰寫元件與資料流程,減少開發錯誤與強化可維護性。 - 以 Leaflet 實作地圖與表格聯動功能(highlight、點擊後 panTo 等)。 - 網站支援 PWA(Progressive Web App),並設計 OG 圖、RWD、Autocomplete 搜尋與鍵盤操作等互動體驗。 - 使用 SCSS 撰寫樣式,模組化管理每個元件的樣式。 - 使用 D3.js 繪製統計圖表(圓餅圖與長條圖),並實作動畫。 - 實作使用者登入狀態切換後的操作權限控制(僅本人可編輯)。 - 前端架設於 Vercel。 ### 後端(NestJS + PostgreSQL + Swagger) 因為熟悉 .NET 的模組化設計,我選擇 NestJS 作為後端框架,讓我能更清晰地拆分 Controller、Service 與 Entity 的邏輯,並搭配 PostgreSQL 建立資料層。 - 使用 NestJS 撰寫後端,並導入 JWT 驗證機制(含 token 解析與過期驗證)。 - 採 RESTful API 設計,並使用 Swagger 自動生成 API 文件以便測試與維護。 - 設計資料表與 Entity 結構,清楚區分使用者、路線與圖層等資料。 - API 支援 CRUD、匯出、統計等操作,並依權限控制資料可見性。 - 後端部署於 Render,資料庫使用 Neon 雲端 PostgreSQL。 ![Frame 18 (2)](https://hackmd.io/_uploads/ry8FOey7xg.png) ### 資料處理與空間邏輯 為了讓地圖資料能正確儲存與後續查詢,我使用 PostGIS 擴充 PostgreSQL,並在資料上傳時進行幾何運算處理。 - 使用 PostGIS 儲存並處理路線資料,支援 GeoJSON 格式輸入。 - 匯入時自動計算並儲存以下幾項幾何資訊: - `geom`:主體幾何資料(經過 `ST_Force2D` 標準化) - `length`:路線長度(轉換為 TWD97 投影後計算,單位公里) - `center`:路線中心點(`ST_Centroid`) ```sql INSERT INTO users_trails (uuid, geom, length, center, name) VALUES ( $1, ST_Force2D(ST_GeomFromGeoJSON($2)), ROUND( ST_Length(ST_Transform(ST_Force2D(ST_GeomFromGeoJSON($2)), 3826))::numeric / 1000, 3 ), ST_Centroid(ST_Force2D(ST_GeomFromGeoJSON($2))), $3 ); ``` - 支援 GeoJSON / GPX / CSV 多種格式匯出,前端可自由選擇下載類型。 ## 成果 ### 首頁、用戶 ![Frame 16 (1)](https://hackmd.io/_uploads/HJGv3ARMee.png) 左圖為首頁畫面,進入後的 Hero Banner 傳達這是關於「山」與「路線」的平台。上方可搜尋用戶或特定路線,下方則列出所有已上傳的軌跡資料,(目前示意為三筆樣本)。 右圖為個人資料頁,包含四個區塊: - 成就:顯示百岳完成進度。 - 統計:呈現近期活動狀況與熱門地區。 - 全部軌跡:以地圖/表格呈現所有資料(同一頁切換)。 - 歷次軌跡:檢視單筆資料,僅登入本人可見非公開資料,確保使用者隱私。 ### 地圖與編輯 ![Frame 17](https://hackmd.io/_uploads/HyWqnR0Ggg.png) 在地圖頁中,點擊表格中的任一路線,地圖會自動 panTo 對應位置,並顯示詳細資料卡片。若使用者已登入,則可進行資料編輯。 ### PWA ![Frame 15 (1)](https://hackmd.io/_uploads/Hkl_WbAzlx.png) 由於地圖頁面在手機瀏覽器中常會受到系統手勢(例如:下滑重新整理)干擾,因此平台支援 PWA,可將網站安裝為原生應用,帶來更穩定的操作體驗。 ## 挑戰 #### 地圖資料展示效能 以個人使用的體感來說,地圖展示效能仍在可接受範圍,但未來隨著路線數量增加,效能瓶頸恐怕會浮現。針對這個問題,我考慮了幾種可能的解法: (1)在儲存階段先簡化路線資料,顯示時再依地圖縮放比例切換不同精度的版本。 (2)改用圖磚方式顯示資料,不過這需要搭配雲端服務或自架伺服器並預處理資料才能提供圖磚。 (3)在地圖縮放到一定比例尺之前僅顯示中心點,等放大後再載入完整路徑。 這些方案目前仍在規劃階段,會視未來資料量成長的實際情況再決定是否實作。 ### 新用戶註冊/OAuth 使用者系統方面,目前只提供登入,還沒有開放註冊。雖然開發初期已經有設定使用者資料表與登入機制,但尚未實作註冊功能。 未來希望能接入 Google 或 LINE 的 OAuth 機制,讓更多人能輕鬆登入並使用。不過這部分我還沒有詳細研究,所以暫時先擱置。 ### 百岳資料尚未串接 在百岳、小百岳、百大步道的資料對應上,目前資料表內已經有對應的欄位(以布林值呈現),但其實並沒有串接實際的路徑清單資料。 這樣的設計在初期是方便的,但實際使用下來,如果同座山多次爬過、並個別上傳,就會在個人成就頁面的圖表中重複統計。 我預計會從「健行筆記」等平台爬取正式的資料清單後匯入資料庫,並將前端的編輯方式改為下拉選單選取對應的路線名稱,讓統計邏輯更加嚴謹。 ## 心得 這是我第一次獨立使用 React 開發多頁式的網頁應用程式,在此之前甚至沒有用過前端框架開發這種規模的專案。從規劃、設計到前後端實作,全都自己來,過程中真的非常耗精力,但也因此對整個開發流程有了完整理解。 我學習了 React 的模組化寫法,知道如何更有效率地拆分與管理元件;也從身邊朋友的使用回饋中,理解到「技術穩不穩」固然重要,但最終還是「有沒有讓使用者看得懂、用得順」才是真的。不過還好有其他人的意見,讓這個作品有了更高的完整度,不是只有我自己看得懂的系統。