Tellus から降水量を取得する
Mix.install([
{:nx, "~> 0.9"},
{:evision, "~> 0.2"},
{:req, "~> 0.5"},
{:kino, "~> 0.14"},
{:kino_maplibre, "~> 0.1"}
])
情報の設定
# Tellus のトークンを入力する
token_input = Kino.Input.password("Token")
# 降水量データの商品ID
rain_product_id = "51f86de1-777f-43e6-821f-a2c1c737cb8b"
base_url = "https://sbs.tellus-tools.com"
このノートブックではTellusの降水観測情報API(試用版)を使用しています
©島津ビジネスシステムズ
Tellus の認証
auth_url = "https://www.tellusxdp.com/api/manager/v2/auth/token/"
json_header = {"Content-Type", "application/json"}
get_auth_header = fn product_id ->
request_body = %{product_id: product_id}
auth_header = {"Authorization", "Bearer " <> Kino.Input.read(token_input)}
auth_url
|> Req.post!(json: request_body, headers: [auth_header, json_header])
|> then(&{"Authorization", "Bearer " <> &1.body["token"]})
end
降水量の取得
get_precipitation = fn lon, lat, ddate ->
auth_header = get_auth_header.(rain_product_id)
"#{base_url}/tellus/wif82af3w39s/rap_af.php?lon=#{lon}&lat=#{lat}&ddate=#{ddate}"
|> Req.get!(headers: [auth_header])
|> then(&Jason.decode!(&1.body))
end
# 大分市のデータ
precipitation = get_precipitation.(131.62, 33.23, 20_190_909)
Enum.count(precipitation["prec"])
# 東京23区の経度範囲
lon_list =
13956..13991
|> Enum.map(&(&1 / 100))
# 東京23区の緯度範囲
lat_list =
3552..3582
|> Enum.map(&(&1 / 100))
Enum.count(lon_list) * Enum.count(lat_list)
precipitation =
Enum.map(lon_list, fn lon ->
Enum.map(lat_list, fn lat ->
get_precipitation.(lon, lat, 20_190_909)
|> Map.get("prec")
end)
end)
降水量の可視化
prec_tensor = Nx.tensor(precipitation)
Nx.reduce_min(prec_tensor)
max_prec =
prec_tensor
|> Nx.reduce_max()
|> Nx.to_number()
alpha =
prec_tensor
|> Nx.multiply(255 / max_prec)
|> Nx.slice_along_axis(0, 1, axis: 2)
{w, h, t} = Nx.shape(prec_tensor)
bgr =
[255, 0, 0]
|> Nx.tensor()
|> Nx.tile([w, h, 1])
[bgr, alpha]
|> Nx.concatenate(axis: 2)
|> Evision.Mat.from_nx_2d()
|> Evision.resize({w * 10, h * 10})
|> Evision.convertScaleAbs(alpha: 3)
|> dbg()
get_mat = fn hour ->
alpha = Nx.slice_along_axis(prec_tensor, hour, 1, axis: 2)
[bgr, alpha]
|> Nx.concatenate(axis: 2)
|> Evision.Mat.from_nx_2d()
|> Evision.resize({56 * 10, 20 * 10})
|> Evision.convertScaleAbs(alpha: 3)
end
0..23
|> Enum.map(fn hour ->
get_mat.(hour)
end)
|> Kino.Layout.grid(columns: 4)
地図へのオーバーレイ
get_data_url = fn mat ->
Evision.imencode(".png", mat)
|> Base.encode64()
|> then(&"data:image/png;base64,#{&1}")
end
show_map_overlay = fn img_base64 ->
{left_lon, right_lon, bottom_lat, top_lat} = {139.56, 139.91, 35.52, 35.82}
center = {(left_lon + right_lon) / 2, (bottom_lat + top_lat) / 2}
# タイルの中心を地図の中心にする
MapLibre.new(center: center, zoom: 9, style: :street)
# 画像をタイルの座標に配置する
|> MapLibre.add_source(
"sar-source",
type: :image,
url: img_base64,
coordinates: [
[left_lon, top_lat],
[right_lon, top_lat],
[right_lon, bottom_lat],
[left_lon, bottom_lat]
]
)
# 画像をレイヤーとして地図に重ね、透過する
|> MapLibre.add_layer(
id: "overlay",
source: "sar-source",
type: :raster,
layout: %{
"visibility" => "visible"
}
)
end
5
|> get_mat.()
|> get_data_url.()
|> show_map_overlay.()
0..11
|> Enum.map(fn hour ->
{
hour,
get_mat.(hour)
|> get_data_url.()
|> show_map_overlay.()
}
end)
|> Kino.Layout.tabs()