宙畑 Sorabatake

Tellusの使い方

【ゼロからのTellusの使い方】AVNIR-2のオルソ補正画像プロダクトを表示させる

JAXAの「だいち」という衛星に搭載されたAVNIR-2(あぶにーるつー)というセンサのオリジナルデータのAPIをTellus上で公開しました!その使い方をご紹介します。

TellusでJAXA提供のAVNIR-2のオリジナルデータ(GeoTiff)を利用できるようになりました。オリジナルデータを利用することで、これまでよりも広い範囲のデータを一度に扱えるようになります。

本記事では、開発環境でAVNIR-2(オルソ補正画像プロダクト)のオリジナルデータを取得する方法を紹介します。

AVNIR-2はJAXAが提供する衛星データです。Tellus内で利用が可能で、二次成果物を作成した際は、(c)JAXAの表記が必要となります。詳しくはTellusのデータカタログをご覧ください。

AVNIR-2(オルソ補正画像プロダクト)のオリジナルデータを取得してみよう

オリジナルデータは、次の3ステップで取得できます。

1. ファイル検索
取得したい条件に一致するファイルのメタ情報を検索します。

2.ダウンロードURL発行
ファイルのメタ情報から、ファイルをダウンロードするためのURLを発行します。

3.ダウンロード
ファイル取得URLからファイルを開発環境にダウンロードし、保存します。

Tellusで開発環境(Jupyter Lab)を使う方法は、「Tellusの開発環境でAPI引っ張ってみた」をご覧ください。

ファイル検索API

ファイル検索は、以下のAPIを利用します。

https://file.tellusxdp.com/api/v1/origin/search/avnir2-ori

引数

名称 クエリ 絞り込み条件
検索開始時刻 after str(ISO8601) observation_datetimeがこれ以降なら返却します。
検索終了時刻 before str(ISO8601) observation_datetimeがこれ以前なら返却します。
雲量 cloud_cover float cloud_coverがこの値以下のものを返却します。
引数が有効な値だった場合、エラー値(99)は返却されません。
RSPパス番号 rsp_path_number int 完全一致
RSPフレーム番号 rsp_frame_number int 完全一致
最小ポインティング角 min_pointing_angle float pointing_angleがこの値以上のものを返却します。
最大ポインティング角 max_pointing_angle float pointing_angleがこの値以下のものを返却します。
シーンID scene_id str 完全一致
検索範囲 left_bottom_lon
right_top_lon
left_bottom_lat
right_top_lat
アウトライン(min_lat, min_lon, max_lat, max_lon)の矩形が一部でもこの範囲に含まれていれば返却します。
https://tools.ietf.org/html/rfc7946#section-5
取得数 page_size int 1回のリクエストで取得する件数を指定できます。
デフォルト100件
最大1000件

返却値

データ名称 プロパティ名 型とサンプル
位置(四隅) coordinates array of float

[
[左下経度, 左下緯度],
[右下経度, 右下緯度],
[右上経度, 右上緯度],
[左上経度, 左上緯度]
]

例)
[
[138.9756564, 36.3759473],
[139.8980412, 36.174298],
[140.1360529, 36.8693836],
[139.2058268, 37.0726605]
]

シーン中心時刻(UTC) center_datetime str(ISO8601)

例) 2006-11-10T01:48:05.325279+00:00

軌道方向 orbit_direction str

Dがディセンディング軌道、 Aがアセンディング軌道

RSPパス番号 rsp_path_number int
RSPフレーム番号 rsp_frame_number int
シーン中心通算軌道番号 total_orbit_number int
シーン中心フレーム番号 frame_number int
シーンシフト量 scene_shift int
雲量参考情報 cloud_cover int
太陽仰角 sun_elevation_angle float
太陽方位角 sun_azimuth_angle float
ポインティング角 pointing_angle float
各バンド値のゲイン gain array of float

[band1, band2, band3, band4]

例) [0.588, 0.573, 0.502, 0.557]

各バンド値のオフセット offset array of float

[band1, band2, band3, band4]

例) [0.0, 0.0, 0.0, 0.0]

シーンID scene_id str

例) ALAV2A042302850

dataset名 dataset_id str

例) ALAV2A042302850-OORIRFU-D075P0-20061110-001

データセットのファイル一覧 files array of string
[
filename1, filename2, …
]例)
[
‘IMG-02-ALAV2A042302850-OORIRFU-D075P0-20061110-001.tif’,
… ,
‘HDR-ALAV2A042302850-OORIRFU-D075P0-20061110-001.txt’
]
Tellusでの公開日 date_added str(ISO8601)
代表時刻 observation_datetime str(ISO8601)

center_datetimeと同じ値です。

範囲 bbox array of float

[left_lon, bottom_lat, right_lon, top_lat]

例)

[138.9756564, 36.174298, 140.1360529, 37.0726605]

発行リンク publish_url 例)

https://file.tellusxdp.com/api/v1/origin/publish/avnir2-ori/ALAV2A042302850-OORIRFU-D075P0-20061110-001

1.ファイル検索

まずは、ファイル検索APIを実行するメソッドを作成しましょう。
トークンはマイページのAPIアクセス設定(要ログイン)から取得してください。

import os, requests
import pprint

TOKEN = ‘ここには自分のアカウントのトークンを貼り付ける'
DOMAIN = 'tellusxdp'

# ファイル検索メソッド
def search_file(params={}, next_url=''):
    if len(next_url) > 0:
        url = next_url
    else:
        url = 'https://file.{}.com/api/v1/origin/search/avnir2-ori'.format(DOMAIN)
    
    headers = {
        'Authorization': 'Bearer ' + TOKEN
    }
    r = requests.get(url, params=params, headers=headers)
    if not r.status_code == requests.codes.ok:
        r.raise_for_status()
    return r.json()

このメソッドは辞書型のparamsを受け取り、該当するファイルのヘッダテキストファイルをjson型で返却します。

ファイル検索を実行してみましょう。
今回は検索条件として、以下を設定しています。
– 雲量10%以下
– ポインティング角が20deg以下
– 一度に取得する件数は3件

# サンプル1
# 雲量10%以下 かつ ポインティング角が20deg以下、一度に取得する件数は3件
metas = search_file({'cloud_cover': 10, 'min_pointing_angle': 20, 'page_size': 3})
# 検索結果を表示
pprint.pprint(metas)

実行すると、次のような結果が表示されます。

{'count': 3,
 'items': [{'bbox': [138.9756564, 36.174298, 140.1360529, 37.0726605],
            'center_datetime': '2006-11-10T01:48:05.325279+00:00',
            'cloud_cover': 0,
            'coordinates': [[138.9756564, 36.3759473],
                            [139.8980412, 36.174298],
                            [140.1360529, 36.8693836],
                            [139.2058268, 37.0726605]],
            'dataset_id': 'ALAV2A042302850-OORIRFU-D075P0-20061110-001',
            'date_added': '2020-01-10T15:42:32.298772+00:00',
            'files': ['IMG-02-ALAV2A042302850-OORIRFU-D075P0-20061110-001.tif',
                      'IMG-04-ALAV2A042302850-OORIRFU-D075P0-20061110-001.tif',
                      'IMG-03-ALAV2A042302850-OORIRFU-D075P0-20061110-001.tif',
                      'HDR-ALAV2A042302850-OORIRFU-D075P0-20061110-001.txt',
                      'IMG-01-ALAV2A042302850-OORIRFU-D075P0-20061110-001.tif'],
            'frame_number': 2850,
            'gain': [0.588, 0.573, 0.502, 0.557],
            'observation_datetime': '2006-11-10T01:48:05.325279+00:00',
            'offset': [0.0, 0.0, 0.0, 0.0],
            'orbit_direction': 'D',
            'pointing_angle': 21.5,
            'publish_link': 'https://file.tellusxdp.com/api/v1/origin/publish/avnir2-ori/ALAV2A042302850-OORIRFU-D075P0-20061110-001',
            'rsp_frame_number': 2850,
            'rsp_path_number': 75,
            'scene_id': 'ALAV2A042302850',
            'scene_shift': 0,
            'sun_azimuth_angle': 168.9431399,
            'sun_elevation_angle': 35.5860298,
            'total_orbit_number': 4230},
           {'bbox': [138.3449177, 34.2143897, 139.4736153, 35.10965],
             <2件目。省略>
           },
           {'bbox': [138.1906085, 33.7231867, 139.3123251, 34.6170302],
             <3件目。省略>
           }],
 'next': 'https://file.tellusxdp.com/api/v1/origin/search/avnir2-ori?cursor=eyJjdXJzb3JfbGFzdF90aW1lIjogIjIwMDYtMTEtMTBUMDE6NDg6NDYuMzg2Mzg2IiwgImN1cnNvcl9iZWdpbl90aW1lIjogIjIwMDYtMTEtMTBUMDE6NDg6MDUuMzI1Mjc5IiwgInN0YXJ0X3RpbWUiOiAiMTk3MS0wMS0wMVQwMDowMDowMCIsICJlbmRfdGltZSI6ICIyMDIwLTAxLTE4VDEzOjQyOjIxLjQ1MTY5MSIsICJtaW5fcG9pbnRpbmdfYW5nbGUiOiAyMC4wLCAiY2xvdWRfY292ZXIiOiAxMC4wfQ=='}

返却値の一番最後、nextというキー名でURLが設定されています。
これは、設定した条件に該当するデータ数が多く一度に全件を取得しきれなかった場合や、取得件数の上限を設定した場合に、今回の実行結果の続きからデータを取得するためのURLです。
次はこのURLを引数に設定して、続きの1件を取得してみましょう。

# サンプル2
# 同じ条件で続きを取得する、取得件数を1件にする
metas = search_file({'page_size': 1}, next_url = metas['next'])
pprint.pprint(metas)

こちらの実行結果は以下のようになります。

{'count': 1,
 'items': [{'bbox': [138.749406, 34.426666, 140.3650394, 35.4539698],
            'center_datetime': '2011-04-01T01:55:29.999478+00:00',
            'cloud_cover': 0,
            'coordinates': [[138.749406, 34.7618061],
                            [140.1085505, 34.426666],
                            [140.3650394, 35.1162994],
                            [138.9952686, 35.4539698]],
            'dataset_id': 'ALAV2A276132870-OORIRFU-D080P0-20110401-000',
            'date_added': '2020-01-10T15:42:32.298772+00:00',
            'files': ['IMG-02-ALAV2A276132870-OORIRFU-D080P0-20110401-000.tif',
                      'HDR-ALAV2A276132870-OORIRFU-D080P0-20110401-000.txt',
                      'IMG-01-ALAV2A276132870-OORIRFU-D080P0-20110401-000.tif',
                      'IMG-03-ALAV2A276132870-OORIRFU-D080P0-20110401-000.tif',
                      'IMG-04-ALAV2A276132870-OORIRFU-D080P0-20110401-000.tif'],
            'frame_number': 2870,
            'gain': [0.588, 0.573, 0.502, 0.557],
            'observation_datetime': '2011-04-01T01:55:29.999478+00:00',
            'offset': [0.0, 0.0, 0.0, 0.0],
            'orbit_direction': 'D',
            'pointing_angle': 38.0,
            'publish_link': 'https://file.tellusxdp.com/api/v1/origin/publish/avnir2-ori/ALAV2A276132870-OORIRFU-D080P0-20110401-000',
            'rsp_frame_number': 2870,
            'rsp_path_number': 80,
            'scene_id': 'ALAV2A276132870',
            'scene_shift': 0,
            'sun_azimuth_angle': 156.295176,
            'sun_elevation_angle': 57.2636783,
            'total_orbit_number': 27613}],
 'next': 'https://file.tellusxdp.com/api/v1/origin/search/avnir2-ori?cursor=eyJjdXJzb3JfbGFzdF90aW1lIjogIjIwMTEtMDQtMDFUMDE6NTU6MjkuOTk5NDc4IiwgImN1cnNvcl9iZWdpbl90aW1lIjogIjIwMTEtMDQtMDFUMDE6NTU6MjkuOTk5NDc4IiwgInN0YXJ0X3RpbWUiOiAiMTk3MS0wMS0wMVQwMDowMDowMCIsICJlbmRfdGltZSI6ICIyMDIwLTAxLTE2VDEwOjUyOjU4LjEwNzczOCIsICJtaW5fcG9pbnRpbmdfYW5nbGUiOiAyMC4wLCAiY2xvdWRfY292ZXIiOiAzMC4wfQ=='}

同じ条件で、次の1件のデータが取得できました。

さらに条件を絞って検索してみましょう。
今度は、指定した時刻以降に撮影されたファイルを取得します。

# サンプル3
# 指定時刻以降
metas = search_file({'after': '2010-10-05T15:00:00.000000+00:00', 'page_size': 3})

pprint.pprint(metas)

こちらの実行結果は以下のとおりです。

{'count': 3,
 'items': [<省略>],
 'next': 'https://file.tellusxdp.com/api/v1/origin/search/avnir2-ori?cursor=eyJjdXJzb3JfbGFzdF90aW1lIjogIjIwMTAtMTAtMDZUMDE6NDg6MTcuOTUzODEwIiwgImN1cnNvcl9iZWdpbl90aW1lIjogIjIwMTAtMTAtMDZUMDE6NDg6MDEuNTIwMjU5IiwgInN0YXJ0X3RpbWUiOiAiMjAxMC0xMC0wNVQxNTowMDowMCIsICJlbmRfdGltZSI6ICIyMDIwLTAxLTIxVDE1OjI2OjUyLjA5ODY4OSJ9'}

「2010年10月5日以降に撮影」という条件で検索した結果を、page_sizeで指定した件数の3件表示しています。

2.ダウンロードURL発行

検索したファイルは、ダウンロードすることで実際のデータを確認できます。
次は、ファイルをダウンロードするためのURLを発行しましょう。

ファイルをダウンロードするためのURLを発行するメソッドを作成します。

# ダウンロードURL発行メソッド
def publish_file(dataset_id='', searched_url=''):
    if len(searched_url) > 0:
        url = searched_url
    else:
        url = 'https://file.{}.com/api/v1/origin/publish/avnir2-ori/{}'.format(DOMAIN, dataset_id)
    
    headers = {
        'Authorization': 'Bearer ' + TOKEN
    }

    r = requests.get(url, headers=headers)
    if not r.status_code == requests.codes.ok:
        r.raise_for_status()
    return r.json()

ダウンロードURL発行メソッドを実行してみましょう。
このメソッドは、2種類の方法で実行できます。
まずは、先ほどのファイル検索メソッドを用いて、検索結果の先頭にあるデータからURLを発行する方法です。

# サンプル4
# 取得用URL発行(検索結果から取得したい場合)
# ファイル検索
metas = search_file({'page_size': 10})
# ダウンロードURL発行
published = publish_file(searched_url=metas['items'][0]['publish_link'])
# 結果を表示する
pprint.pprint(published)

こちらの実行結果は以下です。

{'dataset_id': 'ALAV2A013272710-OORIRFU-D066P3-20060425-000',
 'expires_at': '2020-01-18T20:29:06.307812+00:00',
 'files': [{'file_name': 'IMG-02-ALAV2A013272710-OORIRFU-D066P3-20060425-000.tif',
            'file_size': 58112471,
            'url': 'https://file.tellusxdp.com/api/v1/origin/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzkzMzYxNDYsImV4cCI6MTU3OTM3OTM0NiwiYXBpX3Rva2VuIjoiVEgwRS04UWRlTFZnIiwiZGF0YXNldCI6ImF2bmlyMi1vcmkiLCJrZXkiOiJBTEFWMkEwMTMyNzI3MTAtT09SSVJGVS1EMDY2UDMtMjAwNjA0MjUtMDAwIiwiZmlsZV9uYW1lIjoiSU1HLTAyLUFMQVYyQTAxMzI3MjcxMC1PT1JJUkZVLUQwNjZQMy0yMDA2MDQyNS0wMDAudGlmIn0.uI3qG28YKg6EbAnYVQiFVcMHQWeeWSTInIfKvXIsxbI'},
           <省略>
           ],
 'project': 'avnir2-ori'}

filesの配列の中に、各ファイルの名前(file_name)、ファイルサイズ(file_size)、URL(url)があります。

このurlの値が、ファイルをダウンロードするためのURLです。

URLには有効期限があり、expires_atに設定されています。これを過ぎるとURLが使えなくなるので、再発行をしてください。

 

もう一つ、dataset_idからダウンロードURLを発行する方法があります。

dataset_idは、ファイル検索APIの実行結果から取得することができます。

# サンプル5
# ダウンロードURL発行(dataset_idがわかっている場合)
published = publish_file('ALAV2A013272800-OORIRFU-D066P3-20060425-000')
pprint.pprint(published)

こちらでも同様に、files.urlの値からダウンロードURLを取得できます。

{'dataset_id': 'ALAV2A013272800-OORIRFU-D066P3-20060425-000',
 'expires_at': '2020-01-18T20:29:17.943066+00:00',
 'files': [{'file_name': 'IMG-01-ALAV2A013272800-OORIRFU-D066P3-20060425-000.tif',
            'file_size': 58024471,
            'url': 'https://file.tellusxdp.com/api/v1/origin/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzkzMzYxNTcsImV4cCI6MTU3OTM3OTM1NywiYXBpX3Rva2VuIjoiVEgwRS04UWRlTFZnIiwiZGF0YXNldCI6ImF2bmlyMi1vcmkiLCJrZXkiOiJBTEFWMkEwMTMyNzI4MDAtT09SSVJGVS1EMDY2UDMtMjAwNjA0MjUtMDAwIiwiZmlsZV9uYW1lIjoiSU1HLTAxLUFMQVYyQTAxMzI3MjgwMC1PT1JJUkZVLUQwNjZQMy0yMDA2MDQyNS0wMDAudGlmIn0.BfqWgxI5Ns7JhveOCsK2wJdZ17RAjsmAibQHhIxI_XY'},
           <省略>
           ],
 'project': 'avnir2-ori'}

3.ダウンロード

最後に、発行したURLからファイルをダウンロードします。

# サンプル6
# 画像データをダウンロードし保存
# ※サンプル5の実行後でないとエラーになります。
for file in published['files']:
    if re.match(r'.*tif$', file['file_name']):
        pprint.pprint(file['file_name'])
        download_file(file['url'], file['file_name'], published['dataset_id'])

ファイルサイズが大きいので、実行が終わるまでに少し時間がかかります。

シーン(データ)の取得条件をより詳しく知りたい場合は、ヘッダーテキストをダウンロードしてみてください。

# サンプル7
# ヘッダーテキストをダウンロードし保存
# ※サンプル5の実行後でないとエラーになります。
for file in published['files']:
    if re.match(r'.*txt$', file['file_name']):
        pprint.pprint(file['file_name'])
        download_file(file['url'], file['file_name'], published['dataset_id'])

ヘッダーテキストの中身については、JAXAが提供するプロダクトフォーマット説明書を参照してください。

最後に、ダウンロードしたGeoTiffファイルの読み込みと表示をしてみましょう。

# 読み込み
from skimage import io
from osgeo import gdal, gdalconst, gdal_array

# 青の波長
tif = gdal.Open('./ALAV2A013272800-OORIRFU-D066P3-20060425-000/IMG-01-ALAV2A013272800-OORIRFU-D066P3-20060425-000.tif', gdalconst.GA_ReadOnly)

# GeoTiffのタグコードを表示
pprint.pprint(tif.GetProjection())
pprint.pprint(tif.GetGeoTransform())
pprint.pprint((tif.RasterXSize, tif.RasterYSize)) 
pprint.pprint(tif.GetMetadata())
pprint.pprint(tif.RasterCount)

io.imshow(tif.GetRasterBand(1).ReadAsArray())

AVNIR-2のデータは、波長(バンド)ごとにファイルが分かれています。
AVNIR-2ではバンド1に青、2に緑、3に赤、4に近赤外が設定されています。
(データ詳細:https://www.tellusxdp.com/ja/dev/data/avnir-2
上記のサンプルコードは青の波長のファイルのみを扱ったものです。

青の波長のファイルを画像表示した結果が以下になります。単バンドのため画像はモノクロで表示されます。

Credit : JAXA

最後に、各波長ごとに分かれているファイルを合成して、私たちが普段見ている画像と同じようにカラー表示してみましょう。
赤、青、緑にあたる01から03の番号の3ファイルを読み込んで合成します。

# 可視光
import numpy as np

tif_b = gdal.Open('./ALAV2A013272800-OORIRFU-D066P3-20060425-000/IMG-01-ALAV2A013272800-OORIRFU-D066P3-20060425-000.tif', gdalconst.GA_ReadOnly)
tif_g = gdal.Open('./ALAV2A013272800-OORIRFU-D066P3-20060425-000/IMG-02-ALAV2A013272800-OORIRFU-D066P3-20060425-000.tif', gdalconst.GA_ReadOnly)
tif_r = gdal.Open('./ALAV2A013272800-OORIRFU-D066P3-20060425-000/IMG-03-ALAV2A013272800-OORIRFU-D066P3-20060425-000.tif', gdalconst.GA_ReadOnly)

x_size = tif_b.RasterXSize
y_size = tif_b.RasterYSize
dtype = tif_b.GetRasterBand(1).ReadAsArray().dtype

rgb_array = np.zeros((y_size, x_size, 3), dtype=dtype)

rgb_array[:,:,0] = tif_r.GetRasterBand(1).ReadAsArray()
rgb_array[:,:,1] = tif_g.GetRasterBand(1).ReadAsArray()
rgb_array[:,:,2] = tif_b.GetRasterBand(1).ReadAsArray()
io.imshow(rgb_array)
Credit : JAXA

以上、Tellusの開発環境で、AVNIR-2(オルソ補正画像プロダクト)の提供元オリジナルデータを取得する方法を紹介しました。

今後も様々な衛星の提供元オリジナルデータを公開していく予定です。楽しみにお待ちください。