我不會 GEE,也能做出土壤含水量監測工具:從 SMAP 衛星資料看見坡地防災的新視角
有些數位轉型,不是從一堂程式課開始的——它可能只是從一個很單純的問題開始:「如果我想知道某個山區過去的土壤有多濕,能不能直接在地圖上點一下,就看到它的變化?」
這次我利用 Google Earth Engine(GEE),串接 NASA 的 SMAP 土壤含水量資料,在地圖上任意點選一個位置,系統就會自動抓出該地點的土壤含水量時間序列,並畫出圖表。
這聽起來像是遙測工程師或資料科學家才會做的事。但真正關鍵的是:我原本並不熟 Google Earth Engine 的 API 語法。
在 AI 協作時代,領域專家不一定要先變成程式設計師,才能開始使用大型遙測資料。真正重要的是,你能不能把問題問清楚,把業務邏輯講明白。
這套工具做了什麼?點地圖,看趨勢
簡單說,它做了三件事:打開 GEE、載入 NASA SMAP 土壤含水量資料,然後讓使用者在地圖上點一個位置,系統就自動畫出那個位置的土壤含水變化。
從第一張實作畫面可以看到,左邊是程式碼區,中間是衛星地圖,右側是互動式圖表面板。在地圖點選山區位置後,畫面上出現紅點,右側面板顯示經緯度並產生兩張土壤含水量圖表。
這樣的操作對防災人員很直覺:不用下載資料、不用處理龐大衛星影像、不用一格一格查表——點地圖,就能看趨勢。這正是 GEE 加上 AI 協作最有價值的地方:把藏在 API 語法裡的資訊,變成領域專家可以直接理解的圖。
SMAP 不是在「看土壤」,而是在讀微波訊號
SMAP 的全名是 Soil Moisture Active Passive,是 NASA 觀測全球土壤含水量的衛星任務。它不是像一般相機那樣「拍到土壤有多濕」——它真正觀測的是地表發出的微波亮溫。
可以這樣理解:乾土和濕土對微波的反應不一樣。水的介電特性很強,所以當土壤裡面的水變多,地表發出的微波訊號就會改變。衛星接收到這些變化後,再透過物理模型反推土壤含水量。
所以 SMAP 不是魔法,它背後是一套完整的物理反演邏輯。而這也是為什麼它對防災有價值——土壤含水量直接關係到坡地穩定、崩塌潛勢、集水區前期飽和狀態,以及豪雨來臨前土地還能不能繼續吸水。
L3 和 L4 差在哪裡?一個像「觀測」,一個像「推估」
這次實作同時使用了 SMAP 的兩種產品。一句話分辨:
L3 比較接近衛星觀測後的地表反演結果;L4 則是把觀測資料丟進水文模型後,產生更連續、更完整的估算結果。
每日地表含水量(0–5 cm)
衛星過境時的實測亮溫,經地表粗糙度與植被覆蓋修正後反演而來。受軌道週期影響,資料呈離散散點,時空不一定連續。
三小時連續根系層含水量(0–100 cm)
將 L3 實測結合氣象資料與陸面水文模型,以 EnKF 演算法進行數據同化,向下推估深層滑動最關鍵的根系層含水狀態。
對防災工作而言,L4 的根系層(0–100 cm)含水量才是重點:它代表坡體已「吸了多少水」,直接影響崩塌的觸發門檻。地表濕了,可能一兩天就退掉;但根系層一旦濕了,代表水已進入更深的土體,接著若又遇到豪雨,坡地的安全邊際就會顯著下降。
9 公里網格的防災治理取捨:能看大勢,不能看單一邊坡
SMAP L4 的空間解析度約為 9 公里,這個數字一定要講清楚,否則很容易誤用。一個 9×9 km 的網格,面積高達 81 平方公里——在台灣山區,可能已涵蓋一整個大型集水區,甚至跨過好幾個村里或不同坡向的山坡。
無法反映百公尺等級的局部崩塌地差異、向陽/背陽坡微氣候,以及特定邊坡的地下水突湧。絕不能拿來做單一邊坡的即時示警指標。
極適合評估颱風前整個大流域(如濁水溪、高屏溪)的集水區前期土壤飽和度(ASI),作為戰略層級的整體環境背景研判。
關鍵洞見在於:同樣是 300 mm降雨,落在乾土上和落在已飽和的土壤上,風險完全不同。當某集水區在颱風前幾週,根系層含水量就已偏高,代表土體吸納雨水的空間已很小——即使後續降雨沒破紀錄,觸發大規模崩塌的機率也會飆升。
SMAP 的防災定位:不是「盯著一個點看即時警報」,而是掌握區域尺度的前期土壤含水背景,作為災害潛勢判斷的補充因子。
AI 協作三步驟:專家負責判斷,AI 負責翻譯成程式
這次開發過程中,AI 最有價值的角色不是「幫我寫幾行程式」,而是把我的防災邏輯翻譯成 GEE 能執行的操作。我不需要一開始就知道所有 API 函數——我只需要清楚描述我要什麼。
AI 協作的精髓:不是讓 AI 自由發揮,而是由領域專家把「資料該怎麼用」、「圖表該怎麼看」、「什麼結果才合理」講清楚。
AI 負責產生程式,專家負責判斷方向。它降低了工具開發的門檻,但沒有取代專業判斷——剛好相反,AI 越強,專業判斷越重要。
最大盲區:GEE 的資料同步時效落差,讓它不能做即時防災監測
摸清工具的能耐後,更要看清數據的硬限制。這是最關鍵的資料治理認知:GEE 平台上的 SMAP 資料,存在不可忽視的同步時間落差。
基準時間 2026 年 6 月,GEE 上最新資料僅至 2025 年 12 月底——落後約半年。
更嚴峻:GEE 上最新資料僅至 2025 年 6 月底——落後整整一年,後續在台灣上空存在物理斷流。
原因在於 L4 是高階同化產品,NASA 必須等全球氣象觀測(GPM 降雨、地表氣壓等)完整收錄並率定後,才能進行多維矩陣計算。這層製程帶有極高的時間滯後,GEE 後台自然無法即時同步。
正確定位:不是即時防汛工具,而是「歷史災害科學回溯」與「AI 防災模型訓練基石」。實務展示或計畫推動時,應採用資料最完整的歷史年度(如 2024 年)。
未來三條最值得投入的佈局路線
「降雨量 + 土壤含水量」雙指標崩塌臨界線(I-D-S Curve)
傳統 I-D 曲線只看降雨,常因無法掌握土體初始含水狀態而空報。引入 L4 深層飽和度,讓分析從「降雨條件」走向「降雨條件 × 前期含水狀態 × 地質地形」,更接近真實災害機制。
以 LSTM 時間序列網路分析颱風後退水速度,找出危險窗口期
很多坡地災害不發生在雨最大的一刻,而是在雨停後深層土壤仍飽和時。利用 L4 三小時資料訓練長短期記憶網路,學習不同地質分區(板岩、砂頁岩互層)的退水係數,對災後警戒解除、道路巡查決策提供科學依據。
流域尺度乾濕循環資料庫:看「太乾」和看「太濕」一樣重要
長時間旱季造成土壤裂隙、植生弱化,等梅雨首波強降雨來時,雨水可能沿裂隙快速入滲誘發淺層崩塌。長期累積下來,可建立台灣不同流域的乾濕循環背景資料庫,作為防災、保育與水資源治理的共同基礎。
這次用 GEE 實作 SMAP 土壤含水量監測工具,最大的收穫不是「做出了一個圖表」。真正的收穫是看清楚三件事:
第一,國際遙測資料已經成熟到可以被公共治理實際使用。第二,AI 可以大幅降低領域專家使用這些資料的門檻。第三,資料一定有尺度與時效限制,不能為了展示效果而過度解讀。
SMAP 的 9 公里網格不是單一邊坡警報器,GEE 上的資料也不是即時防汛系統。但它是一個非常好的歷史資料庫、區域背景分析工具,以及未來 AI 防災模型的重要訓練素材。
這次實作的經驗,另一個重點是我們可以透過 GEE 的實作,完整表達最終想要呈現的東西。之後就可以把這樣的想法經驗,移植到 BigGIS平臺上面,更能完整整合防災方面的需求以及資料的即時性。
用過去的環境資料,訓練未來的防災判斷。
完整 GEE 程式碼:直接複製貼上即可使用
以下是本次實作的完整 Google Earth Engine JavaScript 程式碼,可直接複製到 code.earthengine.google.com 貼上執行。時間範圍(startDate / endDate)可自行調整。
/**
* =========================================================================
* SMAP L3 & L4 台灣土壤含水量動態監測互動工具
* =========================================================================
*/
// 1. 設定空間與時間範圍
var centerPoint = ee.Geometry.Point([120.9605, 23.6978]);
Map.setCenter(120.9605, 23.6978, 8);
Map.style().set('cursor', 'crosshair');
var startDate = '2024-06-01'; //每次執行前,記得更新所需要的起始及結束日期
var endDate = '2024-11-30';
// 2. 載入並處理 SMAP L3 數據 (優化篩選,徹底移除第 27 行的黃色警告)
var smapL3 = ee.ImageCollection('NASA/SMAP/SPL3SMP_E/006')
.filterDate(startDate, endDate)
.map(function(img) {
var sm = img.select('soil_moisture_pm');
var qFlag = img.select('retrieval_qual_flag_pm');
// 直接在回傳時進行遮罩,不建立多餘的變數,避開 GEE 語法檢查器的臭蟲
return sm.updateMask(qFlag.lte(1)).rename('L3_Soil_Moisture_AM')
.copyProperties(img, ['system:time_start']);
});
// 3. 載入並處理 SMAP L4 數據 (將時間戳記正規化)
var smapL4 = ee.ImageCollection('NASA/SMAP/SPL4SMGP/007')
.filterDate(startDate, endDate)
.map(function(img) {
var smSurface = img.select('sm_surface').rename('L4_Surface_SM');
var smRootzone = img.select('sm_rootzone').rename('L4_Rootzone_SM');
var dStr = img.date().format('yyyy-MM-dd');
var tMillis = ee.Date(dStr).millis();
return img.addBands([smSurface, smRootzone])
.select(['L4_Surface_SM', 'L4_Rootzone_SM'])
.set('system:time_start', tMillis);
});
// 4. 計算平均值作為地圖底圖背景
var l3Mean = smapL3.select('L3_Soil_Moisture_AM').mean();
var l4SurfaceMean = smapL4.select('L4_Surface_SM').mean();
var vizParams = {
min: 0.0,
max: 0.5,
palette: ['#f59e0b', '#ffffff', '#2563eb']
};
Map.addLayer(l4SurfaceMean, vizParams, 'SMAP L4 根系層平均土壤含水量 (0-100cm)', true);
Map.addLayer(l3Mean, vizParams, 'SMAP L3 地表平均土壤含水量 (0-5cm)', false);
// 5. 建立互動式 UI 面板
var panel = ui.Panel({
style: {width: '400px', padding: '10px'}
});
var intro = ui.Label({
value: '點擊地圖任意網格,查看 L3 與 L4 土壤含水量時間序列折線圖',
style: {fontSize: '14px', fontWeight: 'bold', color: '#374151'}
});
panel.add(intro);
var lonLabel = ui.Label('經度: --');
var latLabel = ui.Label('緯度: --');
panel.add(lonLabel).add(latLabel);
ui.root.insert(1, panel);
// 6. 設定地圖點擊監聽事件
Map.onClick(function(coords) {
lonLabel.setValue('經度: ' + coords.lon.toFixed(4));
latLabel.setValue('緯度: ' + coords.lat.toFixed(4));
var clickedPoint = ee.Geometry.Point([coords.lon, coords.lat]);
var dot = ui.Map.Layer(clickedPoint, {color: 'FF0000'}, '當前選取點');
Map.layers().set(2, dot);
while (panel.widgets().length() > 3) {
panel.remove(panel.widgets().get(3));
}
// 繪製 L3 時間序列 (散點圖)
var chartL3 = ui.Chart.image.series({
imageCollection: smapL3.select('L3_Soil_Moisture_AM'),
region: clickedPoint,
reducer: ee.Reducer.mean(),
scale: 9000
}).setChartType('ScatterChart')
.setOptions({
title: 'SMAP L3 每日地表土壤含水量 (0-5 cm)',
vAxis: {title: '體積含水量 (cm³/cm³)', viewWindow: {min: 0, max: 0.6}},
hAxis: {title: '時間軸 (年-月)', format: 'yyyy-MM'},
pointSize: 5,
colors: ['#059669']
});
panel.add(chartL3);
// 繪製 L4 時間序列 (LineChart 折線圖)
var chartL4 = ui.Chart.image.series({
imageCollection: smapL4.select(['L4_Surface_SM', 'L4_Rootzone_SM']),
region: clickedPoint,
reducer: ee.Reducer.mean(),
scale: 9000
}).setChartType('LineChart')
.setOptions({
title: 'SMAP L4 三小時連續土壤含水量 (0-5cm vs 0-100cm)',
vAxis: {title: '體積含水量 (cm³/cm³)', viewWindow: {min: 0, max: 0.6}},
hAxis: {
title: '觀測時間 (年-月)',
format: 'yyyy-MM',
gridlines: {color: '#e5e7eb', count: 6},
minorGridlines: {count: 0}
},
focusTarget: 'category',
lineWidth: 1.5,
pointSize: 0,
colors: ['#2563eb', '#dc2626']
});
panel.add(chartL4);
});
沒有留言:
張貼留言