iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
佛心分享-SideProject30

30 天開發一款真正能每天使用的散步 App系列 第 15

外面在下雨?結合天氣給出散步建議

  • 分享至 

  • xImage
  •  

前面已經完成自由散步與推薦路線,但真正準備出門時,還有一個很實際的問題:

現在適合散步,不代表 10、30 分鐘後也適合。

如果只顯示目前位置的即時天氣,可能出發時沒有下雨,走到一半降雨機率卻開始升高;推薦路線走得比較遠時,路線後段的天氣也不一定和起點相同。

因此這次不另外做一個天氣頁面,而是在開始散步前,根據散步模式、位置與時間取得相關的天氣預報,需要時再提醒使用者。

取得天氣預報與警報

這次使用 Google Weather API,需要先到 Google Cloud Console 啟用 Weather API。

會用到兩種資料:

  • 逐時天氣預報:取得氣溫、體感溫度、降雨機率、雷雨機率、風速與 UV。
  • 天氣警報:確認散步期間是否有豪雨、雷雨等警報。

取得逐時天氣預報

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"
    }
  ]
}

如果警報的生效時間和散步時間重疊,就提高提醒等級。

一般降雨可能只需要提醒攜帶雨具,但如果散步期間有較嚴重的天氣警報,就可以改成不建議出發,並讓使用者再次確認是否仍要開始。

自由散步要怎麼判斷天氣?

推薦路線可以事先知道路徑與預估時間,但自由散步開始前不知道使用者會往哪裡走,也不知道最後會走多久。

所以自由散步分成開始前與散步途中兩個階段處理。

開始前

開始自由散步前,先使用:

  • 使用者目前的 GPS 位置
  • 現在時間
  • 未來 60 分鐘的逐時預報

送給後端的資料大致如下:

{
  "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 分鐘:

  • 起點:現在抵達
  • 中點:約 30 分鐘後抵達
  • 終點:約 60 分鐘後抵達

如果三個位置全部都查現在的天氣,走到中點或終點時,實際天氣可能已經不同。

所以每個取樣點還需要估算使用者大約什麼時候會走到這裡。

目前使用該點在整條路線上的累積距離比例計算:

const arrivalOffsetSeconds =
  routeDurationSeconds *
  (point.cumulativeDistanceMeters /
    routeDistanceMeters);

再加上預計開始散步的時間:

const expectedArrivalAt = new Date(
  startsAt.getTime() +
    arrivalOffsetSeconds * 1000,
);

例如某個取樣點位於整條路線 50% 的位置,而路線預估需要 60 分鐘,就先把這個位置的抵達時間估成出發後 30 分鐘。

這裡先假設整段路線的行走速度差不多,所以算出來的不是精確 ETA,只是用來決定這個位置應該查哪個時間的天氣。

因此一條 60 分鐘的路線,最後查詢的會是:

  1. 起點現在的天氣
  2. 中點約 30 分鐘後的天氣
  3. 終點約 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,因為沒有取得資料並不代表那個位置的天氣沒有問題。

後續可以再調整的地方

目前這套機制已經可以處理大部分的情境,不過還有兩個地方之後可以繼續優化:

API 快取

如果附近有其他使用者在差不多時間散步,其實不需要每次都重新查詢 Weather API。

之後可以在後端把經緯度轉成 GeoHash 網格,並將抵達時間對齊到 30 分鐘的時間區間做快取。這樣只要地點與時間相近,就可以直接使用快取的天氣資料,減少 API 查詢次數。

動態調整抵達時間

目前預估抵達時間時,是假設整段路線都以勻速行走。但實際散步時,使用者可能會停下來休息、買東西,或是遇到坡度較大的路段而走得比較慢。

如果散步途中發現實際進度與原本估算落後太多,後續也可以利用途中取得的最新位置,重新計算剩餘路線的抵達時間,再更新對應的天氣預報。

小結

這次除了串接 Weather API,主要還要處理「要查哪裡」和「要查什麼時間」這兩個問題。

自由散步沒有預先路線,所以開始前先用目前位置查看未來一段時間的天氣,開始後再跟著最新 GPS 位置更新。

推薦路線則可以利用已經產生好的路線,在途中選幾個位置,再根據累積距離估算使用者大約什麼時候會抵達,查詢對應時間的天氣。

這樣使用者在開始散步前看到的就不只是現在的天氣,也能提前知道這趟散步途中可能遇到的天氣變化。


上一篇
推薦路線(5)——實作路線導航與語音提醒
下一篇
根據散步習慣,自動決定提醒時間
系列文
30 天開發一款真正能每天使用的散步 App18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言