Blazor WebAssembly+OpenMapWeatherApiで取得したデータをExcelファイルとしてダウンロードする
前回 Blazor WebAssemblyで作成した天気予想アプリにグラフを表示する機能を追加します。
サンプルページはこちらです。
JavaScriptを使うのですが、dataとoptionsをJSON形式で渡します。最初にwwwroot/index.htmlを示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>Blazor WebAssemblyとOpenMapWeatherApiで天気予報を表示する</title> <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" /> </head> <body> <app>Loading...</app> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">??</a> </div> <!-- グラフ関連 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script> <script> function ShowChart(elm, dataSet, option) { var chart = new Chart(elm, { type: 'line', data: JSON.parse(dataSet), options: JSON.parse(option) }); } </script> <!-- グラフ関連 ここまで --> <script src="_framework/blazor.webassembly.js"></script> </body> </html> |
ShowChart関数にdataSet, optionという引数を渡していますが、これを生成するためのクラスを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
[Serializable] public class ChartJSDataModel { [System.Text.Json.Serialization.JsonPropertyName("labels")] public string[] Labels { get; set; } [System.Text.Json.Serialization.JsonPropertyName("datasets")] public DataSetsModel[] DataSets { get; set; } } [Serializable] public class DataSetsModel { [System.Text.Json.Serialization.JsonPropertyName("label")] public string Label { get; set; } [System.Text.Json.Serialization.JsonPropertyName("backgroundColor")] public string BackgroundColor { get; set; } [System.Text.Json.Serialization.JsonPropertyName("borderColor")] public string BorderColor { get; set; } [System.Text.Json.Serialization.JsonPropertyName("data")] public int[] Data { get; set; } } [Serializable] public class Options { [System.Text.Json.Serialization.JsonPropertyName("title")] public Title Title { get; set; } [System.Text.Json.Serialization.JsonPropertyName("scales")] public Scales Scales { get; set; } [System.Text.Json.Serialization.JsonPropertyName("responsive")] public bool Responsive { get; set; } [System.Text.Json.Serialization.JsonPropertyName("legend")] public Legend Legend { get; set; } [System.Text.Json.Serialization.JsonPropertyName("pan")] public Pan Pan { get; set; } [System.Text.Json.Serialization.JsonPropertyName("zoom")] public Zoom Zoom { get; set; } } [Serializable] public class Pan { [System.Text.Json.Serialization.JsonPropertyName("enabled")] public bool Enabled { get; set; } } [Serializable] public class Zoom { [System.Text.Json.Serialization.JsonPropertyName("enabled")] public bool Enabled { get; set; } } [Serializable] public class Legend { [System.Text.Json.Serialization.JsonPropertyName("display")] public bool Display { get; set; } } [Serializable] public class Title { [System.Text.Json.Serialization.JsonPropertyName("display")] public bool Display { get; set; } [System.Text.Json.Serialization.JsonPropertyName("text")] public string Text { get; set; } [System.Text.Json.Serialization.JsonPropertyName("fontSize")] public int FontSize { get; set; } } [Serializable] public class Scales { [System.Text.Json.Serialization.JsonPropertyName("yAxes")] public yAxes[] yAxes { get; set; } } [Serializable] public class yAxes { [System.Text.Json.Serialization.JsonPropertyName("ticks")] public Ticks Ticks { get; set; } } [Serializable] public class Ticks { [System.Text.Json.Serialization.JsonPropertyName("suggestedMax")] public int SuggestedMax { get; set; } [System.Text.Json.Serialization.JsonPropertyName("suggestedMin")] public int SuggestedMin { get; set; } [System.Text.Json.Serialization.JsonPropertyName("stepSize")] public int StepSize { get; set; } } |
Index.razorの変更部分を示します。気温、湿度、気圧のグラフを表示したいのでcanvasを3つ追加しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
@page "/" @inject HttpClient httpClient @using Newtonsoft.Json; <h1>OpenWeatherMap で 天気予報を取得</h1> <div class="form-group"> <input type="button" @onclick="Search" value="検索" /> <select name="area" @bind="@SelectedValue"> @foreach (var pair in KeyValuePairs) { <option value="@pair.Value">@pair.Key</option> } </select> <select name="count" @bind="@SelectedCount"> <option value="8">24時間</option> <option value="16">48時間</option> <option value="24">72時間</option> <option value="32">96時間</option> </select> </div> <hr /> @if (weatherResult != null && weatherResult.List.Length != 0) { <canvas @ref="canvasElementReference1" width="300" height="300" style=" display: inline-block" /> <canvas @ref="canvasElementReference2" width="300" height="300" style=" display: inline-block" /> <canvas @ref="canvasElementReference3" width="300" height="300" style=" display: inline-block" /> <table class="table"> // 前回と同じなので省略 </table> <div>@weatherResult.City.Name</div> } |
@code の部分で追加された部分を示します。それ以外は前回と同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
@code { @inject IJSRuntime JS; private ElementReference canvasElementReference1; private ElementReference canvasElementReference2; private ElementReference canvasElementReference3; List<string> dates = new List<string>(); List<double> temps = new List<double>(); List<long> humidities = new List<long>(); List<long> pressures = new List<long>(); public async void Search() { // SelectedValue, SelectedCount, apiKey に関する部分は省略 string url = String.Format("https://api.openweathermap.org/data/2.5/forecast?id={0}&units=metric&cnt={1}&APPID={2}", SelectedValue, SelectedCount, apiKey); string str = await httpClient.GetStringAsync(url); this.weatherResult = JsonConvert.DeserializeObject<WeatherResult>(str); // 今回の追加部分 ここから // 処理を連続しておこなう場合、前のデータはクリアする dates.Clear(); temps.Clear(); humidities.Clear(); pressures.Clear(); foreach (var weatherSummary in weatherResult.List) { dates.Add(weatherSummary.DtTxt.DateTime.AddHours(9).ToString("M/d H:mm")); temps.Add(weatherSummary.Main.Temp); humidities.Add(weatherSummary.Main.Humidity); pressures.Add(weatherSummary.Main.Pressure); } await ShowTempChart(); await ShowHumidityChart(); await ShowPressuresChart(); } } |
まずリストのなかに取得したデータを格納しています。これをもとにJavaScriptにわたす引数を生成します。Optionsはほとんど同じなので別メソッドにしました。
GetOptions(string titleText, int max, int min, int stepSize)メソッドではグラフの目盛りの最小値と最大値を設定します。ひとつの折れ線グラフにひとつの項目しか表示させないので凡例は表示させません(Legend legend = new Legend { Display = false };の部分)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@code { Options GetOptions(string titleText, int max, int min, int stepSize) { Options options = new Options(); options.Responsive = false; Legend legend = new Legend { Display = false }; options.Legend = legend; Title title = new Title(); title.Display = true; title.Text = titleText; title.FontSize = 18; options.Title = title; Scales scales = new Scales(); scales.yAxes = new yAxes[] { new yAxes { Ticks = new Ticks { StepSize = stepSize, SuggestedMax = max, SuggestedMin = min } } }; options.Scales = scales; return options; } } |
気温は目盛りの最大値と最小値はデータの最大値と最小値の±5℃にしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@code { async Task ShowTempChart() { var dataModel = new ChartJSDataModel(); dataModel.Labels = dates.ToArray(); int max = (int)temps.Max(); int min = (int)temps.Min(); dataModel.DataSets = new DataSetsModel[] { new DataSetsModel(){ BackgroundColor = "rgba(0, 0, 0, 0)", BorderColor = "rgb(255, 99, 132)", Data = temps.Select(x => (int)x).ToArray(), } }; Options options = GetOptions("気温", max +5, min-5, 5); await JS.InvokeVoidAsync( "ShowChart", canvasElementReference1, System.Text.Json.JsonSerializer.Serialize(dataModel), System.Text.Json.JsonSerializer.Serialize(options)); } } |
湿度も気温と同じですが、100%を超えたり0%を下回ることはないのでそのように調整しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
@code { async Task ShowHumidityChart() { var dataModel = new ChartJSDataModel(); dataModel.Labels = dates.ToArray(); int max = (int)humidities.Max(); int min = (int)humidities.Min(); dataModel.DataSets = new DataSetsModel[] { new DataSetsModel(){ BackgroundColor = "rgba(0, 0, 0, 0)", BorderColor = "rgb(0, 120, 255)", Data = humidities.Select(x => (int)x).ToArray(), } }; if (max + 5 > 100) max = 100; else max += 5; if (min - 5 < 0) min = 0; else min -= 5; Options options = GetOptions("湿度", max, min, 5); await JS.InvokeVoidAsync( "ShowChart", canvasElementReference2, System.Text.Json.JsonSerializer.Serialize(dataModel), System.Text.Json.JsonSerializer.Serialize(options)); } } |
以下は気圧のグラフを表示するメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@code { async Task ShowPressuresChart() { var dataModel = new ChartJSDataModel(); dataModel.Labels = dates.ToArray(); int max = (int)pressures.Max(); int min = (int)pressures.Min(); dataModel.DataSets = new DataSetsModel[] { new DataSetsModel(){ BackgroundColor = "rgba(0, 0, 0, 0)", BorderColor = "rgb(127, 255, 0)", Data = pressures.Select(x => (int)x).ToArray(), } }; Options options = GetOptions("気圧", max, min, 10); await JS.InvokeVoidAsync( "ShowChart", canvasElementReference3, System.Text.Json.JsonSerializer.Serialize(dataModel), System.Text.Json.JsonSerializer.Serialize(options)); } } |
勉強でお世話になっています。
Blazor での [Inject] chart.js の使い方の勉強に
使わさせてもらってます。
ソースコードが、積み上げ式なので、過去のプログラムを
動作させながらでしたので、少し時間がかかりましたが
動かせました。(勉強になりました)
最後で、チャートの画面が出なくて悩みましたが
@if (weatherResult != null && weatherResult.List.Length != 0)
{
の部分を
@if (weatherResult != null && weatherResult.List.Length != 0)
{
と移動させたら、表示できました。
C# のコードを コードビハインド で作成したせいかもしれません。
canvas の初期化のタイミングが合わず、chart.js コール時には
canvas ができていなかったようです。
ありがとうございました。