Google Earth EngineのデータをBigQueryに連携してみる
この記事は Google Cloud Japan Advent Calendar 2023 (入門編) の 13 日目の記事です。
みなさん、Google Earth Engineというサービスをご存知でしょうか。
Google Earth Engineは地球の様子を可視化、分析できるクラウドベースの地理空間分析プラットフォームです。本稿では、Google Earth Engineで分析した結果をBigQueryに連携し、連携したデータとBigQuery内のデータを組み合わせて可視化します。
tl;dr
- Google Earth Engineで衛星画像を処理して簡単にBigQueryにデータ連携できる
- 連携した地理空間データはBigQueryの地理関数とBigQuery内のデータを使って分析できる
- BigQueryの地理空間データは簡単に可視化できる
Google Earth Engineとは
地球の様子を衛星画像などによって可視化するツールというとGoogle Earth を思い浮かべる方も多いのではないでしょうか。Google Earthは衛星画像などを視覚化して操作できるツールで、Google Earth Engineは40 年以上前から現在までの衛星画像や天候、地形といった地理空間データを分析するためのツールです。
多くの科学者や非営利機関が、リモート センシング調査、伝染病流行の予測、天然資源の管理などで Google Earth Engine を利用しています。以前は非営利および研究目的の使用では無償で提供していましたが、現在では政府及び企業向けに有償で商用利用することも可能になりました。
早速使ってみる
本稿では、米国時間 2023 年 7 月 6 日に、Google Cloud blog に投稿された内容を元に、以下の手順で冠水した道路の区間を可視化します。
- 大雨前後の衛星画像データから浸水地域を識別
- 識別結果BigQueryにデータ連携
- BigQueryのデータと連携したデータから冠水道路区間を算出する
- 冠水道路区間を可視化
前提条件
- 連携するGoogle Cloud プロジェクトを作成し、BigQuery API と Earth Engine API を有効にします。
- Google Earth Engineへのアクセスを構成します。
- 連携するBigQueryのデータセットを作成します。
- Google Earth Engine のコードエディタを使用するか、 Colab 環境でほぼ同等の Python を使用して同じ手順を実施できます。
注意:この例を実施するには、Google Cloudのプロジェクトで請求先アカウントを有効にする必要があり、Google Earth Engine のコンピューティング、BigQuery ストレージ、BigQuery のコンピューティング、ネットワーク トラフィックに対する料金が発生します。
Google Earth Engineで浸水地域を識別する
今回は水域を算出できるCopernicus Sentinel 衛星の合成開口レーダーデータを利用します。
Google Earth Engineのコードエディタにアクセスします。
内容は実行後確認していきますので、ここでは以下のスクリプトを “Run” します。
以下のスクリプトでは、連携先テーブルを your_project.your_dataset.your_table
としています。
// 対象地域(「AOI」)ポリゴンはインポートまたは手書きも可能。
var aoi = ee.Geometry.Polygon(
[[[-2.92, 54.10],
[-2.92, 53.99],
[-2.67, 53.99],
[-2.67, 54.10]]], null, false);
// Sentinel-1 コレクションの読み込み(対数スケール、VV 共偏波)。
var collection = ee.ImageCollection('COPERNICUS/S1_GRD')
.filterBounds(aoi)
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV'))
.select('VV');
// データを平滑化してノイズを除去。
var SMOOTHING_RADIUS_METERS = 100;
// 日付(大雨の前後)でフィルタ。
var before = collection.filterDate('2017-11-01', '2017-11-17')
.mosaic()
.focalMedian(SMOOTHING_RADIUS_METERS, 'circle', 'meters');
var after = collection.filterDate('2017-11-18', '2017-11-23')
.mosaic()
.focalMedian(SMOOTHING_RADIUS_METERS, 'circle', 'meters');
// 平滑化するレーダー強度のしきい値を設定し、浸水地域を識別。
var DIFF_THRESHOLD_DB = -3;
var diffSmoothed = after.subtract(before);
var diffThresholded = diffSmoothed.lt(DIFF_THRESHOLD_DB);
// 地表水(海、湖など)を削除。
var jrcData0 = ee.Image('JRC/GSW1_0/Metadata')
.select('total_obs')
.lte(0);
var waterMask = ee.Image('JRC/GSW1_0/GlobalSurfaceWater')
.select('occurrence')
.unmask(0)
.max(jrcData0)
.lt(50); // 持続的な水域(50% 以上)をマスク。
var floodedPixels = diffThresholded.updateMask(waterMask);
// 斑点部分のピクセルをポリゴン(ベクター)に変換。
var vectors = floodedPixels.reduceToVectors({
geometry: aoi,
scale: 10,
geometryType: 'polygon',
eightConnected: false // ピクセルが端を共有している場合にのみ接続。
});
// データセットの大規模な対象を除外。
var MAX_AREA = 500 * 1000; // m^2
vectors = vectors.map(function (f) {
return f.set('area', f.geometry().area(10));
}).filter(ee.Filter.lt("area", MAX_AREA));
Map.centerObject(aoi, 13); // AOI をズームレベル 13 で表示。
Map.addLayer(before, {min: -30, max: 0}, 'Before rains');
Map.addLayer(after, {min: -30, max: 0}, 'After rains');
Map.addLayer(diffSmoothed, {min: -10, max: 10}, 'Difference', false);
Map.addLayer(vectors, {color: 'blue'}, 'Flooded areas');
Export.table.toBigQuery({
collection: vectors,
description:'ee2bq_export_polygons',
// 連携先のプロジェクトとデータセット名、テーブル名。
table: 'your_project.your_dataset.your_table'
});
スクリプトを “Run” するとそれぞれの分析結果として以下の4つのレイヤーが作成されます。
実行した内容を各レイヤーの画像を元に確認していきましょう。
まずは、データの読み込みと分析のため画像を平準化しています。
以下は、平準化済みの大雨が降る前のレイヤー (Before rains) です。
次は、平準化済みの大雨が降った後のレイヤー (After rains) を確認します。
何かが変化しているのはわかります。大雨前後のレイヤーの差分 (Difference) を見てみましょう。
2つのレイヤーの変化が大きく、暗くなっている部分が浸水地域となります。上記のDifferenceレイヤーから、地表水のデータセットを使用して、元々水域である場所(川や湖など)を取り除き、浸水した地域を抽出します。
浸水地域を画像データから BigQuery で利用できる形式のデータ(ベクターデータ)に変換します。
以下は画像データの暗い部分をポリゴンと呼ばれるベクターデータに変換する例です。青い点の集まりがポリゴンになります。
地図上に浸水地域を青く表示したレイヤー (Flooded areas)が以下になります。
ここまでで浸水地域を抽出できたので、次にBigQueryにデータ連携します。
識別結果を BigQuery に連携する
前述のスクリプトの最後に Export.table.toBigQuery()
という関数があります。
この関数を実行するだけで、 BigQuery にデータ連携されます。
前提条件に記載のとおり、事前にデータ連携する先のプロジェクトと BigQuery のデータセットは作成しておきます。テーブルは未作成で問題ありません。Google Earth Engine にログインしているアカウントが、連携先の BigQuery にテーブルを作成できる権限を持っている必要があります。
スクリプトを “Run” すると、タスクとして登録されている状態になっており、実際に連携する処理を動かすにはコードエディターの右上の “Task” タブの “UNSUBMITTED TASK” にある “ee2bq_export_ploygons” を “Run” します。
Task が完了したら、BigQueryを確認してみましょう。
以下の “Open in BigQuery” から遷移できます。
Export.table.toBigQuery()
で指定したデータセットにテーブルが作成されています。
作成されたテーブルのフィールドは以下となります。
プレビューを見ると、以下のようなデータが連携されています。
これで、BigQueryに浸水地域のデータを連携できました。
次は、BigQueryのPublic SchemaにあるOpenStreetMapのデータと組み合わせて、冠水道路区間を算出します。
冠水道路区間を算出する
以下のSQLを実行して、Google Earth Engineで分析した浸水地域と OpenStreetMap 道路情報をBigQueryの地理関数を使って結合することで、 ”浸水した道路(冠水道路区間)” を算出します。
SELECT
road_geometry
FROM (
-- クエリ 1 - すべての浸水地域を検出
SELECT
geo AS flood_poly
FROM
`your_dataset.your_table`) t1
JOIN (
-- クエリ 2 - Open Street Map ですべての幹線道路を検出 -
SELECT
id,
version,
changeset,
osm_timestamp,
geometry as road_geometry
FROM
`bigquery-public-data.geo_openstreetmap.planet_ways` planet_ways,
planet_ways.all_tags AS all_tags
WHERE
-- このタグですべてのタイプの道路を取得 https://wiki.openstreetmap.org/wiki/Map_features
all_tags.key = 'highway' )
ON
ST_INTERSECTS(flood_poly, road_geometry)
;
SQLを実行すると、線を表す地理空間データが表示されます。
このままでは見てもわからないので、地図上に可視化しましょう。
BigQuery Geo Vizで冠水道路区間を可視化する
簡単にBigQuery の地理空間データを可視化できる BigQuery Geo Viz というウェブツールを使います。BigQueryの実行結果の右側にある ”データを探索” から “GeoViz で調べる” をクリックします。
BigQuery Geo Vizの画面に移動すると、認証後、BigQueryで実行したSQLが Query に表示されるので、 “Run” しましょう。
実行が完了すると、 BigQuery Geo Vizの地図上に、今回算出した冠水道路区間が表示されます。
以上で、Google Earth Engineの分析結果をBigQueryに連携して可視化する手順は完了です。
まとめ
本稿では、Google Earth Engineを使って分析した結果をBigQueryに連携し、更にBigQueryのデータを組み合わせて詳細化した情報を地図上に可視化しました。
今回の例のように、Google Earth Engineで扱える豊富な衛星画像データも、BigQueryで扱えるデータへの変換が可能ですし、他にも降水量や地表温度のような気象データとBigQueryにある自身のデータを組み合わせ、機械学習などを用いた高度な分析をすることで、新たな発見が得られるかもしれません。
Discussion