予測気温表示アプリ──JSON処理とAPIの呼び出し
予測気温をWebAPIから取得する
Open-Meteoで公開されているWebAPIを利用すると、さまざまな気象情報が入手可能です。しかもこのAPIは特別な認証などは必要ないため、とても手軽に活用できます。
今回は、次のURLで参照できる予測気温を利用することにします。任意の緯度経度の予測気温をJSON文字列で返すAPIです。
https://api.open-meteo.com/v1/forecast?latitude={緯度}&longitude={経度}&hourly=temperature_2m
JSON文字列は、次のようなフォーマットになっています。
{ latitude: 34.6, longitude: 135.5, ... hourly: { time: [ "2022-12-30T00:00", ... "2023-01-05T23:00" ], temperature_2m: [ 5.8, ... -1.1 ] } }
今回は、時系列のデータのみを参照するので、hourly以下のデータを利用します。
.NET標準のJSON処理
.NETでJSON文字列を処理するにはいくつかの方法がありますが、今回は標準のJsonSerializerクラスを利用することにします。JsonSerializer.Deserializeメソッドを使えば、JSON文字列を解析してC#のオブジェクトに変換できます。
気温の時系列データは、次のようなクラス定義としました。
// 日時と気温のリスト public class Hourly { public List<DateTime>? time { get; set; } // 日時 public List<float>? temperature_2m { get; set; } // 気温 } // 時刻毎の気温データ public class Temp { public Hourly? hourly { get; set; } }
Tempクラスは、JSON文字列の時系列データをそのままモデルにしたものです。時系列以外のデータは使用しないので、Tempクラス以外の定義は不要です。このTempクラスを使って、Deserializeメソッドで解析するには、次のように指定します。
var jsonData = JsonSerializer.Deserialize<Temp>(予測気温のjson文字列);
JSON文字列とTempクラスのプロパティが合致するものだけ、オブジェクトとして変換されます。つまり変数jsonDataには、JSON文字列を変換したTempオブジェクトがセットされることになります。
.NET標準のHTTPクライアントを利用する
次に、JSON文字列を取得するWebAPIの呼び出し処理を追加しましょう。これは、.NETのHttpClientクラスを使えば簡単です。HttpClientクラスはstaticとしてインスタンスを生成し、各処理で共有するようにします。
WebAPIの取得処理は、次のようにOpenMeteoクラスのGetTempAsyncメソッドとして定義しました。
public class OpenMeteo { static readonly HttpClient client = new(); // 予測気温を取得する public static async Task<string> GetTempAsync(CapitalPoint cp) { var tmp = new StringBuilder(); try { // Open Meteo APIから、指定の緯度経度地点の予測気温を取得する var result = await client.GetAsync($"https://api.open-meteo.com/v1/forecast?latitude={cp.Lat}&longitude={cp.Lon}&hourly=temperature_2m"); var json = await result.Content.ReadAsStringAsync(); // 結果のJSON文字列を解析する var jsonData = JsonSerializer.Deserialize<Temp>(json); ~中略~ } return tmp.ToString(); } }
HttpClientクラスのGetAsyncメソッドで、HTTP通信でGET処理を行います。ここでは、指定の緯度経度を、メソッドのパラメータ、CapitalPointを参照しています。HTTPのレスポンスは、非同期のReadAsStringAsyncメソッドで受けとります。
LINQを用いて現在時刻以降を取得する
APIの予測データはデフォルトでは7日先までのデータとなり、少し長すぎるので、今回は現在時刻を含む1日(25時間)分を切り出す形にしてみました(APIのパラメータで調整も可能)。
~中略~ public static async Task<string> GetTempAsync(CapitalPoint cp) { ~中略~ // 結果のJSON文字列を解析する var jsonData = JsonSerializer.Deserialize<Temp>(json); // 現在のUTC日時を求める(1) DateTime now = DateTime.UtcNow; now = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0); // 現在時以降の25時間分の気温を抽出し、文字列にする(2) var temps = jsonData?.hourly?.time?.Select((value, index) => new { value, index }).Where((tmp => now.CompareTo(tmp.value) <= 0)).Take(25); if (temps != null) { foreach (var item in temps) { var local = TimeZoneInfo.ConvertTimeFromUtc(item.value, TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time")); // JSTに変換(3) tmp.Append(local.ToString("yyyy年MM月dd日(ddd) HH時")); tmp.Append(" "); tmp.Append(string.Format("{0:f1}", jsonData?.hourly?.temperature_2m?[item.index])); // 日時のインデックスを用いて気温を参照する(4) tmp.Append(" ℃\n"); } } ~中略~ }
取得したデータ内容で、注意点は2つあります。1つ目は、時刻のタイムゾーンがGMT(UTC)になっていることです。2つ目は、日時と気温のデータがペアではなく、それぞれのリストになっていることです。
25時間分を抽出する方法はいくつかありますが、今回はLINQを用いて現在時刻から25時間分のデータを取得しました(2)。
時刻に関しては、現在のUTC時刻を元にデータを特定し(1)、表示時にJST(日本標準時)に変換するようにしました(3)。また、日時のリストのインデックス(何番目か)から気温のデータを参照するようにしています(4)。
クリック後の画面表示を追加する
作成したGetTempAsyncメソッドは、結果を文字列として返しています。この文字列をビューモデルのResultプロパティに設定するようにします。なお、GetTempAsyncメソッドは非同期メソッドのため、呼び出し時にawaitをつけ、OnClickメソッドにもasyncをつけています。
private async void OnClick() { if (Capitalpoint.Name != "") { Result = "お待ちください"; Result = await OpenMeteo.GetTempAsync(Capitalpoint); } else { Result = "県庁所在地を選択してください "; } }
最後に
最後に、Avalonia UIを紹介しました。.NET環境でのマルチプラットフォーム向けフレームワークでは、連載では紹介できなかった、Uno Platformもあります。また、.NET環境以外でも、Flutterなど魅力的なフレームワークがあります。
このようなマルチプラットフォーム向けフレームワークは、もちろんネイティブアプリのようにかゆい所に手が届くというわけではありません。ただし、マルチプラットフォーム向け開発のハードルはずいぶんと低くなったのは間違いありません。