Quickstart

このガイドでは、Google Colab を使用して、Python から Earth Engine REST API へのクエリの発行をすぐに開始する方法について説明します。他の言語や環境から API にアクセスする場合も、同じコンセプトが適用されます。

注: REST API には、すべてのユーザーに適していない可能性のある新しい高度な機能が含まれています。Earth Engine を初めてご利用になる場合は、JavaScript ガイドから始めることをおすすめします。

始める前に

こちらの手順に沿って、次の操作を行います。

  • Earth Engine へのアクセスを構成する
  • サービス アカウントを作成する

Colab ノートブックを設定する

このクイックスタートを最初から始める場合は、Colab のスタートページで [新しいノートブック] をクリックして新しい Colab ノートブックを作成し、新しいコードセルに次のコードサンプルを入力します。Colab には Cloud SDK がすでにインストールされています。これには、Cloud サービスの管理に使用できる gcloud コマンドライン ツールが含まれます。または、このページの上部にあるボタンからデモ ノートブックを実行します。

Google Cloud に対して認証を行う

まず、Google Cloud への認証済みリクエストを作成できるようにログインします。

Colab では、次のコマンドを実行できます。

PROJECT = 'my-project'
!gcloud auth login --project {PROJECT}

(または、ローカルでコマンドラインから実行する場合は、Cloud SDK がインストールされていることを前提としています)。

PROJECT='my-project'
gcloud auth login --project $PROJECT

Google ユーザー アカウントを使用してログインするためのオプションを受け入れ、ログイン プロセスを完了します。

サービス アカウントの秘密鍵ファイルを取得する

サービス アカウントを使用して認証を行うには、まず秘密鍵ファイルをダウンロードする必要があります。Colab でノートブック VM にダウンロードするには:

SERVICE_ACCOUNT='foo-name@project-name.iam.gserviceaccount.com'
KEY = 'my-secret-key.json'
!gcloud iam service-accounts keys create {KEY} --iam-account {SERVICE_ACCOUNT}

または、コマンドラインから次のコマンドを実行します。

SERVICE_ACCOUNT='foo-name@project-name.iam.gserviceaccount.com'
KEY='my-secret-key.json'
gcloud iam service-accounts keys create $KEY --iam-account $SERVICE_ACCOUNT

認証情報へのアクセスとテスト

これで、Earth Engine API に最初のクエリを送信する準備が整いました。秘密鍵を使用して認証情報を取得します。認証情報を使用して、HTTP リクエストを行うための認証済みセッションを作成します。これは、Colab ノートブックの新しいコードセルに入力できます。(コマンドラインを使用している場合は、必要なライブラリがインストールされていることを確認する必要があります)。

from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file(KEY)
scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/cloud-platform'])

session = AuthorizedSession(scoped_credentials)

url = 'https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/LANDSAT'

response = session.get(url)

from pprint import pprint
import json
pprint(json.loads(response.content))

すべてが正しく構成されている場合、このコマンドを実行すると次のような出力が生成されます。

{'id': 'LANDSAT',
 'name': 'projects/earthengine-public/assets/LANDSAT',
 'type': 'FOLDER'}

データセットを選択する

code.earthengine.google.comEarth Engine コードエディタを使用して、利用可能なデータセットを検索して探索できます。Sentinel 2 のデータをいくつか探してみましょう。(コードエディタを初めて使用する場合は、ログイン時に、ユーザーに代わって Earth Engine にアクセスすることを承認するよう求められます)。コード エディタで、上部の検索ボックスに「sentinel」と入力して検索します。複数のラスター データセットが表示されます。

[Sentinel-2: MultiSpectral Instrument (MSI), Level-1C] をクリックします。

このようなデータセットの説明ページには、Earth Engine 公開データ カタログ内のデータセットを使用するために必要な重要な情報が記載されています。たとえば、データセットの簡単な説明、詳細情報を取得するためのデータ プロバイダへのリンク、データセットに適用される可能性のある使用制限に関する情報、データセットの Earth Engine アセット ID などです。

この場合、ウィンドウの右側に、パスが COPERNICUS/S2 の画像コレクション アセットであることが表示されます。

特定の画像をクエリする

この Sentinel-2 データセットには、2015 年から現在までの世界をカバーする 200 万枚以上の画像が含まれています。projects.assets.listImages クエリを画像コレクションに対して発行し、カリフォルニア州マウンテン ビューの特定のポイントを含む、雲量が少ない 2017 年 4 月のデータを検索します。

import urllib

coords = [-122.085, 37.422]

project = 'projects/earthengine-public'
asset_id = 'COPERNICUS/S2'
name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}:listImages?{}'.format(
  name, urllib.parse.urlencode({
    'startTime': '2017-04-01T00:00:00.000Z',
    'endTime': '2017-05-01T00:00:00.000Z',
    'region': '{"type":"Point", "coordinates":' + str(coords) + '}',
    'filter': 'CLOUDY_PIXEL_PERCENTAGE < 10',
}))

response = session.get(url)
content = response.content

for asset in json.loads(content)['images']:
    id = asset['id']
    cloud_cover = asset['properties']['CLOUDY_PIXEL_PERCENTAGE']
    print('%s : %s' % (id, cloud_cover))

このスクリプトは、一致する画像をコレクションにクエリし、結果の JSON レスポンスをデコードして、一致する各画像アセットのアセット ID と雲量を印刷します。出力は次のようになります。

COPERNICUS/S2/20170420T184921_20170420T190203_T10SEG : 4.3166
COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG : 0

この地点の上空には、今月撮影された雲量の少ない画像が 2 枚あるようです。

特定の画像を検査する

一致するものの 1 つは、雲量がほぼゼロのようです。ID が COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG のアセットを詳しく見てみましょう。すべての公開カタログ アセットはプロジェクト earthengine-public に属します。次の Python スニペットは、projects.assets.get クエリを発行して、ID で特定のアセットの詳細を取得し、使用可能なデータバンドを出力して、最初のバンドに関する詳細情報を出力します。

asset_id = 'COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG'
name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}'.format(name)

response = session.get(url)
content = response.content

asset = json.loads(content)
print('Band Names: %s' % ','.join(band['id'] for band in asset['bands']))
print('First Band: %s' % json.dumps(asset['bands'][0], indent=2, sort_keys=True))

出力は次のようになります。

Band Names: B1,B2,B3,B4,B5,B6,B7,B8,B8A,B9,B10,B11,B12,QA10,QA20,QA60
First Band: {
  "dataType": {
    "precision": "INTEGER",
    "range": {
      "max": 65535
    }
  },
  "grid": {
    "affineTransform": {
      "scaleX": 60,
      "scaleY": -60,
      "translateX": 499980,
      "translateY": 4200000
    },
    "crsCode": "EPSG:32610",
    "dimensions": {
      "height": 1830,
      "width": 1830
    }
  },
  "id": "B1",
  "pyramidingPolicy": "MEAN"
}

データバンドのリストは、前述のデータセットの説明で確認した内容に対応しています。このデータセットには、EPSG:32610 座標系(UTM ゾーン 10N)の 16 ビット整数データが含まれていることがわかります。この最初のバンドの ID は B1 で、解像度は 1 ピクセルあたり 60 メートルです。この座標系では、画像の原点は位置(499980,4200000)にあります。

affineTransform.scaleY の負の値は、通常どおり、原点が画像の北西隅にあることを示します。y ピクセル インデックスの増加は、y 空間座標の減少(南向き)に対応します。

ピクセル値の取得

projects.assets.getPixels クエリを発行して、この画像の高解像度バンドからデータを取得しましょう。データセットの説明ページには、バンド B2B3B4B8 の解像度が 1 ピクセルあたり 10 メートルと記載されています。このスクリプトは、これらの 4 つのバンドからデータの左上 256x256 ピクセルタイルを取得します。numpy NPY 形式でデータを読み込むと、レスポンスを Python データ配列に簡単にデコードできます。

import numpy
import io

name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}:getPixels'.format(name)
body = json.dumps({
    'fileFormat': 'NPY',
    'bandIds': ['B2', 'B3', 'B4', 'B8'],
    'grid': {
        'affineTransform': {
            'scaleX': 10,
            'scaleY': -10,
            'translateX': 499980,
            'translateY': 4200000,
        },
        'dimensions': {'width': 256, 'height': 256},
    },
})

pixels_response = session.post(url, body)
pixels_content = pixels_response.content

array = numpy.load(io.BytesIO(pixels_content))
print('Shape: %s' % (array.shape,))
print('Data:')
print(array)

出力は次のようになります。

Shape: (256, 256)
Data:
[[( 899, 586, 351, 189) ( 918, 630, 501, 248) (1013, 773, 654, 378) ...,
  (1014, 690, 419, 323) ( 942, 657, 424, 260) ( 987, 691, 431, 315)]
 [( 902, 630, 541, 227) (1059, 866, 719, 429) (1195, 922, 626, 539) ...,
  ( 978, 659, 404, 287) ( 954, 672, 426, 279) ( 990, 678, 397, 304)]
 [(1050, 855, 721, 419) (1257, 977, 635, 569) (1137, 770, 400, 435) ...,
  ( 972, 674, 421, 312) (1001, 688, 431, 311) (1004, 659, 378, 284)]
 ...,
 [( 969, 672, 375, 275) ( 927, 680, 478, 294) (1018, 724, 455, 353) ...,
  ( 924, 659, 375, 232) ( 921, 664, 438, 273) ( 966, 737, 521, 306)]
 [( 920, 645, 391, 248) ( 979, 728, 481, 327) ( 997, 708, 425, 324) ...,
  ( 927, 673, 387, 243) ( 927, 688, 459, 284) ( 962, 732, 509, 331)]
 [( 978, 723, 449, 330) (1005, 712, 446, 314) ( 946, 667, 393, 269) ...,
  ( 949, 692, 413, 271) ( 927, 689, 472, 285) ( 966, 742, 516, 331)]]

この画像から別のピクセルセットを選択するには、それに応じて affineTransform を指定します。affineTransform は画像の空間座標参照系で指定されることに注意してください。代わりにピクセル座標で原点の位置を調整する場合は、次の簡単な式を使用します。

request_origin = image_origin + pixel_scale * offset_in_pixels

サムネイル画像を生成する

同様のメカニズムを使用して、この画像の RGB サムネイルを生成できます。ネイティブ解像度でデータをリクエストする代わりに、リージョンと画像寸法を明示的に指定します。画像全体のサムネイルを取得するには、画像フットプリントのジオメトリをリクエスト領域として使用します。最後に、赤、緑、青の画像バンドと適切なデータ値の範囲を指定することで、魅力的な RGB サムネイル画像を取得できます。

これらをまとめると、Python スニペットは次のようになります(Colab の IPython 画像表示ウィジェットを使用)。

url = 'https://earthengine.googleapis.com/v1alpha/{}:getPixels'.format(name)
body = json.dumps({
    'fileFormat': 'PNG',
    'bandIds': ['B4', 'B3', 'B2'],
    'region': asset['geometry'],
    'grid': {
        'dimensions': {'width': 256, 'height': 256},
    },
    'visualizationOptions': {
        'ranges': [{'min': 0, 'max': 3000}],
    },
})

image_response = session.post(url, body)
image_content = image_response.content

from IPython.display import Image
Image(image_content)

生成されたサムネイル画像は次のようになります。