前面已經完成自由散步與推薦路線,但真正準備出門時,還有一個很實際的問題:
現在適合散步,不代表 10、30 分鐘後也適合。
如果只顯示目前位置的即時天氣,可能出發時沒有下雨,走到一半降雨機率卻開始升高;推薦路線走得比較遠時,路線後段的天氣也不一定和起點相同。
因此這次不另外做一個天氣頁面,而是在開始散步前,根據散步模式、位置與時間取得相關的天氣預報,需要時再提醒使用者。

這次使用 Google Weather API,需要先到 Google Cloud Console 啟用 Weather API。
會用到兩種資料:
Google Weather API 提供逐時預報:
GET https://weather.googleapis.com/v1/forecast/hours:lookup
查詢時傳入經緯度與需要的預報時數:
GET /v1/forecast/hours:lookup
?key=API_KEY
&location.latitude=25.033
&location.longitude=121.5654
&hours=2
Response 很長,目前只會用到其中幾個欄位:
{
"forecastHours": [
{
"interval": {
"startTime": "2026-07-22T07:00:00Z"
},
"displayTemperature": {
"degrees": 30
},
"feelsLikeTemperature": {
"degrees": 35
},
"precipitation": {
"probability": {
"percent": 70
}
},
"thunderstormProbability": {
"percent": 40
},
"wind": {
"speed": {
"value": 18
}
},
"uvIndex": 9
}
]
}
取得這些資料後,再根據散步時間判斷是否需要提醒,例如接下來降雨機率升高、可能有雷雨,或體感溫度過高。
除了逐時預報,也會另外查詢天氣警報:
GET https://weather.googleapis.com/v1/publicAlerts:lookup
?key=API_KEY
&location.latitude=25.033
&location.longitude=121.5654
目前只會用到 response 中和警報內容、生效時間有關的資料:
{
"alerts": [
{
"event": "Heavy Rain Warning",
"severity": "SEVERE",
"headline": "Heavy rain expected",
"effectiveTime": "2026-07-22T08:00:00Z",
"expireTime": "2026-07-22T14:00:00Z"
}
]
}
如果警報的生效時間和散步時間重疊,就提高提醒等級。
一般降雨可能只需要提醒攜帶雨具,但如果散步期間有較嚴重的天氣警報,就可以改成不建議出發,並讓使用者再次確認是否仍要開始。

推薦路線可以事先知道路徑與預估時間,但自由散步開始前不知道使用者會往哪裡走,也不知道最後會走多久。
所以自由散步分成開始前與散步途中兩個階段處理。
開始自由散步前,先使用:
送給後端的資料大致如下:
{
"mode": "free",
"latitude": 25.033,
"longitude": 121.5654,
"startsAt": "2026-07-22T07:00:00.000Z",
"durationSeconds": 3600
}
自由散步開始前還不知道使用者會走多久,所以目前先查未來 60 分鐘的天氣,避免只判斷出發當下的狀況。
開始散步後,就已經知道使用者目前走到哪裡,因此後續查詢可以直接改用最新位置。
目前每 30 分鐘取最新一筆通過 GPS 過濾的位置,重新檢查接下來 30 分鐘的天氣:
{
"mode": "free",
"latitude": 25.041,
"longitude": 121.572,
"startsAt": "2026-07-22T07:20:00.000Z",
"durationSeconds": 1800,
"previousAdvisoryLevel": "suitable",
"previousHazardCodes": []
}
這樣使用者離開出發地後,後續提醒也會跟著目前的位置更新,不會一直使用起點附近的天氣。
目前先設定每 30 分鐘更新一次,之後再看實際使用情況調整。
推薦路線在開始前就已經知道完整 Polyline、總距離與預估時間,所以除了起點,也可以一起檢查沿途的天氣。
不過沒有必要把 Polyline 上的每個座標都拿去查 Weather API,因此目前會根據路線距離選出幾個位置:
const ROUTE_WEATHER_SAMPLE_DISTANCE_METERS = 3000;
const MAX_ROUTE_WEATHER_SAMPLE_POINTS = 5;
短路線只取少量位置,路線比較長時再增加途中的取樣點,最多取五個。目前先以每 3 公里作為取樣間距,之後再根據實際使用情況調整。
前端不需要自己計算這些位置,只要把推薦路線 ID 傳給後端:
{
"mode": "recommended",
"generatedRouteId": "route-id",
"startsAt": "2026-07-22T07:00:00.000Z"
}
推薦路線建立時,後端已經保存路線點、累積距離、總距離與預估時間,因此查詢天氣時可以直接從這些資料找出要取樣的位置。
找到路線上的取樣點後,還有時間需要處理。
假設一條路線預計走 60 分鐘:
如果三個位置全部都查現在的天氣,走到中點或終點時,實際天氣可能已經不同。
所以每個取樣點還需要估算使用者大約什麼時候會走到這裡。
目前使用該點在整條路線上的累積距離比例計算:
const arrivalOffsetSeconds =
routeDurationSeconds *
(point.cumulativeDistanceMeters /
routeDistanceMeters);
再加上預計開始散步的時間:
const expectedArrivalAt = new Date(
startsAt.getTime() +
arrivalOffsetSeconds * 1000,
);
例如某個取樣點位於整條路線 50% 的位置,而路線預估需要 60 分鐘,就先把這個位置的抵達時間估成出發後 30 分鐘。
這裡先假設整段路線的行走速度差不多,所以算出來的不是精確 ETA,只是用來決定這個位置應該查哪個時間的天氣。
因此一條 60 分鐘的路線,最後查詢的會是:
這樣就不會只知道「這些地方現在的天氣」,而是能對應到使用者實際走到附近時的預報。

每個取樣點查完後,都會得到自己的天氣建議,例如:
起點:suitable
中段:caution
終點:not_recommended
最後再從中找出最嚴重的結果,作為整條路線的提醒等級:
const advisorySeverity = {
suitable: 1,
caution: 2,
not_recommended: 3,
};
以上面的結果來說,最後會得到 not_recommended。
除了提醒等級,也會保留風險出現的位置、時間與原因。
這樣畫面上就可以顯示:
路線後段可能出現雷雨,建議稍後再出發。
而不是只顯示「天氣可能不佳」。
推薦路線一次可能會查好幾個位置,如果其中一個 Weather API request 失敗,但其他位置都有正常取得資料,就不需要把整次結果都丟掉。
例如五個取樣點中有一個失敗,其他四個都有結果,response 會標記成:
{
"available": true,
"partiallyAvailable": true,
"advisory": {
"level": "caution"
}
}
available 代表目前仍有資料可以判斷天氣,partiallyAvailable 則表示部分位置沒有成功取得資料。
只有全部取樣點都查詢失敗時,才會回傳 unavailable。
另外查詢失敗的取樣點也不會直接當成 suitable,因為沒有取得資料並不代表那個位置的天氣沒有問題。

目前這套機制已經可以處理大部分的情境,不過還有兩個地方之後可以繼續優化:
如果附近有其他使用者在差不多時間散步,其實不需要每次都重新查詢 Weather API。
之後可以在後端把經緯度轉成 GeoHash 網格,並將抵達時間對齊到 30 分鐘的時間區間做快取。這樣只要地點與時間相近,就可以直接使用快取的天氣資料,減少 API 查詢次數。
目前預估抵達時間時,是假設整段路線都以勻速行走。但實際散步時,使用者可能會停下來休息、買東西,或是遇到坡度較大的路段而走得比較慢。
如果散步途中發現實際進度與原本估算落後太多,後續也可以利用途中取得的最新位置,重新計算剩餘路線的抵達時間,再更新對應的天氣預報。
這次除了串接 Weather API,主要還要處理「要查哪裡」和「要查什麼時間」這兩個問題。
自由散步沒有預先路線,所以開始前先用目前位置查看未來一段時間的天氣,開始後再跟著最新 GPS 位置更新。
推薦路線則可以利用已經產生好的路線,在途中選幾個位置,再根據累積距離估算使用者大約什麼時候會抵達,查詢對應時間的天氣。
這樣使用者在開始散步前看到的就不只是現在的天氣,也能提前知道這趟散步途中可能遇到的天氣變化。