├── src
├── KyoshinMonitorLib
│ ├── ApiResult
│ │ ├── ApiResultJsonContext.cs
│ │ ├── Result.cs
│ │ ├── Security.cs
│ │ ├── AppApi
│ │ │ ├── RealTimeData.cs
│ │ │ └── SiteList.cs
│ │ └── WebApi
│ │ │ └── Eew.cs
│ ├── KyoshinMonitorException.cs
│ ├── UrlGenerator
│ │ ├── WebApiUrlType.cs
│ │ ├── AppApiUrlType.cs
│ │ ├── LpgmWebApiUrlType.cs
│ │ ├── WebApiUrlGenerator.cs
│ │ ├── AppApiUrlGenerator.cs
│ │ ├── RealTimeDataType.cs
│ │ └── LpgmWebApiUrlGenerator.cs
│ ├── LinkedRealTimeData.cs
│ ├── ApiResult.cs
│ ├── LinkedObservationPoint.cs
│ ├── KyoshinMonitorLib.csproj
│ ├── ObservationPointType.cs
│ ├── Location.cs
│ ├── WebApi.cs
│ ├── Extensions.cs
│ ├── LpgmWebApi.cs
│ ├── Api.cs
│ ├── Mesh.cs
│ ├── AppApi.cs
│ ├── NtpAssistance.cs
│ ├── Point2.cs
│ ├── JmaIntensity.cs
│ └── ObservationPoint.cs
├── Tests
│ ├── Tests.csproj
│ └── Program.cs
├── KyoshinMonitorLib.Timers
│ ├── KyoshinMonitorLib.Timers.csproj
│ └── SecondBasedTimer.cs
├── KyoshinMonitorLib.SkiaImages
│ ├── KyoshinMonitorLib.SkiaImages.csproj
│ ├── ImageAnalysisResult.cs
│ ├── ColorConverter.cs
│ └── Extensions.cs
└── KyoshinMonitorLib.Images
│ ├── KyoshinMonitorLib.Images.csproj
│ ├── ImageAnalysisResult.cs
│ ├── Extensions.cs
│ └── ColorConverter.cs
├── LICENSE
├── .github
└── workflows
│ ├── publish-nuget.yml
│ └── codeql-analysis.yml
├── .gitattributes
├── KyoshinMonitorLib.sln
├── .gitignore
└── README.md
/src/KyoshinMonitorLib/ApiResult/ApiResultJsonContext.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KyoshinMonitorLib.ApiResult
4 | {
5 | [JsonSerializable(typeof(AppApi.RealtimeData))]
6 | [JsonSerializable(typeof(AppApi.SiteList))]
7 | [JsonSerializable(typeof(WebApi.Eew))]
8 | internal partial class ApiResultJsonContext : JsonSerializerContext
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/ApiResult/Result.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KyoshinMonitorLib.ApiResult
4 | {
5 | ///
6 | /// リザルト
7 | ///
8 | public class Result
9 | {
10 | ///
11 | /// ステータス
12 | ///
13 | [JsonPropertyName("status")]
14 | public string? Status { get; set; }
15 | ///
16 | /// メッセージ
17 | ///
18 | [JsonPropertyName("message")]
19 | public string? Message { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/ApiResult/Security.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KyoshinMonitorLib.ApiResult
4 | {
5 | ///
6 | /// セキュリティ情報
7 | ///
8 | public class Security
9 | {
10 | ///
11 | /// realm
12 | ///
13 | [JsonPropertyName("realm")]
14 | public string? Realm { get; set; }
15 | ///
16 | /// ハッシュ
17 | ///
18 | [JsonPropertyName("hash")]
19 | public string? Hash { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/KyoshinMonitorException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KyoshinMonitorLib
4 | {
5 | ///
6 | /// 強震モニタの処理に失敗したとき
7 | ///
8 | public class KyoshinMonitorException : Exception
9 | {
10 | ///
11 | /// 例外の初期化を行います。
12 | ///
13 | public KyoshinMonitorException(string message) : base(message)
14 | { }
15 | ///
16 | /// 例外の初期化を行います。
17 | ///
18 | public KyoshinMonitorException(string message, Exception innerException) : base(message, innerException)
19 | { }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/UrlGenerator/WebApiUrlType.cs:
--------------------------------------------------------------------------------
1 | namespace KyoshinMonitorLib.UrlGenerator
2 | {
3 | ///
4 | /// 生成するURLの種類(WebApi)
5 | ///
6 | public enum WebApiUrlType
7 | {
8 | ///
9 | /// リアルタイム情報
10 | /// 震度、加速度など
11 | ///
12 | RealtimeImg = 0,
13 |
14 | ///
15 | /// 到達予想震度
16 | ///
17 | EstShindo,
18 |
19 | ///
20 | /// P波、S波到達予想円
21 | ///
22 | PSWave,
23 |
24 | ///
25 | /// 緊急地震速報のJson
26 | ///
27 | EewJson,
28 | }
29 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/LinkedRealTimeData.cs:
--------------------------------------------------------------------------------
1 | namespace KyoshinMonitorLib
2 | {
3 | ///
4 | /// 結合済みの観測情報
5 | ///
6 | public struct LinkedRealtimeData
7 | {
8 | ///
9 | /// 結合済みの観測情報の初期化
10 | ///
11 | public LinkedRealtimeData(LinkedObservationPoint point, float? value)
12 | {
13 | ObservationPoint = point;
14 | Value = value;
15 | }
16 |
17 | ///
18 | /// 結合済みの観測点情報
19 | ///
20 | public LinkedObservationPoint ObservationPoint { get; }
21 | ///
22 | /// 観測値
23 | ///
24 | public float? Value { get; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/UrlGenerator/AppApiUrlType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KyoshinMonitorLib.UrlGenerator
4 | {
5 | ///
6 | /// 生成するURLの種類(スマホアプリApi)
7 | ///
8 | public enum AppApiUrlType
9 | {
10 | ///
11 | /// リアルタイム情報
12 | /// 震度、加速度など
13 | ///
14 | RealtimeData,
15 |
16 | ///
17 | /// 緊急地震速報の到達予想震度
18 | ///
19 | [Obsolete("このAPIは現在利用できなくなっています。")]
20 | HypoInfoJson,
21 |
22 | ///
23 | /// 緊急地震速報のP波、S波到達予想円
24 | ///
25 | [Obsolete("このAPIは現在利用できなくなっています。")]
26 | PSWaveJson,
27 |
28 | ///
29 | /// 緊急地震速報の予想震度
30 | ///
31 | [Obsolete("このAPIは現在利用できなくなっています。")]
32 | EstShindoJson,
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Tests/Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | Debug;Release
7 | latest
8 | enable
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/UrlGenerator/LpgmWebApiUrlType.cs:
--------------------------------------------------------------------------------
1 | namespace KyoshinMonitorLib.UrlGenerator
2 | {
3 | ///
4 | /// 生成するURLの種類(WebApi)
5 | ///
6 | public enum LpgmWebApiUrlType
7 | {
8 | ///
9 | /// リアルタイム情報
10 | /// 震度、加速度など
11 | ///
12 | RealtimeImg = 0,
13 |
14 | ///
15 | /// リアルタイム情報(長周期地震動)
16 | /// 長周期地震動階級、階級データ(周期n秒台)
17 | ///
18 | LpgmRealtimeImg,
19 |
20 | ///
21 | /// 到達予想震度
22 | ///
23 | EstShindo,
24 |
25 | ///
26 | /// P波、S波到達予想円
27 | ///
28 | PSWave,
29 |
30 | ///
31 | /// 緊急地震速報のJson
32 | ///
33 | EewJson,
34 |
35 | ///
36 | /// 長周期地震動の予測階級
37 | ///
38 | LongPeriodImg,
39 | }
40 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/ApiResult.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 |
3 | namespace KyoshinMonitorLib
4 | {
5 | ///
6 | /// APIの実行結果を表します。
7 | ///
8 | /// 実行結果の型
9 | public class ApiResult
10 | {
11 | ///
12 | /// APIの実行結果を初期化します。
13 | ///
14 | /// HTTPステータスコード
15 | /// 実行結果の値
16 | public ApiResult(HttpStatusCode statusCode, TResult data)
17 | {
18 | StatusCode = statusCode;
19 | Data = data;
20 | }
21 |
22 | ///
23 | /// HTTPステータスコード
24 | /// nullの場合はタイムアウトを示します。
25 | ///
26 | public HttpStatusCode StatusCode { get; }
27 | ///
28 | /// API実行結果
29 | /// 実行に失敗した場合nullが代入されます。
30 | ///
31 | public TResult Data { get; }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/LinkedObservationPoint.cs:
--------------------------------------------------------------------------------
1 | using KyoshinMonitorLib.ApiResult.AppApi;
2 | using System;
3 |
4 | namespace KyoshinMonitorLib
5 | {
6 | ///
7 | /// 結合済みの観測点情報
8 | ///
9 | public struct LinkedObservationPoint
10 | {
11 | ///
12 | /// 結合済みの観測点情報の初期化
13 | ///
14 | public LinkedObservationPoint(Site site, ObservationPoint? point)
15 | {
16 | Site = site;
17 | Point = point;
18 | }
19 |
20 | ///
21 | /// 強震モニタAPI上の観測地点情報
22 | ///
23 | public Site Site { get; }
24 | ///
25 | /// 設定ファイルから読み込んだ観測地点情報
26 | ///
27 | public ObservationPoint? Point { get; }
28 | ///
29 | /// 自動的に精度の高い方の緯度経度座標を取得する
30 | ///
31 | public Location? Location => Point?.Location ?? (Site.Lat is float lat && Site.Lng is float lng ? new Location(lat, lng) : null);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 ingen084
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.Timers/KyoshinMonitorLib.Timers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net9.0;net5.0;netstandard2.1;netstandard2.0;net47
4 | 0.4.5.2
5 | Copyright © ingenWorkS 2021
6 | ingenWorkS
7 | ingen084
8 | true
9 | .NETから簡単に強震モニタを利用できるようにするライブラリ
10 | Debug;Release
11 | LICENSE
12 | https://github.com/ingen084/KyoshinMonitorLib
13 | git
14 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
15 |
16 | latest
17 | enable
18 |
19 |
20 |
21 |
22 | True
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/KyoshinMonitorLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net9.0;net6.0;netstandard2.1;netstandard2.0;net47
4 | 0.4.5.2
5 | Copyright © ingenWorkS 2021
6 | ingenWorkS
7 | ingen084
8 | true
9 | .NETから簡単に強震モニタを利用できるようにするライブラリ
10 | Debug;Release
11 | LICENSE
12 | https://github.com/ingen084/KyoshinMonitorLib
13 | git
14 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
15 |
16 | latest
17 | enable
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | True
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.SkiaImages/KyoshinMonitorLib.SkiaImages.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0;net6.0;netstandard2.1;netstandard2.0;net47
5 | 0.4.5.2
6 | Copyright © ingenWorkS 2021
7 | ingenWorkS
8 | ingen084
9 | true
10 | .NETから簡単に強震モニタを利用できるようにするライブラリ
11 | Debug;Release
12 | LICENSE
13 | https://github.com/ingen084/KyoshinMonitorLib
14 | git
15 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
16 |
17 | latest
18 | enable
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | True
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/ApiResult/AppApi/RealTimeData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KyoshinMonitorLib.ApiResult.AppApi
5 | {
6 | ///
7 | /// リアルタイムデータ
8 | ///
9 | public class RealtimeData
10 | {
11 | ///
12 | /// 時間
13 | ///
14 | [JsonPropertyName("dataTime")]
15 | public DateTime? DateTime { get; set; }
16 | ///
17 | /// 情報の種類?
18 | ///
19 | [JsonPropertyName("packetType")]
20 | public string? PacketType { get; set; }
21 | ///
22 | /// 強震の種類
23 | ///
24 | [JsonPropertyName("kyoshinType")]
25 | public string? KyoshinType { get; set; }
26 | ///
27 | /// ベースとなるデータ?
28 | ///
29 | [JsonPropertyName("baseData")]
30 | public string? BaseData { get; set; }
31 | ///
32 | /// 観測点一覧用のID
33 | ///
34 | [JsonPropertyName("baseSerialNo")]
35 | public string? BaseSerialNo { get; set; }
36 | ///
37 | /// 実際のデータ
38 | ///
39 | [JsonPropertyName("items")]
40 | public float?[]? Items { get; set; }
41 | ///
42 | /// リザルト
43 | ///
44 | [JsonPropertyName("result")]
45 | public Result? Result { get; set; }
46 | ///
47 | /// セキュリティ情報
48 | ///
49 | [JsonPropertyName("security")]
50 | public Security? Security { get; set; }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.Images/KyoshinMonitorLib.Images.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net9.0;net6.0;netstandard2.1;netstandard2.0;net47
4 | 0.4.5.2
5 | Copyright © ingenWorkS 2021
6 | ingenWorkS
7 | ingen084
8 | true
9 | .NETから簡単に強震モニタを利用できるようにするライブラリ
10 | Debug;Release
11 | LICENSE
12 | https://github.com/ingen084/KyoshinMonitorLib
13 | git
14 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
15 |
16 | latest
17 | enable
18 |
19 | true
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | True
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/ObservationPointType.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace KyoshinMonitorLib
3 | {
4 | ///
5 | /// 観測点のタイプ
6 | ///
7 | public enum ObservationPointType
8 | {
9 | ///
10 | /// 不明(なるべく使用しないように)
11 | ///
12 | Unknown,
13 |
14 | ///
15 | /// KiK-net
16 | ///
17 | KiK_net,
18 |
19 | ///
20 | /// K-NET
21 | ///
22 | K_NET,
23 | }
24 |
25 | ///
26 | /// ObservationPointTypeの拡張メソッドたち
27 | ///
28 | public static class ObservationPointTypeExtensions
29 | {
30 | ///
31 | /// 人が読みやすい文字に変換します。
32 | ///
33 | /// 変換させるObservationPointType
34 | /// 変換された文字列
35 | public static string ToNaturalString(this ObservationPointType type)
36 | {
37 | switch (type)
38 | {
39 | case ObservationPointType.Unknown:
40 | return "不明";
41 |
42 | case ObservationPointType.KiK_net:
43 | return "KiK-net";
44 |
45 | case ObservationPointType.K_NET:
46 | return "K-NET";
47 | }
48 | return "エラー";
49 | }
50 |
51 | ///
52 | /// EqWatchの観測点の種類からObservationPointTypeに変換します。
53 | ///
54 | /// 変換元
55 | /// 変換後
56 | public static ObservationPointType ToObservationPointType(this string str)
57 | => str == "1" ? ObservationPointType.KiK_net : ObservationPointType.K_NET;
58 | }
59 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/Location.cs:
--------------------------------------------------------------------------------
1 | using MessagePack;
2 | using System;
3 | using System.Runtime.Serialization;
4 |
5 | namespace KyoshinMonitorLib
6 | {
7 | ///
8 | /// 地理座標
9 | ///
10 | [MessagePackObject, DataContract]
11 | public class Location
12 | {
13 | ///
14 | /// Locationを初期化します。
15 | ///
16 | public Location()
17 | {
18 | }
19 |
20 | ///
21 | /// 初期値を指定してLocationを初期化します。
22 | ///
23 | /// 緯度
24 | /// 経度
25 | public Location(float latitude, float longitude)
26 | {
27 | Latitude = latitude;
28 | Longitude = longitude;
29 | }
30 | ///
31 | /// メートル座標を指定してLocationを初期化します。
32 | ///
33 | /// 北緯方向への距離
34 | /// 東経方向への距離
35 | public static Location FromMeters(double x, double y)
36 | {
37 | x = x / 20037508.34 * 180;
38 | y = y / 20037508.34 * 180;
39 | x = 180 / Math.PI * (2 * Math.Atan(Math.Exp(x * Math.PI / 180)) - Math.PI / 2);
40 |
41 | return new Location((float)x, (float)y);
42 | }
43 |
44 | ///
45 | /// 緯度
46 | ///
47 | [Key(0), DataMember(Order = 0)]
48 | public float Latitude { get; set; }
49 |
50 | ///
51 | /// 経度
52 | ///
53 | [Key(1), DataMember(Order = 1)]
54 | public float Longitude { get; set; }
55 |
56 | ///
57 | /// 文字化
58 | ///
59 | /// 文字
60 | public override string ToString()
61 | => $"Lat:{Latitude} Lng:{Longitude}";
62 | }
63 | }
--------------------------------------------------------------------------------
/.github/workflows/publish-nuget.yml:
--------------------------------------------------------------------------------
1 | name: Publish NuGet
2 |
3 | on:
4 | push:
5 | branches:
6 | - nuget-publish
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v1
13 | - uses: actions/setup-dotnet@v1
14 | with:
15 | dotnet-version: '3.0.100'
16 | - run: dotnet build src/KyoshinMonitorLib/KyoshinMonitorLib.csproj -c Release
17 | - run: dotnet build src/KyoshinMonitorLib.Images/KyoshinMonitorLib.Images.csproj -c Release
18 | - run: dotnet build src/KyoshinMonitorLib.Timers/KyoshinMonitorLib.Timers.csproj -c Release
19 | - run: dotnet build src/KyoshinMonitorLib.Training/KyoshinMonitorLib.Training.csproj -c Release
20 | - run: dotnet nuget push src/KyoshinMonitorLib/bin/Release/KyoshinMonitorLib.*.nupkg -k $NUGET_OLG_API_KEY -s https://api.nuget.org/v3/index.json
21 | env:
22 | NUGET_OLG_API_KEY: ${{ secrets.NUGET_OLG_API_KEY }}
23 | - run: dotnet nuget push src/KyoshinMonitorLib.Images/bin/Release/KyoshinMonitorLib.Images.*.nupkg -k $NUGET_OLG_API_KEY -s https://api.nuget.org/v3/index.json
24 | env:
25 | NUGET_OLG_API_KEY: ${{ secrets.NUGET_OLG_API_KEY }}
26 | - run: dotnet nuget push src/KyoshinMonitorLib.Timers/bin/Release/KyoshinMonitorLib.Timers.*.nupkg -k $NUGET_OLG_API_KEY -s https://api.nuget.org/v3/index.json
27 | env:
28 | NUGET_OLG_API_KEY: ${{ secrets.NUGET_OLG_API_KEY }}
29 | - run: dotnet nuget push src/KyoshinMonitorLib.Training/bin/Release/KyoshinMonitorLib.Training.*.nupkg -k $NUGET_OLG_API_KEY -s https://api.nuget.org/v3/index.json
30 | env:
31 | NUGET_OLG_API_KEY: ${{ secrets.NUGET_OLG_API_KEY }}
32 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/WebApi.cs:
--------------------------------------------------------------------------------
1 | using KyoshinMonitorLib.ApiResult.WebApi;
2 | using KyoshinMonitorLib.UrlGenerator;
3 | using System;
4 | using System.Threading.Tasks;
5 |
6 | namespace KyoshinMonitorLib
7 | {
8 | ///
9 | /// Webで使用されているAPI
10 | ///
11 | public class WebApi : Api
12 | {
13 | ///
14 | /// 緊急地震速報の情報を取得します。
15 | ///
16 | /// 取得する時間
17 | ///
18 | public Task> GetEewInfo(DateTime time)
19 | => GetJsonObject(WebApiUrlGenerator.Generate(WebApiUrlType.EewJson, time), ApiResult.ApiResultJsonContext.Default.Eew);
20 |
21 | ///
22 | /// リアルタイム画像の生データを取得します。
23 | ///
24 | /// 取得する時間
25 | /// 取得する画像の種類
26 | /// 地下かどうか
27 | ///
28 | public Task> GetRealtimeImageData(DateTime time, RealtimeDataType dataType, bool isBehore = false)
29 | => GetBytes(WebApiUrlGenerator.Generate(WebApiUrlType.RealtimeImg, time, dataType, isBehore));
30 |
31 | ///
32 | /// 予測震度の画像を取得します。
33 | ///
34 | /// 取得する時間
35 | ///
36 | public Task> GetEstShindoImageData(DateTime time)
37 | => GetBytes(WebApiUrlGenerator.Generate(WebApiUrlType.EstShindo, time));
38 |
39 | ///
40 | /// P波、S波の広がりを示す円の画像を取得します。
41 | ///
42 | /// 取得する時間
43 | ///
44 | public Task> GetPSWaveImageData(DateTime time)
45 | => GetBytes(WebApiUrlGenerator.Generate(WebApiUrlType.PSWave, time));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json;
3 |
4 | namespace KyoshinMonitorLib
5 | {
6 | ///
7 | /// 拡張メソッドたち
8 | ///
9 | public static class Extensions
10 | {
11 | ///
12 | /// 観測点情報をcsvに保存します。失敗した場合は例外がスローされます。
13 | ///
14 | /// 書き込む観測点情報の配列
15 | /// 書き込むpbfファイルのパス
16 | public static void SaveToCsv(this IEnumerable points, string path)
17 | => ObservationPoint.SaveToCsv(path, points);
18 |
19 | ///
20 | /// 観測点情報をmpk形式で保存します。失敗した場合は例外がスローされます。
21 | ///
22 | /// 書き込むmpkファイルのパス
23 | /// 書き込む観測点情報の配列
24 | /// lz4で圧縮させるかどうか(させる場合は拡張子を.mpk.lz4にすることをおすすめします)
25 | public static void SaveToMpk(this IEnumerable points, string path, bool usingLz4 = false)
26 | => ObservationPoint.SaveToMpk(path, points, usingLz4);
27 |
28 | ///
29 | /// 観測点情報をJson形式で保存します。失敗した場合は例外がスローされます。
30 | ///
31 | /// 書き込むJsonファイルのパス
32 | /// 書き込む観測点情報の配列
33 | public static void SaveToJson(this IEnumerable points, string path)
34 | => ObservationPoint.SaveToJson(path, points);
35 |
36 | ///
37 | /// nullの際何故か空白の文字列になってしまうJson要素の解析
38 | ///
39 | public static bool? ToBoolFromStringableBool(this JsonElement element)
40 | {
41 | switch (element.ValueKind)
42 | {
43 | case JsonValueKind.True:
44 | return true;
45 | case JsonValueKind.False:
46 | return false;
47 | default:
48 | return null;
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.Images/ImageAnalysisResult.cs:
--------------------------------------------------------------------------------
1 | using System.Drawing;
2 |
3 | namespace KyoshinMonitorLib.Images
4 | {
5 | ///
6 | /// 画像解析結果
7 | ///
8 | public class ImageAnalysisResult
9 | {
10 | ///
11 | /// 観測点情報
12 | ///
13 | public ObservationPoint ObservationPoint { get; }
14 |
15 | ///
16 | /// 解析されたスケール
17 | ///
18 | public double? AnalysisResult { get; set; }
19 |
20 | ///
21 | /// 解析に使用した色
22 | ///
23 | public Color Color { get; set; }
24 |
25 | ///
26 | /// ObservationPointを元にImageAnalysisResultを初期化します。
27 | ///
28 | /// 元にするObservationPoint
29 | public ImageAnalysisResult(ObservationPoint point)
30 | {
31 | ObservationPoint = point;
32 | }
33 |
34 | ///
35 | /// 結果を震度として返します
36 | ///
37 | ///
38 | public double? GetResultToIntensity()
39 | {
40 | if (AnalysisResult is not double result)
41 | return null;
42 | return ColorConverter.ConvertToIntensityFromScale(result);
43 | }
44 | ///
45 | /// 結果を最大加速度(PGA)として返します
46 | ///
47 | ///
48 | public double? GetResultToPga()
49 | {
50 | if (AnalysisResult is not double result)
51 | return null;
52 | return ColorConverter.ConvertToPgaFromScale(result);
53 | }
54 | ///
55 | /// 結果を最大速度(PGV)として返します
56 | ///
57 | ///
58 | public double? GetResultToPgv()
59 | {
60 | if (AnalysisResult is not double result)
61 | return null;
62 | return ColorConverter.ConvertToPgvFromScale(result);
63 | }
64 | ///
65 | /// 結果を最大変位(PGD)として返します
66 | ///
67 | ///
68 | public double? GetResultToPgd()
69 | {
70 | if (AnalysisResult is not double result)
71 | return null;
72 | return ColorConverter.ConvertToPgdFromScale(result);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.SkiaImages/ImageAnalysisResult.cs:
--------------------------------------------------------------------------------
1 | using SkiaSharp;
2 |
3 | namespace KyoshinMonitorLib.SkiaImages
4 | {
5 | ///
6 | /// 画像解析結果
7 | ///
8 | public class ImageAnalysisResult
9 | {
10 | ///
11 | /// 観測点情報
12 | ///
13 | public ObservationPoint ObservationPoint { get; }
14 |
15 | ///
16 | /// 解析されたスケール
17 | ///
18 | public double? AnalysisResult { get; set; }
19 |
20 | ///
21 | /// 解析に使用した色
22 | ///
23 | public SKColor Color { get; set; }
24 |
25 | ///
26 | /// ObservationPointを元にImageAnalysisResultを初期化します。
27 | ///
28 | /// 元にするObservationPoint
29 | public ImageAnalysisResult(ObservationPoint point)
30 | {
31 | ObservationPoint = point;
32 | }
33 |
34 | ///
35 | /// 結果を震度として返します
36 | ///
37 | ///
38 | public double? GetResultToIntensity()
39 | {
40 | if (AnalysisResult is not double result)
41 | return null;
42 | return ColorConverter.ConvertToIntensityFromScale(result);
43 | }
44 | ///
45 | /// 結果を最大加速度(PGA)として返します
46 | ///
47 | ///
48 | public double? GetResultToPga()
49 | {
50 | if (AnalysisResult is not double result)
51 | return null;
52 | return ColorConverter.ConvertToPgaFromScale(result);
53 | }
54 | ///
55 | /// 結果を最大速度(PGV)として返します
56 | ///
57 | ///
58 | public double? GetResultToPgv()
59 | {
60 | if (AnalysisResult is not double result)
61 | return null;
62 | return ColorConverter.ConvertToPgvFromScale(result);
63 | }
64 | ///
65 | /// 結果を最大変位(PGD)として返します
66 | ///
67 | ///
68 | public double? GetResultToPgd()
69 | {
70 | if (AnalysisResult is not double result)
71 | return null;
72 | return ColorConverter.ConvertToPgdFromScale(result);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/LpgmWebApi.cs:
--------------------------------------------------------------------------------
1 | using KyoshinMonitorLib.ApiResult.WebApi;
2 | using KyoshinMonitorLib.UrlGenerator;
3 | using System;
4 | using System.Threading.Tasks;
5 |
6 | namespace KyoshinMonitorLib
7 | {
8 | ///
9 | /// 長周期地震動モニタで使用されているAPI
10 | ///
11 | public class LpgmWebApi : Api
12 | {
13 | ///
14 | /// 緊急地震速報の情報を取得します。
15 | ///
16 | /// 取得する時間
17 | ///
18 | public Task> GetEewInfo(DateTime time)
19 | => GetJsonObject(LpgmWebApiUrlGenerator.Generate(LpgmWebApiUrlType.EewJson, time), ApiResult.ApiResultJsonContext.Default.Eew);
20 |
21 | ///
22 | /// リアルタイム画像の生データを取得します。
23 | ///
24 | /// 取得する時間
25 | /// 取得する画像の種類
26 | /// 地下かどうか
27 | ///
28 | public Task> GetRealtimeImageData(DateTime time, RealtimeDataType dataType, bool isBehore = false)
29 | => GetBytes(LpgmWebApiUrlGenerator.Generate(LpgmWebApiUrlType.RealtimeImg, time, dataType, isBehore));
30 |
31 | ///
32 | /// 長周期地震動関連のリアルタイム画像の生データを取得します。
33 | ///
34 | /// 取得する時間
35 | /// 取得する画像の種類
36 | ///
37 | public Task> GetLpgmRealtimeImageData(DateTime time, RealtimeDataType dataType)
38 | => GetBytes(LpgmWebApiUrlGenerator.Generate(LpgmWebApiUrlType.LpgmRealtimeImg, time, dataType));
39 |
40 | ///
41 | /// 予測震度の画像を取得します。
42 | ///
43 | /// 取得する時間
44 | ///
45 | public Task> GetEstShindoImageData(DateTime time)
46 | => GetBytes(LpgmWebApiUrlGenerator.Generate(LpgmWebApiUrlType.EstShindo, time));
47 |
48 | ///
49 | /// P波、S波の広がりを示す円の画像を取得します。
50 | ///
51 | /// 取得する時間
52 | ///
53 | public Task> GetPSWaveImageData(DateTime time)
54 | => GetBytes(LpgmWebApiUrlGenerator.Generate(LpgmWebApiUrlType.PSWave, time));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/Api.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Text.Json;
4 | using System.Text.Json.Serialization.Metadata;
5 | using System.Threading.Tasks;
6 |
7 | namespace KyoshinMonitorLib
8 | {
9 | ///
10 | /// APIのベースクラス
11 | ///
12 | public abstract class Api : IDisposable
13 | {
14 | private HttpClient HttpClient { get; } = new() { Timeout = TimeSpan.FromSeconds(10) };
15 | ///
16 | /// APIを呼ぶのにあたってのタイムアウト時間
17 | ///
18 | public TimeSpan Timeout
19 | {
20 | get => HttpClient.Timeout;
21 | set => HttpClient.Timeout = value;
22 | }
23 |
24 | ///
25 | /// GETリクエストを送信し、Jsonをデシリアライズした結果を取得します。
26 | ///
27 | /// デシリアライズする型
28 | /// 使用するURL
29 | /// 使用するTypeInfo
30 | ///
31 | protected async Task> GetJsonObject(string url, JsonTypeInfo jsonTypeInfo)
32 | {
33 | try
34 | {
35 | using var response = await HttpClient.GetAsync(url);
36 | if (!response.IsSuccessStatusCode)
37 | return new(response.StatusCode, default);
38 |
39 | return new(response.StatusCode, JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync(), jsonTypeInfo));
40 | }
41 | catch (TaskCanceledException)
42 | {
43 | throw new KyoshinMonitorException("Request Timeout: " + url);
44 | }
45 | }
46 | ///
47 | /// GETリクエストを送信し、生のbyte配列を取得します。
48 | ///
49 | /// 使用するURL
50 | ///
51 | protected async Task> GetBytes(string url)
52 | {
53 | try
54 | {
55 | using var response = await HttpClient.GetAsync(url);
56 | if (!response.IsSuccessStatusCode)
57 | return new(response.StatusCode, default);
58 |
59 | return new(response.StatusCode, await response.Content.ReadAsByteArrayAsync());
60 | }
61 | catch (TaskCanceledException)
62 | {
63 | throw new KyoshinMonitorException("Request Timeout: " + url);
64 | }
65 | }
66 |
67 | ///
68 | /// オブジェクトの破棄を行います。
69 | ///
70 | public void Dispose()
71 | {
72 | HttpClient?.Dispose();
73 | GC.SuppressFinalize(this);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/UrlGenerator/WebApiUrlGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KyoshinMonitorLib.UrlGenerator
4 | {
5 | ///
6 | /// 新強震モニタのURL生成器
7 | ///
8 | public static class WebApiUrlGenerator
9 | {
10 | ///
11 | /// JsonEewのベースURL
12 | /// 0:時間
13 | ///
14 | public const string JsonEewBase = "http://www.kmoni.bosai.go.jp/webservice/hypo/eew/{0}.json";
15 |
16 | ///
17 | /// PsWaveImgのベースURL
18 | /// 0:日付
19 | /// 1:時間
20 | ///
21 | public const string PsWaveBase = "http://www.kmoni.bosai.go.jp/data/map_img/PSWaveImg/eew/{0}/{1}.eew.gif";
22 |
23 | ///
24 | /// RealtimeImgのベースURL
25 | /// 0:タイプ
26 | /// 1:地上(s)/地下(b)
27 | /// 2:日付
28 | /// 3:時間
29 | ///
30 | public const string RealtimeBase = "http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/{0}_{1}/{2}/{3}.{0}_{1}.gif";
31 |
32 | ///
33 | /// 予想震度のベースURL
34 | /// 0:日付
35 | /// 1:時間
36 | ///
37 | public const string EstShindoBase = "http://www.kmoni.bosai.go.jp/data/map_img/EstShindoImg/eew/{0}/{1}.eew.gif";
38 |
39 | ///
40 | /// 与えられた値を使用してURLを生成します。
41 | ///
42 | /// 生成するURLのタイプ
43 | /// 生成するURLの時間
44 | /// (UrlType=RealtimeImgの際に使用)取得するリアルタイム情報の種類
45 | /// (UrlType=RealtimeImgの際に使用)地中の情報を取得するかどうか
46 | ///
47 | public static string Generate(WebApiUrlType urlType, DateTime datetime, RealtimeDataType realtimeShindoType = RealtimeDataType.Shindo, bool isBerehole = false) => urlType switch
48 | {
49 | WebApiUrlType.RealtimeImg => string.Format(RealtimeBase, realtimeShindoType.ToUrlString(), isBerehole ? "b" : "s", datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
50 | WebApiUrlType.EstShindo => string.Format(EstShindoBase, datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
51 | WebApiUrlType.PSWave => string.Format(PsWaveBase, datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
52 | WebApiUrlType.EewJson => string.Format(JsonEewBase, datetime.ToString("yyyyMMddHHmmss")),
53 | _ => throw new ArgumentException($"URLを生成できない{nameof(WebApiUrlType)}が指定されています", nameof(urlType)),
54 | };
55 | }
56 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/Mesh.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using MessagePack;
6 |
7 | namespace KyoshinMonitorLib
8 | {
9 | ///
10 | /// 5kmメッシュ情報
11 | ///
12 | public class Mesh
13 | {
14 | ///
15 | /// 初期化
16 | ///
17 | public Mesh(string code, Location location1, Location location2)
18 | {
19 | Code = code ?? throw new ArgumentNullException(nameof(code));
20 | LocationLeftTop = location1 ?? throw new ArgumentNullException(nameof(location1));
21 | LocationRightBottom = location2 ?? throw new ArgumentNullException(nameof(location2));
22 | }
23 | ///
24 | /// 初期化
25 | ///
26 | public Mesh(string code, double x, double y)
27 | {
28 | Code = code ?? throw new ArgumentNullException(nameof(code));
29 |
30 | LocationLeftTop = Location.FromMeters(x, y);
31 | LocationRightBottom = Location.FromMeters(x - 5000, y + 5000); //5kmメッシュ
32 | }
33 |
34 | ///
35 | /// メッシュ情報をmpkから読み込みます。失敗した場合は例外がスローされます。
36 | ///
37 | /// 読み込むmpkファイルのパス
38 | /// lz4で圧縮させるかどうか(させる場合は拡張子を.mpk.lz4にすることをおすすめします)
39 | /// 読み込まれたメッシュ情報
40 | public static ObservationPoint[] LoadFromMpk(string path, bool useLz4 = false)
41 | {
42 | using var stream = new FileStream(path, FileMode.Open);
43 | return MessagePackSerializer.Deserialize(stream, options: useLz4 ? MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block) : null);
44 | }
45 | ///
46 | /// メッシュ情報をmpk形式で保存します。失敗した場合は例外がスローされます。
47 | ///
48 | /// 書き込むmpkファイルのパス
49 | /// 書き込むメッシュ情報の配列
50 | /// lz4で圧縮させるかどうか(させる場合は拡張子を.mpk.lz4にすることをおすすめします)
51 | public static void SaveToMpk(string path, IEnumerable points, bool useLz4 = false)
52 | {
53 | using var stream = new FileStream(path, FileMode.Create);
54 | MessagePackSerializer.Serialize(stream, points.ToArray(), options: useLz4 ? MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block) : null);
55 | }
56 |
57 |
58 | ///
59 | /// 地点コード
60 | ///
61 | [Key(0)]
62 | public string Code { get; set; }
63 | ///
64 | /// 座標(左上)
65 | ///
66 | [Key(1)]
67 | public Location LocationLeftTop { get; set; }
68 | ///
69 | /// 座標(右下)
70 | ///
71 | [Key(2)]
72 | public Location LocationRightBottom { get; set; }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | # ******** NOTE ********
12 |
13 | name: "CodeQL"
14 |
15 | on:
16 | push:
17 | branches: [ master ]
18 | pull_request:
19 | # The branches below must be a subset of the branches above
20 | branches: [ master ]
21 | schedule:
22 | - cron: '44 20 * * 3'
23 |
24 | jobs:
25 | analyze:
26 | name: Analyze
27 | runs-on: windows-latest
28 |
29 | strategy:
30 | fail-fast: false
31 | matrix:
32 | language: [ 'csharp' ]
33 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
34 | # Learn more...
35 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
36 |
37 | steps:
38 | - name: Checkout repository
39 | uses: actions/checkout@v2
40 |
41 | # Initializes the CodeQL tools for scanning.
42 | - name: Initialize CodeQL
43 | uses: github/codeql-action/init@v1
44 | with:
45 | languages: ${{ matrix.language }}
46 | # If you wish to specify custom queries, you can do so here or in a config file.
47 | # By default, queries listed here will override any specified in a config file.
48 | # Prefix the list here with "+" to use these queries and those in the config file.
49 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
50 |
51 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
52 | # If this step fails, then you should remove it and run the build manually (see below)
53 | - name: Autobuild
54 | uses: github/codeql-action/autobuild@v1
55 |
56 | # ℹ️ Command-line programs to run using the OS shell.
57 | # 📚 https://git.io/JvXDl
58 |
59 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
60 | # and modify them (or add more) to build your code if your project
61 | # uses a compiled language
62 |
63 | #- run: |
64 | # make bootstrap
65 | # make release
66 |
67 | - name: Perform CodeQL Analysis
68 | uses: github/codeql-action/analyze@v1
69 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/UrlGenerator/AppApiUrlGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KyoshinMonitorLib.UrlGenerator
4 | {
5 | ///
6 | /// 強震モニタスマホアプリのURL生成器
7 | ///
8 | public class AppApiUrlGenerator
9 | {
10 | ///
11 | /// リアルタイムJsonのベースURL
12 | /// 0: 種別(jma_sなど)
13 | /// 1: s=地上 b=地下
14 | /// 2: yyyyMMdd
15 | /// 3: yyyyMMddHHmmss
16 | ///
17 | public const string RealtimeDataBase = "http://ts.qtmoni.bosai.go.jp/qt/tsapp/kyoshin_monitor/static/sip_data/RealTimeData/kyoshin_cnt/{0}_{1}/{2}/{3}_{0}_{1}.json";
18 |
19 | ///
20 | /// EEW関係のJsonのベースURL
21 | /// 0: データタイプ(EstShindoJsonV2など)
22 | /// 1: yyyyMMdd
23 | /// 2: yyyyMMddHHmmss
24 | /// 3: データタイプの略称(estなど)
25 | ///
26 | public const string EewJsonBase = "http://kv.kmoni.bosai.go.jp/kyoshin_monitor/static/jsondata/{0}/eew/{1}/{2}_eew_{3}.json";
27 |
28 | ///
29 | /// 観測点一覧のベースURL
30 | /// 0: baseSerialNo
31 | ///
32 | public const string SiteListBase = "http://ts.qtmoni.bosai.go.jp/qt/tsapp/kyoshin_monitor/static/sip_data/site_list/eq/{0}.json";
33 |
34 | ///
35 | /// メッシュ一覧のURL
36 | ///
37 | public const string Meches = "http://kv.kmoni.bosai.go.jp/webservice/est/mesh_v2/list.json";
38 |
39 | #pragma warning disable CS0618 // 型またはメンバーが旧型式です
40 | ///
41 | /// 与えられた値を使用してURLを生成します。
42 | ///
43 | /// 生成するURLのタイプ
44 | /// 生成するURLの時間
45 | /// (UrlType=RealtimeDataの際に使用)取得するリアルタイム情報の種類
46 | /// (UrlType=RealtimeDataの際に使用)地中の情報を取得するかどうか
47 | ///
48 | public static string Generate(AppApiUrlType urlType, DateTime datetime, RealtimeDataType realtimeShindoType = RealtimeDataType.Shindo, bool isBerehole = false) => urlType switch
49 | {
50 | AppApiUrlType.RealtimeData => string.Format(RealtimeDataBase, realtimeShindoType.ToUrlString(), isBerehole ? "b" : "s", datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
51 | AppApiUrlType.EstShindoJson => string.Format(EewJsonBase, "EstShindoJsonV2", datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss"), "est"),
52 | AppApiUrlType.PSWaveJson => string.Format(EewJsonBase, "PSWaveJsonV2", datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss"), "psw"),
53 | AppApiUrlType.HypoInfoJson => string.Format(EewJsonBase, "HypoInfoJsonV2", datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss"), "hypo"),
54 | _ => throw new ArgumentException($"URLを生成できない{nameof(AppApiUrlType)}が指定されています", nameof(urlType)),
55 | };
56 | #pragma warning restore CS0618 // 型またはメンバーが旧型式です
57 | ///
58 | /// 観測点一覧のURLを生成します。
59 | ///
60 | /// シリアル番号
61 | ///
62 | public static string Generate(string baseSerialNo)
63 | => string.Format(SiteListBase, baseSerialNo);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Tests/Program.cs:
--------------------------------------------------------------------------------
1 | using KyoshinMonitorLib;
2 | using KyoshinMonitorLib.SkiaImages;
3 | using KyoshinMonitorLib.Timers;
4 | using KyoshinMonitorLib.UrlGenerator;
5 | using System;
6 | using System.Linq;
7 | using System.Net;
8 |
9 | var points = ObservationPoint.LoadFromMpk("ShindoObsPoints.mpk.lz4", true);
10 | using var webApi = new LpgmWebApi();
11 | // タイマーのインスタンスを作成
12 | var timer = new SecondBasedTimer()
13 | {
14 | Offset = TimeSpan.FromSeconds(1.1),//1.1
15 | };
16 | // 適当にイベント設定
17 | timer.Elapsed += async time =>
18 | {
19 | Console.WriteLine($"\nsys: {DateTime.Now:HH:mm:ss.fff} ntp:{time:HH:mm:ss.fff}");
20 |
21 | try
22 | {
23 | // WebAPIから結果を計算 (良い子のみんなはawaitを使おうね!)
24 | var result = await webApi.ParseScaleFromParameterAsync(points, time);
25 | if (result.Data != null)
26 | {
27 | var data = result.Data.ToArray();
28 | // 現在の最大震度
29 | Console.WriteLine($"*WEB* 最大震度: 生:{data.Max(r => r.GetResultToIntensity()):0.0} jma:{data.Max(r => r.GetResultToIntensity()).ToJmaIntensity().ToLongString()} 数:{data.Count(d => d.AnalysisResult != null)}");
30 | }
31 | else if (result.StatusCode == HttpStatusCode.NotFound)
32 | {
33 | // timer.Offset += TimeSpan.FromMilliseconds(100);
34 | // Console.WriteLine($"404のためオフセット調整 to:{timer.Offset.TotalSeconds}s");
35 | Console.WriteLine($"404");
36 | }
37 | else
38 | Console.WriteLine($"*WEB* 取得失敗 " + result.StatusCode);
39 | }
40 | catch (Exception ex)
41 | {
42 | Console.WriteLine($"*WEB* 取得失敗 " + ex);
43 | }
44 | try
45 | {
46 | var result = await webApi.ParseScaleFromParameterAsync(points, time, RealtimeDataType.Pga);
47 | if (result.Data != null)
48 | {
49 | var data = result.Data.ToArray();
50 | // 現在の最大震度
51 | Console.WriteLine($"*WEB* 最大PGA: 生:{data.Max(r => r.GetResultToPga()):0.0} 数:{data.Count(d => d.AnalysisResult != null)}");
52 | }
53 | else if (result.StatusCode == HttpStatusCode.NotFound)
54 | Console.WriteLine($"404");
55 | else
56 | Console.WriteLine($"*WEB* 取得失敗 " + result.StatusCode);
57 | }
58 | catch (Exception ex)
59 | {
60 | Console.WriteLine($"*WEB* 取得失敗 " + ex);
61 | }
62 | try
63 | {
64 | var result = await webApi.GetEewInfo(time);
65 | if (result.Data != null)
66 | {
67 | // 現在の最大震度
68 | Console.WriteLine($"*EEW* {result.Data.ReportNum}");
69 | }
70 | else if (result.StatusCode == HttpStatusCode.NotFound)
71 | Console.WriteLine($"404");
72 | else
73 | Console.WriteLine($"*EEW* 取得失敗 " + result.StatusCode);
74 | }
75 | catch (Exception ex)
76 | {
77 | Console.WriteLine($"*EEW* 取得失敗 " + ex);
78 | }
79 | };
80 |
81 | var ntp = await NtpAssistance.GetNetworkTimeWithHttp();
82 | // タイマー開始
83 | timer.Start(ntp ?? throw new Exception());
84 |
85 | Console.WriteLine("Enterキー入力で終了");
86 |
87 | // 改行入力待ち
88 | Console.ReadLine();
89 |
90 | // タイマー終了
91 | timer.Stop();
92 |
93 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.SkiaImages/ColorConverter.cs:
--------------------------------------------------------------------------------
1 | using SkiaSharp;
2 | using System;
3 |
4 | namespace KyoshinMonitorLib.SkiaImages
5 | {
6 | ///
7 | /// 色を震度に変換する
8 | ///
9 | public static class ColorConverter
10 | {
11 | ///
12 | /// スケールを震度に変換します。
13 | ///
14 | /// 変換前のスケール
15 | ///
16 | public static double ConvertToIntensityFromScale(double scale)
17 | => scale * 10 - 3;
18 |
19 | ///
20 | /// スケールをPGA(最大加速度)に変換します。
21 | ///
22 | /// 変換前のスケール
23 | ///
24 | public static double ConvertToPgaFromScale(double scale)
25 | => Math.Pow(10, 5 * scale - 2);
26 |
27 | ///
28 | /// スケールをPGV(最大速度)に変換します。
29 | ///
30 | /// 変換前のスケール
31 | ///
32 | public static double ConvertToPgvFromScale(double scale)
33 | => Math.Pow(10, 5 * scale - 3);
34 |
35 | ///
36 | /// スケールをPGD(最大変位)に変換します。
37 | ///
38 | /// 変換前のスケール
39 | ///
40 | public static double ConvertToPgdFromScale(double scale)
41 | => Math.Pow(10, 5 * scale - 4);
42 |
43 | ///
44 | /// 多項式補完を使用して色をスケールに変換します。
45 | /// from: https://qiita.com/NoneType1/items/a4d2cf932e20b56ca444
46 | ///
47 | /// 変換元の色
48 | /// 変換後のスケール
49 | public static double ConvertToScaleAtPolynomialInterpolation(SKColor color)
50 | => Math.Max(0, ConvertToScaleAtPolynomialInterpolationInternal(color));
51 | private static double ConvertToScaleAtPolynomialInterpolationInternal(SKColor color)
52 | {
53 | // Input : color in hsv space
54 | (var h, var s, var v) = GetHsv(color);
55 | h /= 360;
56 |
57 | // Check if the color belongs to the scale
58 | if (v <= 0.1 || s <= 0.75)
59 | return 0;
60 |
61 | if (h > 0.1476)
62 | return 280.31 * Math.Pow(h, 6) - 916.05 * Math.Pow(h, 5) + 1142.6 * Math.Pow(h, 4) - 709.95 * Math.Pow(h, 3) + 234.65 * Math.Pow(h, 2) - 40.27 * h + 3.2217;
63 | else if (h > 0.001)
64 | return 151.4 * Math.Pow(h, 4) - 49.32 * Math.Pow(h, 3) + 6.753 * Math.Pow(h, 2) - 2.481 * h + 0.9033;
65 | else
66 | return -0.005171 * Math.Pow(v, 2) - 0.3282 * v + 1.2236;
67 | }
68 |
69 | ///
70 | /// 指定した色をHSVで返す
71 | ///
72 | /// 変換する色
73 | /// 変換した色
74 | private static (double h, double s, double v) GetHsv(SKColor rgb)
75 | {
76 | var max = Math.Max(rgb.Red, Math.Max(rgb.Green, rgb.Blue));
77 | var min = Math.Min(rgb.Red, Math.Min(rgb.Green, rgb.Blue));
78 |
79 | if (min == max)
80 | return (0, 0, max / 255d);
81 | double w = max - min;
82 | var h = 0d;
83 | if (rgb.Red == max)
84 | h = (rgb.Green - rgb.Blue) / w;
85 | if (rgb.Green == max)
86 | h = ((rgb.Blue - rgb.Red) / w) + 2;
87 | if (rgb.Blue == max)
88 | h = ((rgb.Red - rgb.Green) / w) + 4;
89 | if ((h *= 60) < 0)
90 | h += 360;
91 | return (h, (double)(max - min) / max, max / 255d);
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/UrlGenerator/RealTimeDataType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KyoshinMonitorLib.UrlGenerator
4 | {
5 | ///
6 | /// リアルタイム画像の種類
7 | ///
8 | public enum RealtimeDataType
9 | {
10 | ///
11 | /// 震度
12 | ///
13 | Shindo,
14 |
15 | ///
16 | /// 最大加速度
17 | ///
18 | Pga,
19 |
20 | ///
21 | /// 最大速度
22 | ///
23 | Pgv,
24 |
25 | ///
26 | /// 最大変位
27 | ///
28 | Pgd,
29 |
30 | ///
31 | /// 速度応答0.125Hz
32 | ///
33 | Response_0_125Hz,
34 |
35 | ///
36 | /// 速度応答0.25Hz
37 | ///
38 | Response_0_25Hz,
39 |
40 | ///
41 | /// 速度応答0.5Hz
42 | ///
43 | Response_0_5Hz,
44 |
45 | ///
46 | /// 速度応答1Hz
47 | ///
48 | Response_1Hz,
49 |
50 | ///
51 | /// 速度応答2Hz
52 | ///
53 | Response_2Hz,
54 |
55 | ///
56 | /// 速度応答4Hz
57 | ///
58 | Response_4Hz,
59 |
60 | ///
61 | /// 長周期地震動階級
62 | /// Lpgm系列でのみ利用可
63 | ///
64 | Abrspmx,
65 |
66 | ///
67 | /// 階級データ(周期1秒台)
68 | /// Lpgm系列でのみ利用可
69 | ///
70 | Abrsp_1s,
71 |
72 | ///
73 | /// 階級データ(周期2秒台)
74 | /// Lpgm系列でのみ利用可
75 | ///
76 | Abrsp_2s,
77 |
78 | ///
79 | /// 階級データ(周期3秒台)
80 | /// Lpgm系列でのみ利用可
81 | ///
82 | Abrsp_3s,
83 |
84 | ///
85 | /// 階級データ(周期4秒台)
86 | /// Lpgm系列でのみ利用可
87 | ///
88 | Abrsp_4s,
89 |
90 | ///
91 | /// 階級データ(周期5秒台)
92 | /// Lpgm系列でのみ利用可
93 | ///
94 | Abrsp_5s,
95 |
96 | ///
97 | /// 階級データ(周期6秒台)
98 | /// Lpgm系列でのみ利用可
99 | ///
100 | Abrsp_6s,
101 |
102 | ///
103 | /// 階級データ(周期7秒台)
104 | /// Lpgm系列でのみ利用可
105 | ///
106 | Abrsp_7s,
107 | }
108 |
109 | ///
110 | /// RealtimeImgTypeの拡張メソッド
111 | ///
112 | public static class RealtimeDataExtensions
113 | {
114 | ///
115 | /// URLに使用する文字列に変換する
116 | ///
117 | /// 変換するRealtimeImgTypy
118 | /// 変換された文字列
119 | public static string ToUrlString(this RealtimeDataType type) => type switch
120 | {
121 | RealtimeDataType.Shindo => "jma",
122 | RealtimeDataType.Pga => "acmap",
123 | RealtimeDataType.Pgv => "vcmap",
124 | RealtimeDataType.Pgd => "dcmap",
125 | RealtimeDataType.Response_0_125Hz => "rsp0125",
126 | RealtimeDataType.Response_0_25Hz => "rsp0250",
127 | RealtimeDataType.Response_0_5Hz => "rsp0500",
128 | RealtimeDataType.Response_1Hz => "rsp1000",
129 | RealtimeDataType.Response_2Hz => "rsp2000",
130 | RealtimeDataType.Response_4Hz => "rsp4000",
131 | RealtimeDataType.Abrspmx => "abrspmx",
132 | RealtimeDataType.Abrsp_1s => "abrsp1s",
133 | RealtimeDataType.Abrsp_2s => "abrsp2s",
134 | RealtimeDataType.Abrsp_3s => "abrsp3s",
135 | RealtimeDataType.Abrsp_4s => "abrsp4s",
136 | RealtimeDataType.Abrsp_5s => "abrsp5s",
137 | RealtimeDataType.Abrsp_6s => "abrsp6s",
138 | RealtimeDataType.Abrsp_7s => "abrsp7s",
139 | _ => throw new ArgumentException($"URLを生成できない{nameof(RealtimeDataType)}が指定されています", nameof(type)),
140 | };
141 | }
142 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/UrlGenerator/LpgmWebApiUrlGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KyoshinMonitorLib.UrlGenerator
4 | {
5 | ///
6 | /// 長周期地震動モニタのURL生成器
7 | ///
8 | public static class LpgmWebApiUrlGenerator
9 | {
10 | ///
11 | /// JsonEewのベースURL
12 | /// 0:時間
13 | ///
14 | public const string JsonEewBase = "https://www.lmoni.bosai.go.jp/monitor/webservice/hypo/eew/{0}.json";
15 |
16 | ///
17 | /// PsWaveImgのベースURL
18 | /// 0:日付
19 | /// 1:時間
20 | ///
21 | public const string PsWaveBase = "https://www.lmoni.bosai.go.jp/monitor/data/data/map_img/PSWaveImg/eew/{0}/{1}.eew.gif";
22 |
23 | ///
24 | /// RealtimeImgのベースURL
25 | /// 0:タイプ
26 | /// 1:地上(s)/地下(b)
27 | /// 2:日付
28 | /// 3:時間
29 | ///
30 | public const string RealtimeBase = "https://smi.lmoniexp.bosai.go.jp/data/map_img/RealTimeImg/{0}_{1}/{2}/{3}.{0}_{1}.gif";
31 |
32 | ///
33 | /// RealtimeImg(長周期地震動階級)のベースURL
34 | /// 0:タイプ
35 | /// 1:日付
36 | /// 2:時間
37 | ///
38 | public const string LpgmRealtimeBase = "https://www.lmoni.bosai.go.jp/monitor/data/data/map_img/RealTimeImg/{0}_s/{1}/{2}.{0}_s.gif";
39 |
40 | ///
41 | /// LongPeriodImg 長周期地震動の予測階級のベースURL
42 | /// 0:日付
43 | /// 1:時間
44 | ///
45 | public const string LongPeriodBase = "https://www.lmoni.bosai.go.jp/monitor/data/data/map_img/LongPeriodImg/eew/m2/{0}/{1}.eew_m2.abrspmx.gif";
46 |
47 | ///
48 | /// 予想震度のベースURL
49 | /// 0:日付
50 | /// 1:時間
51 | ///
52 | public const string EstShindoBase = "https://smi.lmoniexp.bosai.go.jp/data/map_img/EstShindoImg/eew/{0}/{1}.eew.gif";
53 |
54 | ///
55 | /// 与えられた値を使用してURLを生成します。
56 | ///
57 | /// 生成するURLのタイプ
58 | /// 生成するURLの時間
59 | /// (UrlType=RealtimeImg/LpgmRealtimeImgの際に使用)取得するリアルタイム情報の種類
60 | /// (UrlType=RealtimeImgの際に使用)地中の情報を取得するかどうか
61 | ///
62 | public static string Generate(LpgmWebApiUrlType urlType, DateTime datetime, RealtimeDataType realtimeShindoType = RealtimeDataType.Shindo, bool isBerehole = false) => urlType switch
63 | {
64 | LpgmWebApiUrlType.RealtimeImg => string.Format(RealtimeBase, realtimeShindoType.ToUrlString(), isBerehole ? "b" : "s", datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
65 | LpgmWebApiUrlType.LpgmRealtimeImg => string.Format(LpgmRealtimeBase, realtimeShindoType.ToUrlString(), datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
66 | LpgmWebApiUrlType.EstShindo => string.Format(EstShindoBase, datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
67 | LpgmWebApiUrlType.PSWave => string.Format(PsWaveBase, datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
68 | LpgmWebApiUrlType.EewJson => string.Format(JsonEewBase, datetime.ToString("yyyyMMddHHmmss")),
69 | LpgmWebApiUrlType.LongPeriodImg => string.Format(LongPeriodBase, datetime.ToString("yyyyMMdd"), datetime.ToString("yyyyMMddHHmmss")),
70 | _ => throw new ArgumentException($"URLを生成できない{nameof(LpgmWebApiUrlType)}が指定されています", nameof(urlType)),
71 | };
72 | }
73 | }
--------------------------------------------------------------------------------
/KyoshinMonitorLib.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.12.35506.116 d17.12
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0725ADC2-85A0-4F75-97F2-475756E5362F}"
7 | ProjectSection(SolutionItems) = preProject
8 | LICENSE = LICENSE
9 | README.md = README.md
10 | EndProjectSection
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KyoshinMonitorLib", "src\KyoshinMonitorLib\KyoshinMonitorLib.csproj", "{5A597781-7993-4CDB-8E50-539E93A6F705}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "src\Tests\Tests.csproj", "{40C247D7-1292-4FE5-9DAA-A47D067F2560}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KyoshinMonitorLib.Timers", "src\KyoshinMonitorLib.Timers\KyoshinMonitorLib.Timers.csproj", "{F3D3DB38-8BBC-42B2-8344-806917A4A888}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KyoshinMonitorLib.Images", "src\KyoshinMonitorLib.Images\KyoshinMonitorLib.Images.csproj", "{0A7BD896-197C-4777-80E2-1F51D8F031E5}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KyoshinMonitorLib.SkiaImages", "src\KyoshinMonitorLib.SkiaImages\KyoshinMonitorLib.SkiaImages.csproj", "{25100A14-A241-4480-8747-CF5E8676A140}"
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Release|Any CPU = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {5A597781-7993-4CDB-8E50-539E93A6F705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {5A597781-7993-4CDB-8E50-539E93A6F705}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {5A597781-7993-4CDB-8E50-539E93A6F705}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {5A597781-7993-4CDB-8E50-539E93A6F705}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {40C247D7-1292-4FE5-9DAA-A47D067F2560}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {40C247D7-1292-4FE5-9DAA-A47D067F2560}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {40C247D7-1292-4FE5-9DAA-A47D067F2560}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {40C247D7-1292-4FE5-9DAA-A47D067F2560}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {F3D3DB38-8BBC-42B2-8344-806917A4A888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {F3D3DB38-8BBC-42B2-8344-806917A4A888}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {F3D3DB38-8BBC-42B2-8344-806917A4A888}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {F3D3DB38-8BBC-42B2-8344-806917A4A888}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {0A7BD896-197C-4777-80E2-1F51D8F031E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {0A7BD896-197C-4777-80E2-1F51D8F031E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {0A7BD896-197C-4777-80E2-1F51D8F031E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {0A7BD896-197C-4777-80E2-1F51D8F031E5}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {25100A14-A241-4480-8747-CF5E8676A140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {25100A14-A241-4480-8747-CF5E8676A140}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {25100A14-A241-4480-8747-CF5E8676A140}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {25100A14-A241-4480-8747-CF5E8676A140}.Release|Any CPU.Build.0 = Release|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {D8348AF1-267F-4EC5-B52D-DB37C403AAC9}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.Timers/SecondBasedTimer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace KyoshinMonitorLib.Timers
7 | {
8 | ///
9 | /// 時刻補正機能付きタイマー
10 | ///
11 | public class SecondBasedTimer
12 | {
13 | private readonly TimeSpan Interval = TimeSpan.FromSeconds(1);
14 | private Timer InnerTimer { get; }
15 | private TimeSpan LastTime { get; set; }
16 | private Stopwatch Stopwatch { get; }
17 |
18 | ///
19 | /// 現在のタイマー内の時間
20 | ///
21 | public DateTime CurrentTime { get; private set; }
22 | ///
23 | /// イベントタスクが実行中かどうか
24 | ///
25 | public bool IsEventRunning { get; private set; }
26 | ///
27 | /// 最後に内部時刻が更新された時刻
28 | ///
29 | public DateTime LastUpdatedTime { get; private set; }
30 |
31 | private TimeSpan _offset = TimeSpan.Zero;
32 | ///
33 | /// オフセットを調整します。
34 | ///
35 | public TimeSpan Offset
36 | {
37 | get => _offset;
38 | set
39 | {
40 | LastTime -= _offset - value;
41 | _offset = value;
42 | }
43 | }
44 |
45 | ///
46 | /// タイマーの精度 ただし精度を保証するものではありません。
47 | /// 上限がある上、精度を高くするとその分重くなります。
48 | /// この値より短い単位でイベントが発行されることはありません。
49 | /// Intervalよりも大きな値を指定した際、誤差が蓄積されない特性が消滅することに気をつけてください。
50 | ///
51 | public TimeSpan Accuracy { get; set; } = TimeSpan.FromMilliseconds(1);
52 |
53 | ///
54 | /// falseにするとタイマーのイベントを1度しか発行しません
55 | ///
56 | public bool AutoReset { get; set; } = true;
57 |
58 | ///
59 | /// イベント発生時にすでに他のイベントが実行中の場合新規イベントを実行させないようにするかどうか
60 | ///
61 | public bool BlockingMode { get; set; } = true;
62 |
63 | ///
64 | /// タイマーイベント
65 | ///
66 | public event Func? Elapsed;
67 |
68 | ///
69 | /// NtpTimerを初期化します。
70 | ///
71 | public SecondBasedTimer()
72 | {
73 | Stopwatch = new Stopwatch();
74 | InnerTimer = new Timer(s =>
75 | {
76 | if (Stopwatch.Elapsed - LastTime >= Interval)
77 | {
78 | if (!BlockingMode || !IsEventRunning)
79 | ThreadPool.QueueUserWorkItem(s2 =>
80 | {
81 | IsEventRunning = true;
82 | Elapsed?.Invoke(CurrentTime)?.Wait();
83 | IsEventRunning = false;
84 | });
85 | //かなり時間のズレが大きければその分修正する
86 | if (Stopwatch.Elapsed.Ticks - LastTime.Ticks >= Interval.Ticks * 2)
87 | {
88 | var skipCount = (Stopwatch.Elapsed.Ticks - LastTime.Ticks) / Interval.Ticks;
89 | LastTime += TimeSpan.FromTicks(Interval.Ticks * skipCount);
90 | CurrentTime = CurrentTime.AddSeconds(skipCount);
91 | }
92 | else //そうでなかったばあい普通に修正
93 | {
94 | LastTime += Interval;
95 | CurrentTime = CurrentTime.AddSeconds(1);
96 | }
97 | if (!AutoReset)
98 | InnerTimer?.Change(Timeout.Infinite, Timeout.Infinite);
99 | }
100 | }, null, Timeout.Infinite, Timeout.Infinite);
101 | }
102 |
103 | ///
104 | /// 時間を更新します。
105 | ///
106 | /// 書き込む時間
107 | public void UpdateTime(DateTime time)
108 | {
109 | LastUpdatedTime = time;
110 | CurrentTime = LastUpdatedTime.AddMilliseconds(-LastUpdatedTime.Millisecond).AddSeconds(-Math.Floor(Offset.TotalSeconds));
111 | LastTime = Stopwatch.Elapsed - TimeSpan.FromMilliseconds(LastUpdatedTime.Millisecond);
112 | LastTime += Offset - TimeSpan.FromSeconds(Offset.Seconds);
113 | }
114 |
115 | ///
116 | /// タイマーを開始します。
117 | ///
118 | /// 現在時間
119 | public void Start(DateTime currentTime)
120 | {
121 | IsEventRunning = false;
122 | Stopwatch.Start();
123 | UpdateTime(currentTime);
124 | InnerTimer.Change(Accuracy, Accuracy);
125 | }
126 |
127 | ///
128 | /// タイマーを停止します。
129 | ///
130 | public void Stop()
131 | => InnerTimer.Change(Timeout.Infinite, Timeout.Infinite);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/AppApi.cs:
--------------------------------------------------------------------------------
1 | using KyoshinMonitorLib.ApiResult.AppApi;
2 | using KyoshinMonitorLib.UrlGenerator;
3 | using System;
4 | using System.Collections.Concurrent;
5 | using System.Collections.Generic;
6 | using System.Runtime.CompilerServices;
7 | using System.Threading.Tasks;
8 |
9 | namespace KyoshinMonitorLib
10 | {
11 | ///
12 | /// スマホアプリAPI
13 | ///
14 | public class AppApi : Api
15 | {
16 | ///
17 | /// 観測点情報のキャッシュ
18 | /// BaseSerialNoと観測点情報・idxに適合した情報のマッピング
19 | ///
20 | private static IDictionary SiteListCache { get; set; } = new ConcurrentDictionary();
21 |
22 | private ObservationPoint[]? ObservationPoints { get; }
23 | ///
24 | /// スマホアプリAPIを初期化します。
25 | ///
26 | /// 結合時に使用する観測点情報
27 | public AppApi(ObservationPoint[]? observationPoints = null)
28 | {
29 | ObservationPoints = observationPoints;
30 | }
31 |
32 | ///
33 | /// 観測点一覧を取得します。
34 | ///
35 | public virtual Task> GetSiteList(string baseSerialNo)
36 | => GetJsonObject(AppApiUrlGenerator.Generate(baseSerialNo), ApiResult.ApiResultJsonContext.Default.SiteList);
37 |
38 | ///
39 | /// リアルタイムなデータを取得します。
40 | ///
41 | public virtual Task> GetRealtimeData(DateTime time, RealtimeDataType dataType, bool isBehore = false)
42 | => GetJsonObject(AppApiUrlGenerator.Generate(AppApiUrlType.RealtimeData, time, dataType, isBehore), ApiResult.ApiResultJsonContext.Default.RealtimeData);
43 |
44 | ///
45 | /// 観測点情報と結合済みのリアルタイムなデータを取得します。
46 | ///
47 | public async Task> GetLinkedRealtimeData(DateTime time, RealtimeDataType dataType, bool isBehore = false)
48 | {
49 | var dataResult = await GetRealtimeData(time, dataType, isBehore);
50 | if (dataResult.Data is not RealtimeData data ||
51 | data.BaseSerialNo is null ||
52 | data.Items is null)
53 | return new(dataResult.StatusCode, null);
54 |
55 |
56 | var pair = await GetOrLinkObservationPoint(data.BaseSerialNo);
57 | var result = new List();
58 | for (var i = 0; i < data.Items.Length; i++)
59 | {
60 | if (pair.Length <= i)
61 | throw new KyoshinMonitorException("リアルタイムデータの結合に失敗しました。 SiteListの観測点が少なすぎます。");
62 | result.Add(new(pair[i], data.Items[i]));
63 | }
64 | return new(dataResult.StatusCode, result.ToArray());
65 | }
66 | ///
67 | /// 観測点情報を結合もしくはキャッシュから取得します。
68 | ///
69 | /// 観測点一覧の番号
70 | /// 結合された観測点情報 親となるリストが存在しない場合null
71 | public async Task GetOrLinkObservationPoint(string serialNo)
72 | {
73 | if (SiteListCache.TryGetValue(serialNo, out var pair))
74 | return pair;
75 |
76 | var siteListResult = await GetSiteList(serialNo);
77 | if (siteListResult.Data is null
78 | || siteListResult.Data.Sites is null)
79 | throw new KyoshinMonitorException("SiteListの取得に失敗しました。");
80 |
81 | var pairList = new List();
82 |
83 | for (var i = 0; i < siteListResult.Data.Sites.Length; i++)
84 | {
85 | var site = siteListResult.Data.Sites[i];
86 | if (i < site.Siteidx)
87 | {
88 | pairList.Add(new(site, null));
89 | continue;
90 | }
91 | if (i != site.Siteidx)
92 | throw new KyoshinMonitorException("リアルタイムデータの結合に失敗しました。 idxが一致しません。");
93 |
94 | ObservationPoint? point = null;
95 | if (ObservationPoints != null)
96 | for (int j = 0; j < ObservationPoints.Length; j++)
97 | {
98 | var p = ObservationPoints[j];
99 | if (/*p.IsSuspended || */p.Location == null) continue;
100 | if (CheckNearLocation(p.Location, site))
101 | {
102 | point = p;
103 | break;
104 | }
105 | if (p.OldLocation == null) continue;
106 | if (CheckNearLocation(p.OldLocation, site))
107 | {
108 | point = p;
109 | break;
110 | }
111 | }
112 |
113 | pairList.Add(new(site, point));
114 | }
115 | pair = pairList.ToArray();
116 | SiteListCache.Add(serialNo, pair);
117 | return pair;
118 | }
119 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
120 | private static bool CheckNearLocation(Location l, Site s)
121 | => s.Lat is float lat && s.Lng is float lng &&
122 | Math.Abs(Math.Floor(l.Latitude * 1000) / 1000 - lat) <= 0.01 && Math.Abs(Math.Floor(l.Longitude * 1000) / 1000 - lng) <= 0.01;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/NtpAssistance.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Net;
4 | using System.Net.Http;
5 | using System.Net.Sockets;
6 | using System.Reflection;
7 | using System.Text.RegularExpressions;
8 | using System.Threading.Tasks;
9 |
10 | namespace KyoshinMonitorLib
11 | {
12 | ///
13 | /// Ntpによる時刻取得を補助するクラス
14 | ///
15 | public static class NtpAssistance
16 | {
17 | static readonly Regex TimeRegex = new("[^0-9]*(\\d+\\.\\d+)+.*", RegexOptions.Compiled);
18 |
19 | ///
20 | /// HTTPを使用してネットワーク上から時刻を取得します。
21 | /// このAPIは起動したアセンブリ名・バージョンをUserAgentにセットしてリクエストを送信します。
22 | /// 送信されたくない場合、HttpClientを指定するメソッドを使用してください。
23 | ///
24 | /// 要求するURL POSIX Timeが生で返されるURLである必要があります。
25 | /// タイムアウト時間(ミリ秒)
26 | /// 取得された時刻 取得に失敗した場合はnullが返されます。
27 | public static async Task GetNetworkTimeWithHttp(string url = "https://svs.ingen084.net/time/", double timeout = 1000)
28 | {
29 | try
30 | {
31 | using var client = new HttpClient() { Timeout = TimeSpan.FromMilliseconds(timeout) };
32 | var execAsm = Assembly.GetEntryAssembly() ?? typeof(NtpAssistance).Assembly;
33 | var asmName = execAsm.GetName();
34 | client.DefaultRequestHeaders.TryAddWithoutValidation("UserAgent", $"{asmName.Name ?? "unknown"}-{asmName.Version?.ToString() ?? "unknown"}");
35 | var match = TimeRegex.Match(await client.GetStringAsync(url));
36 | return new DateTime(1970, 1, 1, 9, 0, 0).AddSeconds(double.Parse(match.Groups[1].Value));
37 | }
38 | catch (Exception ex)
39 | {
40 | Debug.WriteLine("GetNetworkTimeWithHttpAsync Error: " + ex);
41 | return null;
42 | }
43 | }
44 |
45 | ///
46 | /// HTTP通信を使用してネットワーク上から時刻を取得します。
47 | ///
48 | /// リクエストに使用するHttpClient
49 | /// 要求するURL POSIX Timeが生で返されるURLである必要があります。
50 | /// 取得された時刻 取得に失敗した場合はnullが返されます。
51 | public static async Task GetNetworkTimeWithHttp(HttpClient client, string url = "https://svs.ingen084.net/time/")
52 | {
53 | try
54 | {
55 | var match = TimeRegex.Match(await client.GetStringAsync(url));
56 | return new DateTime(1970, 1, 1, 9, 0, 0).AddSeconds(double.Parse(match.Groups[1].Value));
57 | }
58 | catch (Exception ex)
59 | {
60 | Debug.WriteLine("GetNetworkTimeWithHttpAsync Error: " + ex);
61 | return null;
62 | }
63 | }
64 |
65 | ///
66 | /// NTPを使用してネットワーク上から時刻を取得します。
67 | ///
68 | /// ホスト名
69 | /// ポート番号 通常はデフォルトのままで構いません。
70 | /// タイムアウト時間(ミリ秒)
71 | /// 取得された時刻 取得に失敗した場合はnullが返されます。
72 | public static async Task GetNetworkTimeWithNtp(string hostName = "ntp.nict.jp", ushort port = 123, int timeout = 100)
73 | {
74 | // RFC 2030準拠
75 | var ntpData = new byte[48];
76 |
77 | //特に使用しません
78 | ntpData[0] = 0b00_100_011;//うるう秒指定子 = 0 (警告なし), バージョン = 4 (SNTP), Mode = 3 (クライアント)
79 |
80 | DateTime sendedTime, recivedTime;
81 | sendedTime = recivedTime = DateTime.Now;
82 |
83 | await Task.Run(() =>
84 | {
85 | if (!IPAddress.TryParse(hostName, out var addr))
86 | {
87 | var addresses = Dns.GetHostEntry(hostName).AddressList;
88 | addr = addresses[new Random().Next(addresses.Length)];
89 | }
90 |
91 | var endPoint = new IPEndPoint(addr, port);
92 | using var socket = new Socket(endPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
93 | socket.Connect(endPoint);
94 | socket.ReceiveTimeout = timeout;
95 |
96 | socket.Send(ntpData);
97 | sendedTime = DateTime.Now;
98 |
99 | socket.Receive(ntpData);
100 | recivedTime = DateTime.Now;
101 | });
102 |
103 | //受信時刻=32 送信時刻=40
104 | var serverReceivedTime = ToTime(ntpData, 32);
105 | var serverSendedTime = ToTime(ntpData, 40);
106 |
107 | // (送信から受信までの時間 - 鯖側での受信から送信までの時間) / 2
108 | var delta = TimeSpan.FromTicks((recivedTime.Ticks - sendedTime.Ticks - (serverSendedTime.Ticks - serverReceivedTime.Ticks)) / 2);
109 | Debug.WriteLine("theta:" + delta);
110 | return serverSendedTime + delta;
111 | }
112 |
113 | private static DateTime ToTime(byte[] bytes, ushort offset)
114 | {
115 | ulong intPart = SwapEndianness(BitConverter.ToUInt32(bytes, offset));
116 | ulong fractPart = SwapEndianness(BitConverter.ToUInt32(bytes, offset + 4));
117 |
118 | var milliseconds = (intPart * 1000) + (fractPart * 1000 / 0x100000000L);
119 |
120 | //時間生成
121 | return new DateTime(1900, 1, 1, 9, 0, 0).AddMilliseconds((long)milliseconds);
122 | }
123 |
124 | //ビット列を逆にする stackoverflow.com/a/3294698/162671
125 | internal static uint SwapEndianness(ulong x)
126 | {
127 | return (uint)(((x & 0x000000ff) << 24) +
128 | ((x & 0x0000ff00) << 8) +
129 | ((x & 0x00ff0000) >> 8) +
130 | ((x & 0xff000000) >> 24));
131 | }
132 | }
133 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/Point2.cs:
--------------------------------------------------------------------------------
1 | using MessagePack;
2 | using System.Drawing;
3 | using System.Runtime.Serialization;
4 |
5 | namespace KyoshinMonitorLib
6 | {
7 | ///
8 | /// シリアライズ+四則演算をできるようにしたPointクラス
9 | ///
10 | [MessagePackObject, DataContract]
11 | public struct Point2
12 | {
13 | ///
14 | /// Point2を初期化します。
15 | ///
16 | /// X
17 | /// Y
18 | public Point2(int x, int y)
19 | {
20 | X = x;
21 | Y = y;
22 | }
23 |
24 | ///
25 | /// X座標
26 | ///
27 | [Key(0), DataMember(Order = 0)]
28 | public int X { get; set; }
29 |
30 | ///
31 | /// Y座標
32 | ///
33 | [Key(1), DataMember(Order = 1)]
34 | public int Y { get; set; }
35 |
36 | ///
37 | /// 文字にします。
38 | ///
39 | /// 文字
40 | public override string ToString()
41 | => $"X:{X} Y:{Y}";
42 |
43 | ///
44 | /// 整数とPoint2を足します。
45 | ///
46 | ///
47 | ///
48 | ///
49 | public static Point2 operator +(Point2 point, int num)
50 | => new Point2(point.X + num, point.Y + num);
51 |
52 | ///
53 | /// Point2から整数を引きます。
54 | ///
55 | ///
56 | ///
57 | ///
58 | public static Point2 operator -(Point2 point, int num)
59 | => new Point2(point.X - num, point.Y - num);
60 |
61 | ///
62 | /// Point2同士を足します。
63 | ///
64 | ///
65 | ///
66 | ///
67 | public static Point2 operator +(Point2 point1, Point2 point2)
68 | => new Point2(point1.X + point2.X, point1.Y + point2.Y);
69 |
70 | ///
71 | /// Point2同士を引きます。
72 | ///
73 | ///
74 | ///
75 | ///
76 | public static Point2 operator -(Point2 point1, Point2 point2)
77 | => new Point2(point1.X - point2.X, point1.Y - point2.Y);
78 |
79 | ///
80 | /// Point2を整数で掛けます。
81 | ///
82 | ///
83 | ///
84 | ///
85 | public static Point2 operator *(Point2 point, int num)
86 | => new Point2(point.X * num, point.Y * num);
87 |
88 | ///
89 | /// Point2を整数で割ります。
90 | ///
91 | ///
92 | ///
93 | ///
94 | public static Point2 operator /(Point2 point, int num)
95 | => new Point2(point.X / num, point.Y / num);
96 |
97 | ///
98 | /// Point2同士を掛けます。
99 | ///
100 | ///
101 | ///
102 | ///
103 | public static Point2 operator *(Point2 point1, Point2 point2)
104 | => new Point2(point1.X * point2.X, point1.Y * point2.Y);
105 |
106 | ///
107 | /// Point2同士を割ります。
108 | ///
109 | ///
110 | ///
111 | ///
112 | public static Point2 operator /(Point2 point1, Point2 point2)
113 | => new Point2(point1.X / point2.X, point1.Y / point2.Y);
114 |
115 | ///
116 | /// 2つのPoint2が同じ値かどうかを判断します。
117 | ///
118 | ///
119 | ///
120 | ///
121 | public static bool operator ==(Point2 point1, Point2 point2)
122 | => point1.X == point2.X && point1.Y == point2.Y;
123 |
124 | ///
125 | /// 2つのPoint2が違う値かどうかを判断します。
126 | ///
127 | ///
128 | ///
129 | ///
130 | public static bool operator !=(Point2 point1, Point2 point2)
131 | => point1.X != point2.X || point1.Y != point2.Y;
132 |
133 | ///
134 | /// Eq
135 | ///
136 | ///
137 | ///
138 | public override bool Equals(object? obj)
139 | => base.Equals(obj);
140 |
141 | ///
142 | /// ハッシュコードを取得します。
143 | ///
144 | ///
145 | public override int GetHashCode()
146 | => base.GetHashCode();
147 |
148 | ///
149 | /// PointからPoint2への自動キャスト
150 | ///
151 | ///
152 | public static implicit operator Point2(Point point)
153 | => new Point2(point.X, point.Y);
154 |
155 | ///
156 | /// Point2からPointへの自動キャスト
157 | ///
158 | ///
159 | public static implicit operator Point(Point2 point)
160 | => new Point(point.X, point.Y);
161 |
162 | ///
163 | /// SizeからPoint2への自動キャスト
164 | ///
165 | ///
166 | public static implicit operator Point2(Size point)
167 | => new Point2(point.Width, point.Height);
168 |
169 | ///
170 | /// Point2からSizeへの自動キャスト
171 | ///
172 | ///
173 | public static implicit operator Size(Point2 point)
174 | => new Size(point.X, point.Y);
175 | }
176 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/ApiResult/WebApi/Eew.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Text.Json;
4 | using System.Text.Json.Serialization;
5 |
6 | namespace KyoshinMonitorLib.ApiResult.WebApi
7 | {
8 | ///
9 | /// Web版APIでの緊急地震速報の情報
10 | ///
11 | public class Eew
12 | {
13 | ///
14 | /// リザルト
15 | ///
16 | [JsonPropertyName("result")]
17 | public Result? Result { get; set; }
18 | ///
19 | /// 発報時間
20 | ///
21 | [JsonPropertyName("report_time")]
22 | public string? ReportTimeString { get; set; }
23 | ///
24 | /// 発報時間
25 | ///
26 | [JsonIgnore]
27 | public DateTime? ReportTime => DateTime.TryParse(ReportTimeString, out var time) ? time : null;
28 | ///
29 | /// 地域コード
30 | ///
31 | [JsonPropertyName("region_code")]
32 | public string? RegionCode { get; set; }
33 | ///
34 | /// リクエスト時間
35 | ///
36 | [JsonPropertyName("request_time")]
37 | public string? RequestTime { get; set; }
38 | ///
39 | /// 地域名
40 | ///
41 | [JsonPropertyName("region_name")]
42 | public string? RegionName { get; set; }
43 | ///
44 | /// 経度(デシリアライズ用)
45 | ///
46 |
47 | [JsonPropertyName("longitude")]
48 | public string? LongitudeString { get; set; }
49 | ///
50 | /// 経度
51 | ///
52 | [JsonIgnore]
53 | public float? Longitude => LongitudeString != null && float.TryParse(LongitudeString, out var lon) ? lon : null;
54 | ///
55 | /// キャンセル報か(デシリアライズ用)
56 | ///
57 | [JsonPropertyName("is_cancel")]
58 | public JsonElement IsCancelRaw { get; set; }
59 | ///
60 | /// キャンセル報か
61 | ///
62 | [JsonIgnore]
63 | public bool? IsCancel => IsCancelRaw.ToBoolFromStringableBool();
64 | ///
65 | /// 震源の深さ(デシリアライズ用)
66 | ///
67 | [JsonPropertyName("depth")]
68 | public string? DepthString { get; set; }
69 | ///
70 | /// 震源の深さ
71 | ///
72 | [JsonIgnore]
73 | public int? Depth => int.TryParse(DepthString?.Replace("km", ""), out var depth) ? depth : null;
74 | ///
75 | /// 予想最大震度(デシリアライズ用)
76 | ///
77 | [JsonPropertyName("calcintensity")]
78 | public string? CalcintensityString { get; set; }
79 | ///
80 | /// 予想最大震度
81 | ///
82 | [JsonIgnore]
83 | public JmaIntensity? Calcintensity => CalcintensityString?.ToJmaIntensity() ?? JmaIntensity.Unknown;
84 | ///
85 | /// 最終報か(デシリアライズ用)
86 | ///
87 | [JsonPropertyName("is_final")]
88 | public JsonElement IsFinalRaw { get; set; }
89 | ///
90 | /// 最終報か
91 | ///
92 | [JsonIgnore]
93 | public bool? IsFinal => IsFinalRaw.ToBoolFromStringableBool();
94 | ///
95 | /// 訓練報か(デシリアライズ用)
96 | ///
97 | [JsonPropertyName("isTraining")]
98 | public JsonElement IsTrainingRaw { get; set; }
99 | ///
100 | /// 訓練報か
101 | ///
102 | public bool? IsTraining => IsTrainingRaw.ToBoolFromStringableBool();
103 | ///
104 | /// 緯度(デシリアライズ用)
105 | ///
106 | [JsonPropertyName("latitude")]
107 | public string? LatitudeString { get; set; }
108 | ///
109 | /// 緯度
110 | ///
111 | [JsonIgnore]
112 | public float? Latitude => LatitudeString != null && float.TryParse(LatitudeString, out var lat) ? lat : null;
113 | ///
114 | /// 震源の座標
115 | ///
116 | [JsonIgnore]
117 | public Location? Location => Latitude is float lat && Longitude is float lng ? new Location(lat, lng) : null;
118 | ///
119 | /// 発生時間(デシリアライズ用)
120 | ///
121 | [JsonPropertyName("origin_time")]
122 | public string? OriginTimeString { get; set; }
123 | ///
124 | /// 発生時間
125 | ///
126 | [JsonIgnore]
127 | public DateTime? OriginTime => DateTime.TryParseExact(OriginTimeString, "yyyyMMddHHmmss", null, DateTimeStyles.None, out var time) ? time : null;
128 | ///
129 | /// セキュリティ情報
130 | ///
131 | [JsonPropertyName("security")]
132 | public Security? Security { get; set; }
133 | ///
134 | /// マグニチュード(デシリアライズ用)
135 | ///
136 | [JsonPropertyName("magunitude")]
137 | public string? MagunitudeString { get; set; }
138 | ///
139 | /// マグニチュード
140 | ///
141 | [JsonIgnore]
142 | public float? Magunitude => float.TryParse(MagunitudeString, out var val) ? val : null;
143 | ///
144 | /// 発報番号(デシリアライズ用)
145 | ///
146 | [JsonPropertyName("report_num")]
147 | public string? ReportNumString { get; set; }
148 | ///
149 | /// 発報番号
150 | ///
151 | [JsonIgnore]
152 | public int? ReportNum => int.TryParse(ReportNumString, out var val) ? val : null;
153 | ///
154 | /// なにこれ?
155 | ///
156 | [JsonPropertyName("request_hypo_type")]
157 | public string? RequestHypoType { get; set; }
158 | ///
159 | /// 地震ID
160 | ///
161 | [JsonPropertyName("report_id")]
162 | public string? ReportId { get; set; }
163 | ///
164 | /// 警報 or 予報
165 | ///
166 | [JsonPropertyName("alertflg")]
167 | public string? AlertFlag { get; set; }
168 | ///
169 | /// 警報か
170 | ///
171 | [JsonIgnore]
172 | public bool IsAlert => AlertFlag == "警報";
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.SkiaImages/Extensions.cs:
--------------------------------------------------------------------------------
1 | using KyoshinMonitorLib.UrlGenerator;
2 | using SkiaSharp;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Linq;
7 | using System.Threading.Tasks;
8 |
9 | namespace KyoshinMonitorLib.SkiaImages
10 | {
11 | ///
12 | /// 拡張メソッドたち
13 | ///
14 | public static class Extensions
15 | {
16 | ///
17 | /// 与えられた情報から強震モニタの画像を取得し、そこから観測点情報を使用しスケールを取得します。
18 | /// asyncなのはStream取得部分のみなので注意してください。
19 | ///
20 | /// WebApiインスタンス
21 | /// 使用する観測点情報の配列
22 | /// 参照する日付
23 | /// 取得する情報の種類
24 | /// 地中の情報を取得するかどうか
25 | /// 震度情報が追加された観測点情報の配列 取得に失敗した場合null
26 | public static async Task?>> ParseScaleFromParameterAsync(this WebApi webApi, IEnumerable points, DateTime datetime, RealtimeDataType dataType = RealtimeDataType.Shindo, bool isBehole = false)
27 | => await webApi.ParseScaleFromParameterAsync(points.Select(p => new ImageAnalysisResult(p)).ToArray(), datetime, dataType, isBehole);
28 |
29 | ///
30 | /// 与えられた情報から強震モニタの画像を取得し、そこから観測点情報を使用しスケールを取得します。
31 | /// asyncなのはStream取得部分のみなので注意してください。
32 | ///
33 | /// WebApiインスタンス
34 | /// 使用する観測点情報の配列
35 | /// 参照する日付
36 | /// 取得する情報の種類
37 | /// 地中の情報を取得するかどうか
38 | /// 震度情報が追加された観測点情報の配列 取得に失敗した場合null
39 | public static async Task?>> ParseScaleFromParameterAsync(this WebApi webApi, IEnumerable points, DateTime datetime, RealtimeDataType dataType = RealtimeDataType.Shindo, bool isBehole = false)
40 | {
41 | var imageResult = await webApi.GetRealtimeImageData(datetime, dataType, isBehole);
42 | if (imageResult.Data is not byte[] data)
43 | return new(imageResult.StatusCode, null);
44 |
45 | using var bitmap = SKBitmap.Decode(data);
46 | return new(imageResult.StatusCode, points.ParseScaleFromImage(bitmap));
47 | }
48 |
49 | ///
50 | /// 与えられた情報から強震モニタの画像を取得し、そこから観測点情報を使用しスケールを取得します。
51 | /// asyncなのはStream取得部分のみなので注意してください。
52 | ///
53 | /// WebApiインスタンス
54 | /// 使用する観測点情報の配列
55 | /// 参照する日付
56 | /// 取得する情報の種類
57 | /// 地中の情報を取得するかどうか
58 | /// 震度情報が追加された観測点情報の配列 取得に失敗した場合null
59 | public static async Task?>> ParseScaleFromParameterAsync(this LpgmWebApi webApi, IEnumerable points, DateTime datetime, RealtimeDataType dataType = RealtimeDataType.Shindo, bool isBehole = false)
60 | => await webApi.ParseScaleFromParameterAsync(points.Select(p => new ImageAnalysisResult(p)).ToArray(), datetime, dataType, isBehole);
61 |
62 | ///
63 | /// 与えられた情報から強震モニタの画像を取得し、そこから観測点情報を使用しスケールを取得します。
64 | /// asyncなのはStream取得部分のみなので注意してください。
65 | ///
66 | /// WebApiインスタンス
67 | /// 使用する観測点情報の配列
68 | /// 参照する日付
69 | /// 取得する情報の種類
70 | /// 地中の情報を取得するかどうか
71 | /// 震度情報が追加された観測点情報の配列 取得に失敗した場合null
72 | public static async Task?>> ParseScaleFromParameterAsync(this LpgmWebApi webApi, IEnumerable points, DateTime datetime, RealtimeDataType dataType = RealtimeDataType.Shindo, bool isBehole = false)
73 | {
74 | var imageResult = await webApi.GetRealtimeImageData(datetime, dataType, isBehole);
75 | if (imageResult.Data is not byte[] data)
76 | return new(imageResult.StatusCode, null);
77 |
78 | using var bitmap = SKBitmap.Decode(data);
79 | return new(imageResult.StatusCode, points.ParseScaleFromImage(bitmap));
80 | }
81 |
82 | ///
83 | /// 与えられた画像から観測点情報を使用しスケールを取得します。
84 | ///
85 | /// 使用する観測点情報の配列
86 | /// 参照する画像
87 | /// 震度情報が追加された観測点情報の配列
88 | public static IEnumerable ParseScaleFromImage(this IEnumerable points, SKBitmap bitmap)
89 | {
90 |
91 | //var pixelData = bitmap.GetPixelSpan();
92 | foreach (var point in points)
93 | {
94 | if (point.ObservationPoint.Point == null || point.ObservationPoint.IsSuspended)
95 | {
96 | point.AnalysisResult = null;
97 | continue;
98 | }
99 |
100 | try
101 | {
102 | var color = bitmap.GetPixel(point.ObservationPoint.Point.Value.X, point.ObservationPoint.Point.Value.Y);
103 | // [(bitmap.Width * point.ObservationPoint.Point.Value.Y + point.ObservationPoint.Point.Value.X) * 4];
104 | point.Color = color;
105 | if (color.Alpha != 255)
106 | {
107 | point.AnalysisResult = null;
108 | continue;
109 | }
110 |
111 | point.AnalysisResult = ColorConverter.ConvertToScaleAtPolynomialInterpolation(color);
112 | }
113 | catch (Exception ex)
114 | {
115 | point.AnalysisResult = null;
116 | Debug.WriteLine("parseEx: " + ex.ToString());
117 | }
118 | }
119 | return points;
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
262 |
263 | # Debug PointsData
264 | /src/Tests/ShindoObsPoints.*
265 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.Images/Extensions.cs:
--------------------------------------------------------------------------------
1 | using KyoshinMonitorLib.UrlGenerator;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Drawing;
6 | using System.Drawing.Imaging;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Threading.Tasks;
10 |
11 | namespace KyoshinMonitorLib.Images
12 | {
13 | ///
14 | /// 拡張メソッドたち
15 | ///
16 | public static class Extensions
17 | {
18 | ///
19 | /// 与えられた情報から強震モニタの画像を取得し、そこから観測点情報を使用しスケールを取得します。
20 | /// asyncなのはStream取得部分のみなので注意してください。
21 | ///
22 | /// WebApiインスタンス
23 | /// 使用する観測点情報の配列
24 | /// 参照する日付
25 | /// 取得する情報の種類
26 | /// 地中の情報を取得するかどうか
27 | /// 震度情報が追加された観測点情報の配列 取得に失敗した場合null
28 | public static async Task?>> ParseScaleFromParameterAsync(this WebApi webApi, IEnumerable points, DateTime datetime, RealtimeDataType dataType = RealtimeDataType.Shindo, bool isBehole = false)
29 | => await webApi.ParseScaleFromParameterAsync(points.Select(p => new ImageAnalysisResult(p)).ToArray(), datetime, dataType, isBehole);
30 |
31 | ///
32 | /// 与えられた情報から強震モニタの画像を取得し、そこから観測点情報を使用しスケールを取得します。
33 | /// asyncなのはStream取得部分のみなので注意してください。
34 | ///
35 | /// WebApiインスタンス
36 | /// 使用する観測点情報の配列
37 | /// 参照する日付
38 | /// 取得する情報の種類
39 | /// 地中の情報を取得するかどうか
40 | /// 震度情報が追加された観測点情報の配列 取得に失敗した場合null
41 | public static async Task?>> ParseScaleFromParameterAsync(this WebApi webApi, IEnumerable points, DateTime datetime, RealtimeDataType dataType = RealtimeDataType.Shindo, bool isBehole = false)
42 | {
43 | var imageResult = await webApi.GetRealtimeImageData(datetime, dataType, isBehole);
44 | if (imageResult.Data == null)
45 | return new(imageResult.StatusCode, null);
46 |
47 | using var stream = new MemoryStream(imageResult.Data);
48 | using var bitmap = new Bitmap(stream);
49 | return new(imageResult.StatusCode, points.ParseScaleFromImage(bitmap));
50 | }
51 |
52 | ///
53 | /// 与えられた情報から強震モニタの画像を取得し、そこから観測点情報を使用しスケールを取得します。
54 | /// asyncなのはStream取得部分のみなので注意してください。
55 | ///
56 | /// WebApiインスタンス
57 | /// 使用する観測点情報の配列
58 | /// 参照する日付
59 | /// 取得する情報の種類
60 | /// 地中の情報を取得するかどうか
61 | /// 震度情報が追加された観測点情報の配列 取得に失敗した場合null
62 | public static async Task?>> ParseScaleFromParameterAsync(this LpgmWebApi webApi, IEnumerable points, DateTime datetime, RealtimeDataType dataType = RealtimeDataType.Shindo, bool isBehole = false)
63 | => await webApi.ParseScaleFromParameterAsync(points.Select(p => new ImageAnalysisResult(p)).ToArray(), datetime, dataType, isBehole);
64 |
65 | ///
66 | /// 与えられた情報から強震モニタの画像を取得し、そこから観測点情報を使用しスケールを取得します。
67 | /// asyncなのはStream取得部分のみなので注意してください。
68 | ///
69 | /// WebApiインスタンス
70 | /// 使用する観測点情報の配列
71 | /// 参照する日付
72 | /// 取得する情報の種類
73 | /// 地中の情報を取得するかどうか
74 | /// 震度情報が追加された観測点情報の配列 取得に失敗した場合null
75 | public static async Task?>> ParseScaleFromParameterAsync(this LpgmWebApi webApi, IEnumerable points, DateTime datetime, RealtimeDataType dataType = RealtimeDataType.Shindo, bool isBehole = false)
76 | {
77 | var imageResult = await webApi.GetRealtimeImageData(datetime, dataType, isBehole);
78 | if (imageResult.Data == null)
79 | return new(imageResult.StatusCode, null);
80 |
81 | using var stream = new MemoryStream(imageResult.Data);
82 | using var bitmap = new Bitmap(stream);
83 | return new(imageResult.StatusCode, points.ParseScaleFromImage(bitmap));
84 | }
85 |
86 | ///
87 | /// 与えられた画像から観測点情報を使用しスケールを取得します。
88 | ///
89 | /// 使用する観測点情報の配列
90 | /// 参照する画像
91 | /// 震度情報が追加された観測点情報の配列
92 | public static IEnumerable ParseScaleFromImage(this IEnumerable points, Bitmap bitmap)
93 | {
94 | var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
95 | Span pixelData;
96 | unsafe
97 | {
98 | pixelData = new Span(data.Scan0.ToPointer(), bitmap.Width * bitmap.Height);
99 | }
100 | foreach (var point in points)
101 | {
102 | if (point.ObservationPoint.Point == null || point.ObservationPoint.IsSuspended)
103 | {
104 | point.AnalysisResult = null;
105 | continue;
106 | }
107 |
108 | try
109 | {
110 | var color = bitmap.Palette.Entries[pixelData[bitmap.Width * point.ObservationPoint.Point.Value.Y + point.ObservationPoint.Point.Value.X]];
111 | point.Color = color;
112 | if (color.A != 255)
113 | {
114 | point.AnalysisResult = null;
115 | continue;
116 | }
117 |
118 | point.AnalysisResult = ColorConverter.ConvertToScaleAtPolynomialInterpolation(color);
119 | }
120 | catch (Exception ex)
121 | {
122 | point.AnalysisResult = null;
123 | Debug.WriteLine("parseEx: " + ex.ToString());
124 | }
125 | }
126 | return points;
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/JmaIntensity.cs:
--------------------------------------------------------------------------------
1 | namespace KyoshinMonitorLib
2 | {
3 | ///
4 | /// 気象庁震度階級 +α
5 | ///
6 | public enum JmaIntensity
7 | {
8 | ///
9 | /// 震度不明
10 | ///
11 | Unknown,
12 |
13 | ///
14 | /// 震度1未満
15 | ///
16 | Int0,
17 |
18 | ///
19 | /// 震度1
20 | ///
21 | Int1,
22 |
23 | ///
24 | /// 震度2
25 | ///
26 | Int2,
27 |
28 | ///
29 | /// 震度3
30 | ///
31 | Int3,
32 |
33 | ///
34 | /// 震度4
35 | ///
36 | Int4,
37 |
38 | ///
39 | /// 震度5弱
40 | ///
41 | Int5Lower,
42 |
43 | ///
44 | /// 震度5強
45 | ///
46 | Int5Upper,
47 |
48 | ///
49 | /// 震度6弱
50 | ///
51 | Int6Lower,
52 |
53 | ///
54 | /// 震度6強
55 | ///
56 | Int6Upper,
57 |
58 | ///
59 | /// 震度7
60 | ///
61 | Int7,
62 |
63 | ///
64 | /// 震度異常
65 | ///
66 | Error,
67 | }
68 |
69 | ///
70 | /// JmaIntensityの拡張メソッド
71 | ///
72 | public static class JmaIntensityExtensions
73 | {
74 | ///
75 | /// 生の震度の値を気象庁震度階級に変換します。(float?版)
76 | ///
77 | /// 変換対象の震度
78 | /// 変換されたShindo
79 | public static JmaIntensity ToJmaIntensity(this float? intensity)
80 | {
81 | if (intensity == null)
82 | return JmaIntensity.Unknown;
83 | return ((double)intensity.Value).ToJmaIntensity();
84 | }
85 |
86 | ///
87 | /// 生の震度の値を気象庁震度階級に変換します。(double?版)
88 | ///
89 | /// 変換対象の震度
90 | /// 変換されたShindo
91 | public static JmaIntensity ToJmaIntensity(this double? intensity)
92 | {
93 | if (intensity == null)
94 | return JmaIntensity.Unknown;
95 | return ((double)intensity.Value).ToJmaIntensity();
96 | }
97 |
98 | ///
99 | /// 生の震度の値を気象庁震度階級に変換します。(double版)
100 | ///
101 | /// 変換対象の震度
102 | /// 変換されたShindo
103 | public static JmaIntensity ToJmaIntensity(this double intensity)
104 | {
105 | if (intensity < 0.5)
106 | return JmaIntensity.Int0;
107 | if (intensity < 1.5)
108 | return JmaIntensity.Int1;
109 | if (intensity < 2.5)
110 | return JmaIntensity.Int2;
111 | if (intensity < 3.5)
112 | return JmaIntensity.Int3;
113 | if (intensity < 4.5)
114 | return JmaIntensity.Int4;
115 | if (intensity < 5.0)
116 | return JmaIntensity.Int5Lower;
117 | if (intensity < 5.5)
118 | return JmaIntensity.Int5Upper;
119 | if (intensity < 6.0)
120 | return JmaIntensity.Int6Lower;
121 | return intensity < 6.5 ? JmaIntensity.Int6Upper : JmaIntensity.Int7;
122 | }
123 |
124 | ///
125 | /// 気象庁震度階級を短い形式の文字列に変換します。
126 | ///
127 | /// 変換するShindo
128 | /// 変換された文字列
129 | public static string ToShortString(this JmaIntensity shindo)
130 | {
131 | switch (shindo)
132 | {
133 | case JmaIntensity.Error:
134 | return "*";
135 |
136 | case JmaIntensity.Int0:
137 | return "0";
138 |
139 | case JmaIntensity.Int1:
140 | return "1";
141 |
142 | case JmaIntensity.Int2:
143 | return "2";
144 |
145 | case JmaIntensity.Int3:
146 | return "3";
147 |
148 | case JmaIntensity.Int4:
149 | return "4";
150 |
151 | case JmaIntensity.Int5Lower:
152 | return "5-";
153 |
154 | case JmaIntensity.Int5Upper:
155 | return "5+";
156 |
157 | case JmaIntensity.Int6Lower:
158 | return "6-";
159 |
160 | case JmaIntensity.Int6Upper:
161 | return "6+";
162 |
163 | case JmaIntensity.Int7:
164 | return "7";
165 |
166 | default:
167 | return "-";
168 | }
169 | }
170 |
171 | ///
172 | /// 気象庁震度階級を長い形式の文字列に変換します。
173 | ///
174 | /// 変換するShindo
175 | /// 変換された文字列
176 | public static string ToLongString(this JmaIntensity shindo)
177 | {
178 | switch (shindo)
179 | {
180 | case JmaIntensity.Error:
181 | return "震度異常";
182 |
183 | case JmaIntensity.Int0:
184 | return "震度0";
185 |
186 | case JmaIntensity.Int1:
187 | return "震度1";
188 |
189 | case JmaIntensity.Int2:
190 | return "震度2";
191 |
192 | case JmaIntensity.Int3:
193 | return "震度3";
194 |
195 | case JmaIntensity.Int4:
196 | return "震度4";
197 |
198 | case JmaIntensity.Int5Lower:
199 | return "震度5弱";
200 |
201 | case JmaIntensity.Int5Upper:
202 | return "震度5強";
203 |
204 | case JmaIntensity.Int6Lower:
205 | return "震度6弱";
206 |
207 | case JmaIntensity.Int6Upper:
208 | return "震度6強";
209 |
210 | case JmaIntensity.Int7:
211 | return "震度7";
212 |
213 | default:
214 | return "震度不明";
215 | }
216 | }
217 |
218 | ///
219 | /// 文字から気象庁震度階級に変換します。
220 | /// EewJsonぐらいでしか確認していません。
221 | ///
222 | /// 変換する文字列
223 | /// 変換されたShindo
224 | public static JmaIntensity ToJmaIntensity(this string source)
225 | {
226 | if (string.IsNullOrWhiteSpace(source))
227 | return JmaIntensity.Unknown;
228 | source = source.Replace("震度", "");
229 | switch (source)
230 | {
231 | case "0":
232 | case "0":
233 | return JmaIntensity.Int0;
234 |
235 | case "1":
236 | case "1":
237 | return JmaIntensity.Int1;
238 |
239 | case "2":
240 | case "2":
241 | return JmaIntensity.Int2;
242 |
243 | case "3":
244 | case "3":
245 | return JmaIntensity.Int3;
246 |
247 | case "4":
248 | case "4":
249 | return JmaIntensity.Int4;
250 |
251 | case "5-":
252 | case "5弱":
253 | case "5弱":
254 | return JmaIntensity.Int5Lower;
255 |
256 | case "5+":
257 | case "5強":
258 | case "5強":
259 | return JmaIntensity.Int5Upper;
260 |
261 | case "6-":
262 | case "6弱":
263 | case "6弱":
264 | return JmaIntensity.Int6Lower;
265 |
266 | case "6+":
267 | case "6強":
268 | case "6強":
269 | return JmaIntensity.Int6Upper;
270 |
271 | case "7":
272 | case "7":
273 | return JmaIntensity.Int7;
274 |
275 | case "-1":
276 | case "異常":
277 | return JmaIntensity.Error;
278 | }
279 | return JmaIntensity.Unknown;
280 | }
281 | }
282 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # KyoshinMonitorLib
2 | .NETから簡単に強震モニタを利用できるようにするライブラリ
3 |
4 | 主に個人用に開発したライブラリです。
5 | 強震モニタを使用したソフトを開発する際に毎回クラスや処理をコピーするのが面倒なので作成しました。
6 |
7 | # 更新情報
8 | ## 0.4.1.0
9 | ### 変更
10 |
11 | - 画像解析時に実際の値とずれてしまう問題を修正しました。
12 |
13 | ## 0.4.0.0
14 | ### 変更
15 |
16 | - 画像解析周りの機能を一新しました!
17 | - 解析アルゴリズムは [こちらの記事(JQuake)](https://qiita.com/NoneType1/items/a4d2cf932e20b56ca444) のものを使用しています。
18 |
19 | ## 過去の更新情報
20 |
21 | - [0.3.x台](https://github.com/ingen084/KyoshinMonitorLib/blob/f635df256afc1a8b772f932818ab6276fe884202/README.md)
22 | - [0.1.x台](https://github.com/ingen084/KyoshinMonitorLib/blob/e581e49192417d9b65a5403681b8507073c66349/README.md)
23 |
24 | # リファレンス
25 | バージョン:`0.4.0.0`
26 | 主要なクラスのみ解説します。詳細な解説はソースなどを参照してください。
27 | また、気象庁震度階級や地球の緯度経度など、小学生レベルの前提知識が必要なものがあります。
28 |
29 | ## KyoshinMonitorExceptionクラス
30 | 強震モニタのAPIから情報を取得している間に、タイムアウトやレスポンスの異常などが確認された場合に発生する例外です。
31 | ### プロパティ
32 | | 型 | 名前 | 解説 |
33 | |---|---|---|
34 | |`string`|Message|どのような例外が発生したか|
35 | |`Exception`|InnerException|内部で発生した例外|
36 |
37 | ## ApiResultクラス
38 | APIなどを呼んだ際の結果が含まれています。
39 | ### プロパティ
40 | | 型 | 名前 | 解説 |
41 | |---|---|---|
42 | |`HttpStatusCode`|StatusCode|HTTPステータスコード|
43 | |`TResult`(ジェネリック)|Data|APIの結果 リクエストに失敗した場合`null`の可能性もあります。|
44 |
45 | ## ObservationPointクラス
46 | [KyoshinShindoPlaceEditor](https://github.com/ingen084/KyoshinShindoPlaceEditor)と互換があります。
47 | ### LoadFromMpk/Json
48 | ```c#
49 | public static ObservationPoint[] LoadFromMpk(string path, bool usingLz4 = false);
50 | public static ObservationPoint[] LoadFromJson(string path);
51 | ```
52 | 観測点情報をmpk/jsonから読み込みます。失敗した場合は例外がスローされます。
53 | **lz4圧縮済みのmpkを通常のmpkとして読み込まないように注意してください。**
54 |
55 | ### LoadFromCsv
56 | ```c#
57 | public static (ObservationPoint[] points, uint success, uint error) LoadFromCsv(string path, Encoding encoding = null);
58 | ```
59 | 観測点情報をcsvから読み込みます。失敗した場合は例外がスローされます。
60 |
61 | ### SaveToCsv/Mpk/Json
62 | ```c#
63 | public static void SaveToCsv(string path, IEnumerable points);
64 | public static void SaveToMpk(string path, IEnumerable points, bool useLz4 = false);
65 | public static void SaveToJson(string path, IEnumerable points);
66 | ```
67 | 拡張メソッド版
68 | ```c#
69 | public static void SaveToCsv(this IEnumerable points, string path);
70 | public static void SaveToMpk(this IEnumerable points, string path, bool useLz4 = false);
71 | public static void SaveToJson(this IEnumerable points, string path);
72 | ```
73 | 観測点情報を各形式に保存します。失敗した場合は例外がスローされます。
74 |
75 | ## Apiクラス共通
76 | `WebApi`/`AppApi`共通で利用できます。
77 | ### プロパティ
78 | | 型 | 名前 | 解説 |
79 | |---|---|---|
80 | |`TimeSpan`|Timeout|APIを呼ぶにあたってのタイムアウト時間|
81 |
82 | ## WebApiクラス
83 | Webで見ることができる強震モニタのAPIを使用してEEWなどの画像やデータを取得するためのクラスです。
84 | ### メソッド
85 | | 返り値の型 | 名前(引数) | 解説 |
86 | |---|---|---|
87 | |`Task>`|GetEewInfo(`DateTime` time)|緊急地震速報のJsonを取得します。 EewクラスはJsonをそのままパースしたものです。 |
88 | |`Task>`|GetRealtimeImageData(`DateTime` time, `RealtimeDataType` dataType, `bool` isBehore = false)|リアルタイムな情報(リアルタイム・震度・加速度など)の画像のbyte配列を取得します。 画像解析まで行いたい場合は下記の拡張メソッドをご利用ください。|
89 | |`Task>`|GetEstShindoImageData(`DateTime` time)|緊急地震速報の予想震度の画像のbyte配列を取得します。|
90 | |`Task>`|GetPSWaveImageData(`DateTime` time)|緊急地震速報のP波とS波の広がりを示す円の画像のbyte配列を取得します。|
91 |
92 | ### KyoshinMonitorLib.Imagesによる拡張メソッド
93 | | 返り値の型 | 名前(引数) | 解説 |
94 | |---|---|---|
95 | |`Task>>`|ParseScaleFromParameterAsync(this `WebApi` webApi, `IEnumerable` points, `DateTime` datetime, `RealtimeDataType` dataType = RealtimeDataType.Shindo, `bool` isBehole = false)|ObservationPointのコレクションを使用して新強震モニタの画像を取得し、解析します。|
96 |
97 | 他にもありますが割愛させていただきます。
98 |
99 | #### 画像から震度を解析するにあたってのメモ
100 |
101 | `ImageAnalysisResult.AnalysisResult` は強震モニタ上のスケール(0~1)が返されます。
102 | 解析する画像の種類に応じて `GetResultToIntensity` `GetResultToPga` `GetResultToPgv` `GetResultToPgd` を使い分けてください。
103 |
104 | ## AppApiクラス
105 | スマートフォンアプリケーションのAPIを使用してリアルタイム震度などのデータを取得します。
106 | **ほとんどのAPIが現在利用できません。** いつか復活を願って処理は残しておきます…。
107 | ### メソッド
108 | | 返り値の型 | 名前(引数) | 解説 |
109 | |---|---|---|
110 | |`Task>`|GetLinkedRealtimeData(`DateTime` time, `RealtimeDataType` dataType, `bool` isBehore = false)|リアルタイムデータを取得します。 自動で観測点情報などと結びつけ、インスタンスを返します。|
111 | |`Task>`|GetRealtimeData(`DateTime` time, `RealtimeDataType` dataType, `bool` isBehore = false)|リアルタイムデータを取得します。 特に理由がない限り`GetLinkedRealtimeData`を使用することを推奨します。|
112 | |`Task>`|GetSiteList(`string` baseSerialNo)|APIから参照できる観測点情報の一覧を取得します。 特に理由がない限り`GetLinkedRealtimeData`を使用することを推奨します。|
113 | |`Task>`|GetEewHypoInfo(`DateTime` time)|**[利用不可]** APIから緊急地震速報の情報を取得します。 **ちなみに、複数のEEWに対応してそうです…(要検証)**|
114 | |`Task>`|GetPSWave(`DateTime` time)|**[利用不可]** 緊急地震速報から算出された揺れの広がりを取得します。 **こちらも複数のEEWに対応してそうです。**|
115 | |`Task>`|GetEstShindo(`DateTime` time)|**[利用不可]** 緊急地震速報から算出された予想震度の5kmメッシュ情報を取得します。|
116 | |`Task>`|GetMeshes()|**[利用不可]** メッシュ一覧を取得します。 非常に時間がかかるため、起動時などに行い、別ファイルとしてキャッシュしておくことを推奨します。|
117 |
118 | ### 重要事項
119 | - `GetEewHypoInfo`
120 | - `GetPSWave`
121 | - `GetEstShindo`
122 |
123 | **この3つのAPIはEEWが発表されていない場合は404が帰ってきます。**
124 |
125 | ## Meshクラス
126 | 5kmメッシュ情報を取り扱います。
127 | ### プロパティ
128 | | 型 | 名前(引数) | 解説 |
129 | |---|---|---|
130 | |`string`|Code|メッシュのコード 詳細不明|
131 | |`Location`|LocationLeftTop|右上(北西)の緯度経度|
132 | |`Location`|LocationRightBottom|左下(南東)の緯度経度|
133 |
134 | ### 備考
135 | 使用方法の詳細は省略しますが、 `GetEstShindo` の返り値を見ればわかると思います。
136 | ですが需要があれば書くかもしれません。またお知らせください。
137 |
138 |
139 | ## UrlGeneratorクラス群
140 | UrlGeneratorは分離した上に、各種Apiクラスでラップしているため、解説は省略させていただきます。
141 |
142 | ## SecondBasedTimerクラス
143 | FixedTimerに時刻管理機能をつけたものです。
144 | 強震モニタの取得タイマーとしてしか考慮していないので必ず1秒になります。
145 |
146 | ### 注意
147 | 時間の更新(補正)は自動でされないため、別途タイマーなどで実行してください。
148 |
149 | ### サンプル
150 | ```c#
151 | //タイマーのインスタンスを作成(デフォルトは精度1ms↓)
152 | var timer = new SecondBasedTimer()
153 | {
154 | Offset = TimeSpan.FromSeconds(2.5), //イベントの発火時間を2500ms *後ろに* ずらす。だいたいこれ前後がおすすめ。
155 | };
156 | //適当にイベント設定
157 | timer.Elapsed += time =>
158 | {
159 | //timeに時間が入っているのでそれを使用して取得する
160 | };
161 | //タイマー開始 引数には現在の時刻が必要です。
162 | timer.Start(await NtpAssistance.GetNetworkTimeWithNtp() ?? throw new Exception());
163 |
164 | // 時刻の補正(別のタイマーとかで回すといいと思います)
165 | //timer.UpdateTime(await NtpAssistance.GetNetworkTimeWithNtp() ?? throw new Exception());
166 |
167 | //改行入力待ち
168 | Console.ReadLine();
169 | //タイマーストップ
170 | timer.Stop();
171 | ```
172 |
173 | ## NtpAssistance
174 | NTPから簡単に時刻取得をするクラスです。
175 |
176 | ### メソッド
177 | | 返り値の型 | 名前(引数) | 解説 |
178 | |---|---|---|
179 | |`Task`|GetNetworkTimeWithNtp(`string` hostName = "ntp.nict.jp", `ushort` port = 123, `int` timeout = 100)|SNTP通信を使用してネットワーク上から時刻を取得します。 一応SNTPを実装していますが、NICT以外のNTPサーバーでの挙動は保証しません。|
180 | |`Task`|GetNetworkTimeWithHttp(`string` url = "https://ntp-a1.nict.go.jp/cgi-bin/jst", `double` timeout = 1000)|Http通信を使用してネットワーク上から時刻を取得します。 小数のPOSIX Timeを含んだレスポンスが返されるURLであればなんでも使用できるとおもいます。|
181 |
182 | ## JmaIntensity
183 | 気象庁震度階級を示す列挙型です。震度異常などを扱うために値が増やされています。
184 |
185 | ### サンプル
186 | ```c#
187 | JmaIntensity shindo = 1.0f.ToJmaIntensity(); //JmaIntensity.Int1
188 | Console.WriteLine(shindo.ToShortString()); //1
189 | Console.WriteLine(shondo.ToLongString()); //震度1
190 |
191 | Console.WriteLine("5+".ToJmaIntensity().ToLongString()); //文字からも解析できます。 出力:震度5強
192 |
193 | float? invalidIntensity = null;
194 | Console.WriteLine(invalidIntensity.ToJmaIntensity()); //nullableなfloatもできます。 出力:JmaIntensity.Unknown
195 | ```
196 |
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/ObservationPoint.cs:
--------------------------------------------------------------------------------
1 | using MessagePack;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Runtime.Serialization;
7 | using System.Text;
8 | using System.Text.Json;
9 |
10 | namespace KyoshinMonitorLib
11 | {
12 | ///
13 | /// NIEDの観測点情報
14 | ///
15 | [MessagePackObject, DataContract]
16 | public class ObservationPoint : IComparable
17 | {
18 | ///
19 | /// 観測点情報をmpkから読み込みます。失敗した場合は例外がスローされます。
20 | ///
21 | /// 読み込むmpkファイルのパス
22 | /// lz4で圧縮させるかどうか(させる場合は拡張子を.mpk.lz4にすることをおすすめします)
23 | /// 読み込まれた観測点情報
24 | public static ObservationPoint[] LoadFromMpk(string path, bool useLz4 = false)
25 | {
26 | using var stream = new FileStream(path, FileMode.Open);
27 | return MessagePackSerializer.Deserialize(stream, options: useLz4 ? MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block) : null);
28 | }
29 |
30 | ///
31 | /// 観測点情報をmpk形式で保存します。失敗した場合は例外がスローされます。
32 | ///
33 | /// 書き込むmpkファイルのパス
34 | /// 書き込む観測点情報の配列
35 | /// lz4で圧縮させるかどうか(させる場合は拡張子を.mpk.lz4にすることをおすすめします)
36 | public static void SaveToMpk(string path, IEnumerable points, bool useLz4 = false)
37 | {
38 | using var stream = new FileStream(path, FileMode.Create);
39 | MessagePackSerializer.Serialize(stream, points.ToArray(), options: useLz4 ? MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block) : null);
40 | }
41 |
42 | ///
43 | /// 観測点情報をJsonから読み込みます。失敗した場合は例外がスローされます。
44 | ///
45 | /// 読み込むJsonファイルのパス
46 | /// 読み込まれた観測点情報
47 | public static ObservationPoint[]? LoadFromJson(string path)
48 | => JsonSerializer.Deserialize(File.ReadAllText(path));
49 |
50 | ///
51 | /// 観測点情報をJson形式で保存します。失敗した場合は例外がスローされます。
52 | ///
53 | /// 書き込むJsonファイルのパス
54 | /// 書き込む観測点情報の配列
55 | public static void SaveToJson(string path, IEnumerable points)
56 | {
57 | using var stream = new FileStream(path, FileMode.Create);
58 | var data = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(points.ToArray()));
59 | stream.Write(data, 0, data.Length);
60 | }
61 |
62 | ///
63 | /// 観測点情報をcsvから読み込みます。失敗した場合は例外がスローされます。
64 | ///
65 | /// 読み込むcsvファイルのパス
66 | /// 読み込むファイル文字コード 何も指定していない場合はUTF8が使用されます。
67 | /// list:読み込まれた観測点情報 success:読み込みに成功した項目のカウント error:読み込みに失敗した項目のカウント
68 | public static (ObservationPoint[] points, uint success, uint error) LoadFromCsv(string path, Encoding? encoding = null)
69 | {
70 | var addedCount = 0u;
71 | var errorCount = 0u;
72 |
73 | var points = new List();
74 |
75 | using var stream = File.OpenRead(path);
76 | using var reader = new StreamReader(stream, encoding ?? Encoding.UTF8);
77 | while (reader.Peek() >= 0)
78 | try
79 | {
80 | if (reader.ReadLine()?.Split(',') is not string[] strings ||
81 | strings.Length < 7)
82 | {
83 | errorCount++;
84 | continue;
85 | }
86 |
87 | var point = new ObservationPoint()
88 | {
89 | Type = (ObservationPointType)int.Parse(strings[0]),
90 | Code = strings[1],
91 | IsSuspended = bool.Parse(strings[2]),
92 | Name = strings[3],
93 | Region = strings[4],
94 | Location = new Location(float.Parse(strings[5]), float.Parse(strings[6])),
95 | Point = null
96 | };
97 | if (!string.IsNullOrWhiteSpace(strings[7]) && !string.IsNullOrWhiteSpace(strings[8]))
98 | point.Point = new Point2(int.Parse(strings[7]), int.Parse(strings[8]));
99 | if (strings.Length >= 11)
100 | {
101 | point.ClassificationId = int.Parse(strings[9]);
102 | point.PrefectureClassificationId = int.Parse(strings[10]);
103 | }
104 | if (strings.Length >= 13)
105 | point.OldLocation = new Location(float.Parse(strings[11]), float.Parse(strings[12]));
106 |
107 | points.Add(point);
108 | addedCount++;
109 | }
110 | catch
111 | {
112 | errorCount++;
113 | }
114 |
115 | return (points.ToArray(), addedCount, errorCount);
116 | }
117 |
118 | ///
119 | /// 観測点情報をcsvに保存します。失敗した場合は例外がスローされます。
120 | ///
121 | /// 書き込むcsvファイルのパス
122 | /// 書き込む観測点情報の配列
123 | public static void SaveToCsv(string path, IEnumerable points)
124 | {
125 | using var stream = File.OpenWrite(path);
126 | using var writer = new StreamWriter(stream);
127 | foreach (var point in points)
128 | writer.WriteLine($"{(int)point.Type},{point.Code},{point.IsSuspended},{point.Name},{point.Region},{point.Location.Latitude},{point.Location.Longitude},{point.Point?.X.ToString() ?? ""},{point.Point?.Y.ToString() ?? ""},{point.ClassificationId?.ToString() ?? ""},{point.PrefectureClassificationId?.ToString() ?? ""},{point.OldLocation.Latitude},{point.OldLocation.Longitude}");
129 | }
130 |
131 | // シリアライザ用コンストラクタのため警告を無効化する
132 | #nullable disable
133 | ///
134 | /// ObservationPointを初期化します。
135 | ///
136 | public ObservationPoint()
137 | {
138 | }
139 | #nullable restore
140 |
141 | ///
142 | /// 観測地点のネットワークの種類
143 | ///
144 | [Key(0), DataMember(Order = 0)]
145 | public ObservationPointType Type { get; set; }
146 |
147 | ///
148 | /// 観測点コード
149 | ///
150 | [Key(1), DataMember(Order = 1)]
151 | public string Code { get; set; }
152 |
153 | ///
154 | /// 観測点名
155 | ///
156 | [Key(2), DataMember(Order = 2)]
157 | public string Name { get; set; }
158 |
159 | ///
160 | /// 観測点広域名
161 | ///
162 | [Key(3), DataMember(Order = 3)]
163 | public string Region { get; set; }
164 |
165 | ///
166 | /// 観測地点が休止状態(無効)かどうか
167 | ///
168 | [Key(4), DataMember(Order = 4)]
169 | public bool IsSuspended { get; set; }
170 |
171 | ///
172 | /// 地理座標
173 | ///
174 | [Key(5), DataMember(Order = 5)]
175 | public Location Location { get; set; }
176 |
177 | ///
178 | /// 地理座標(日本座標系)
179 | ///
180 | [Key(9), DataMember(Order = 9)]
181 | public Location OldLocation { get; set; }
182 |
183 | ///
184 | /// 強震モニタ画像上での座標
185 | ///
186 | [Key(6), DataMember(Order = 6)]
187 | public Point2? Point { get; set; }
188 |
189 | ///
190 | /// 緊急地震速報や震度速報で用いる区域のID(EqWatchインポート用)
191 | ///
192 | [Key(7), DataMember(Order = 7)]
193 | public int? ClassificationId { get; set; }
194 |
195 | ///
196 | /// 緊急地震速報で用いる府県予報区のID(EqWatchインポート用)
197 | ///
198 | [Key(8), DataMember(Order = 8)]
199 | public int? PrefectureClassificationId { get; set; }
200 |
201 | ///
202 | /// ObservationPoint同士を比較します。
203 | ///
204 | /// 比較対象のObservationPoint
205 | ///
206 | public int CompareTo(object? obj)
207 | {
208 | if (obj is not ObservationPoint ins)
209 | throw new ArgumentException("比較対象はObservationPointにキャストできる型でなければなりません。");
210 | return Code.CompareTo(ins.Code);
211 | }
212 | }
213 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib.Images/ColorConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 |
5 | namespace KyoshinMonitorLib.Images
6 | {
7 | ///
8 | /// 色を震度に変換する
9 | ///
10 | public static class ColorConverter
11 |
12 | {
13 | //memo せっかくなのでコード内に定義
14 | private static readonly Dictionary _table = new Dictionary()
15 | {
16 | {Color.FromArgb(255, 0, 0, 205), -3f},
17 | {Color.FromArgb(255, 0, 7, 209), -2.9f},
18 | {Color.FromArgb(255, 0, 14, 214), -2.8f},
19 | {Color.FromArgb(255, 0, 21, 218), -2.7f},
20 | {Color.FromArgb(255, 0, 28, 223), -2.6f},
21 | {Color.FromArgb(255, 0, 36, 227), -2.5f},
22 | {Color.FromArgb(255, 0, 43, 231), -2.4f},
23 | {Color.FromArgb(255, 0, 50, 236), -2.3f},
24 | {Color.FromArgb(255, 0, 57, 240), -2.2f},
25 | {Color.FromArgb(255, 0, 64, 245), -2.1f},
26 | {Color.FromArgb(255, 0, 72, 250), -2f},
27 | {Color.FromArgb(255, 0, 85, 238), -1.9f},
28 | {Color.FromArgb(255, 0, 99, 227), -1.8f},
29 | {Color.FromArgb(255, 0, 112, 216), -1.7f},
30 | {Color.FromArgb(255, 0, 126, 205), -1.6f},
31 | {Color.FromArgb(255, 0, 140, 194), -1.5f},
32 | {Color.FromArgb(255, 0, 153, 183), -1.4f},
33 | {Color.FromArgb(255, 0, 167, 172), -1.3f},
34 | {Color.FromArgb(255, 0, 180, 161), -1.2f},
35 | {Color.FromArgb(255, 0, 194, 150), -1.1f},
36 | {Color.FromArgb(255, 0, 208, 139), -1f},
37 | {Color.FromArgb(255, 6, 212, 130), -0.9f},
38 | {Color.FromArgb(255, 12, 216, 121), -0.8f},
39 | {Color.FromArgb(255, 18, 220, 113), -0.7f},
40 | {Color.FromArgb(255, 25, 224, 104), -0.6f},
41 | {Color.FromArgb(255, 31, 228, 96), -0.5f},
42 | {Color.FromArgb(255, 37, 233, 88), -0.4f},
43 | {Color.FromArgb(255, 44, 237, 79), -0.3f},
44 | {Color.FromArgb(255, 50, 241, 71), -0.2f},
45 | {Color.FromArgb(255, 56, 245, 62), -0.1f},
46 | {Color.FromArgb(255, 63, 250, 54), 0f},
47 | {Color.FromArgb(255, 75, 250, 49), 0.1f},
48 | {Color.FromArgb(255, 88, 250, 45), 0.2f},
49 | {Color.FromArgb(255, 100, 251, 41), 0.3f},
50 | {Color.FromArgb(255, 113, 251, 37), 0.4f},
51 | {Color.FromArgb(255, 125, 252, 33), 0.5f},
52 | {Color.FromArgb(255, 138, 252, 28), 0.6f},
53 | {Color.FromArgb(255, 151, 253, 24), 0.7f},
54 | {Color.FromArgb(255, 163, 253, 20), 0.8f},
55 | {Color.FromArgb(255, 176, 254, 16), 0.9f},
56 | {Color.FromArgb(255, 189, 255, 12), 1f},
57 | {Color.FromArgb(255, 195, 254, 10), 1.1f},
58 | {Color.FromArgb(255, 202, 254, 9), 1.2f},
59 | {Color.FromArgb(255, 208, 254, 8), 1.3f},
60 | {Color.FromArgb(255, 215, 254, 7), 1.4f},
61 | {Color.FromArgb(255, 222, 255, 5), 1.5f},
62 | {Color.FromArgb(255, 228, 254, 4), 1.6f},
63 | {Color.FromArgb(255, 235, 255, 3), 1.7f},
64 | {Color.FromArgb(255, 241, 254, 2), 1.8f},
65 | {Color.FromArgb(255, 248, 255, 1), 1.9f},
66 | {Color.FromArgb(255, 255, 255, 0), 2f},
67 | {Color.FromArgb(255, 254, 251, 0), 2.1f},
68 | {Color.FromArgb(255, 254, 248, 0), 2.2f},
69 | {Color.FromArgb(255, 254, 244, 0), 2.3f},
70 | {Color.FromArgb(255, 254, 241, 0), 2.4f},
71 | {Color.FromArgb(255, 255, 238, 0), 2.5f},
72 | {Color.FromArgb(255, 254, 234, 0), 2.6f},
73 | {Color.FromArgb(255, 255, 231, 0), 2.7f},
74 | {Color.FromArgb(255, 254, 227, 0), 2.8f},
75 | {Color.FromArgb(255, 255, 224, 0), 2.9f},
76 | {Color.FromArgb(255, 255, 221, 0), 3f},
77 | {Color.FromArgb(255, 254, 213, 0), 3.1f},
78 | {Color.FromArgb(255, 254, 205, 0), 3.2f},
79 | {Color.FromArgb(255, 254, 197, 0), 3.3f},
80 | {Color.FromArgb(255, 254, 190, 0), 3.4f},
81 | {Color.FromArgb(255, 255, 182, 0), 3.5f},
82 | {Color.FromArgb(255, 254, 174, 0), 3.6f},
83 | {Color.FromArgb(255, 255, 167, 0), 3.7f},
84 | {Color.FromArgb(255, 254, 159, 0), 3.8f},
85 | {Color.FromArgb(255, 255, 151, 0), 3.9f},
86 | {Color.FromArgb(255, 255, 144, 0), 4f},
87 | {Color.FromArgb(255, 254, 136, 0), 4.1f},
88 | {Color.FromArgb(255, 254, 128, 0), 4.2f},
89 | {Color.FromArgb(255, 254, 121, 0), 4.3f},
90 | {Color.FromArgb(255, 254, 113, 0), 4.4f},
91 | {Color.FromArgb(255, 255, 106, 0), 4.5f},
92 | {Color.FromArgb(255, 254, 98, 0), 4.6f},
93 | {Color.FromArgb(255, 255, 90, 0), 4.7f},
94 | {Color.FromArgb(255, 254, 83, 0), 4.8f},
95 | {Color.FromArgb(255, 255, 75, 0), 4.9f},
96 | {Color.FromArgb(255, 255, 68, 0), 5f},
97 | {Color.FromArgb(255, 254, 61, 0), 5.1f},
98 | {Color.FromArgb(255, 253, 54, 0), 5.2f},
99 | {Color.FromArgb(255, 252, 47, 0), 5.3f},
100 | {Color.FromArgb(255, 251, 40, 0), 5.4f},
101 | {Color.FromArgb(255, 250, 33, 0), 5.5f},
102 | {Color.FromArgb(255, 249, 27, 0), 5.6f},
103 | {Color.FromArgb(255, 248, 20, 0), 5.7f},
104 | {Color.FromArgb(255, 247, 13, 0), 5.8f},
105 | {Color.FromArgb(255, 246, 6, 0), 5.9f},
106 | {Color.FromArgb(255, 245, 0, 0), 6f},
107 | {Color.FromArgb(255, 238, 0, 0), 6.1f},
108 | {Color.FromArgb(255, 230, 0, 0), 6.2f},
109 | {Color.FromArgb(255, 223, 0, 0), 6.3f},
110 | {Color.FromArgb(255, 215, 0, 0), 6.4f},
111 | {Color.FromArgb(255, 208, 0, 0), 6.5f},
112 | {Color.FromArgb(255, 200, 0, 0), 6.6f},
113 | {Color.FromArgb(255, 192, 0, 0), 6.7f},
114 | {Color.FromArgb(255, 185, 0, 0), 6.8f},
115 | {Color.FromArgb(255, 177, 0, 0), 6.9f},
116 | {Color.FromArgb(255, 170, 0, 0), 7.0f},
117 | };
118 |
119 | ///
120 | /// 強震モニタの画像の色から震度に変換します。
121 | ///
122 | /// 変換する色
123 | /// 変換された震度
124 | public static float? ConvertToIntensityAtTable(Color color)
125 | {
126 | if (!_table.TryGetValue(color, out var value))
127 | return null;
128 | return value;
129 | }
130 |
131 | ///
132 | /// スケールを震度に変換します。
133 | ///
134 | /// 変換前のスケール
135 | ///
136 | public static double ConvertToIntensityFromScale(double scale)
137 | => scale * 10 - 3;
138 |
139 | ///
140 | /// スケールをPGA(最大加速度)に変換します。
141 | ///
142 | /// 変換前のスケール
143 | ///
144 | public static double ConvertToPgaFromScale(double scale)
145 | => Math.Pow(10, 5 * scale - 2);
146 |
147 | ///
148 | /// スケールをPGV(最大速度)に変換します。
149 | ///
150 | /// 変換前のスケール
151 | ///
152 | public static double ConvertToPgvFromScale(double scale)
153 | => Math.Pow(10, 5 * scale - 3);
154 |
155 | ///
156 | /// スケールをPGD(最大変位)に変換します。
157 | ///
158 | /// 変換前のスケール
159 | ///
160 | public static double ConvertToPgdFromScale(double scale)
161 | => Math.Pow(10, 5 * scale - 4);
162 |
163 | ///
164 | /// 多項式補完を使用して色をスケールに変換します。
165 | /// from: https://qiita.com/NoneType1/items/a4d2cf932e20b56ca444
166 | ///
167 | /// 変換元の色
168 | /// 変換後のスケール
169 | public static double ConvertToScaleAtPolynomialInterpolation(Color color)
170 | => Math.Max(0, ConvertToScaleAtPolynomialInterpolationInternal(color));
171 | private static double ConvertToScaleAtPolynomialInterpolationInternal(Color color)
172 | {
173 | // Input : color in hsv space
174 | (var h, var s, var v) = GetHsv(color);
175 | h /= 360;
176 |
177 | // Check if the color belongs to the scale
178 | if (v <= 0.1 || s <= 0.75)
179 | return 0;
180 |
181 | if (h > 0.1476)
182 | return 280.31 * Math.Pow(h, 6) - 916.05 * Math.Pow(h, 5) + 1142.6 * Math.Pow(h, 4) - 709.95 * Math.Pow(h, 3) + 234.65 * Math.Pow(h, 2) - 40.27 * h + 3.2217;
183 | else if (h > 0.001)
184 | return 151.4 * Math.Pow(h, 4) - 49.32 * Math.Pow(h, 3) + 6.753 * Math.Pow(h, 2) - 2.481 * h + 0.9033;
185 | else
186 | return -0.005171 * Math.Pow(v, 2) - 0.3282 * v + 1.2236;
187 | }
188 |
189 | ///
190 | /// 指定した色をHSVで返す
191 | ///
192 | /// 変換する色
193 | /// 変換した色
194 | private static (double h, double s, double v) GetHsv(Color rgb)
195 | {
196 | var max = Math.Max(rgb.R, Math.Max(rgb.G, rgb.B));
197 | var min = Math.Min(rgb.R, Math.Min(rgb.G, rgb.B));
198 |
199 | if (min == max)
200 | return (0, 0, max / 255d);
201 | double w = max - min;
202 | var h = 0d;
203 | if (rgb.R == max)
204 | h = (rgb.G - rgb.B) / w;
205 | if (rgb.G == max)
206 | h = ((rgb.B - rgb.R) / w) + 2;
207 | if (rgb.B == max)
208 | h = ((rgb.R - rgb.G) / w) + 4;
209 | if ((h *= 60) < 0)
210 | h += 360;
211 | return (h, (double)(max - min) / max, max / 255d);
212 | }
213 | }
214 | }
--------------------------------------------------------------------------------
/src/KyoshinMonitorLib/ApiResult/AppApi/SiteList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json.Serialization;
3 | using System.Reflection;
4 |
5 | namespace KyoshinMonitorLib.ApiResult.AppApi
6 | {
7 | ///
8 | /// 観測点一覧
9 | ///
10 | public class SiteList
11 | {
12 | ///
13 | /// 観測点一覧
14 | ///
15 | [JsonPropertyName("items")]
16 | public Site[]? Sites { get; set; }
17 | ///
18 | /// セキュリティ情報
19 | ///
20 | [JsonPropertyName("security")]
21 | public Security? Security { get; set; }
22 | ///
23 | /// 時間
24 | ///
25 | [JsonPropertyName("dataTime")]
26 | public string? DataTime { get; set; }
27 | ///
28 | /// リザルト
29 | ///
30 | [JsonPropertyName("result")]
31 | public Result? Result { get; set; }
32 | ///
33 | /// シリアル番号
34 | ///
35 | [JsonPropertyName("serialNo")]
36 | public string? SerialNo { get; set; }
37 | }
38 |
39 | ///
40 | /// APIの観測点
41 | ///
42 | public class Site
43 | {
44 | ///
45 | /// 不明(内部ID?)
46 | ///
47 | [JsonPropertyName("muni")]
48 | public int? Muni { get; set; }
49 | ///
50 | /// RealtimeDataでのインデックス
51 | ///
52 | [JsonPropertyName("siteidx")]
53 | public int? Siteidx { get; set; }
54 | ///
55 | /// 都道府県ID
56 | ///
57 | [JsonPropertyName("pref")]
58 | public int? PrefefectureId { get; set; }
59 | ///
60 | /// 都道府県
61 | ///
62 | [JsonIgnore]
63 | public Prefecture? Prefefecture => PrefefectureId is null ? Prefecture.Unknown : (Prefecture)(Enum.ToObject(typeof(Prefecture), PrefefectureId) ?? Prefecture.Unknown);
64 | ///
65 | /// ID
66 | ///
67 | [JsonPropertyName("siteid")]
68 | public string? SiteId { get; set; }
69 | ///
70 | /// 緯度
71 | ///
72 | [JsonPropertyName("lat")]
73 | public float? Lat { get; set; }
74 | ///
75 | /// 経度
76 | ///
77 | [JsonPropertyName("lng")]
78 | public float? Lng { get; set; }
79 | }
80 |
81 | ///
82 | /// 都道府県の名前
83 | ///
84 | [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field)]
85 | public sealed class PrefectureNameAttribute : Attribute
86 | {
87 | ///
88 | /// 初期化
89 | ///
90 | ///
91 | ///
92 | public PrefectureNameAttribute(string shortName, string longName)
93 | {
94 | ShortName = shortName;
95 | LongName = longName;
96 | }
97 |
98 | ///
99 | /// 短縮名
100 | ///
101 | public string ShortName { get; }
102 | ///
103 | /// 正式名称
104 | ///
105 | public string LongName { get; }
106 | }
107 |
108 | ///
109 | /// 都道府県
110 | ///
111 | public enum Prefecture
112 | {
113 | ///
114 | /// その他
115 | ///
116 | [PrefectureName("他", "その他")]
117 | Other,
118 | ///
119 | /// 北海道
120 | ///
121 | [PrefectureName("北海道", "北海道")]
122 | Hokkaido,
123 | ///
124 | /// 青森県
125 | ///
126 | [PrefectureName("青森", "青森県")]
127 | Aomori,
128 | ///
129 | /// 岩手県
130 | ///
131 | [PrefectureName("岩手", "岩手県")]
132 | Iwate,
133 | ///
134 | /// 宮城県
135 | ///
136 | [PrefectureName("宮城", "宮城県")]
137 | Miyagi,
138 | ///
139 | /// 秋田県
140 | ///
141 | [PrefectureName("秋田", "秋田県")]
142 | Akita,
143 | ///
144 | /// 山形県
145 | ///
146 | [PrefectureName("山形", "山形県")]
147 | Yamagata,
148 | ///
149 | /// 福島県
150 | ///
151 | [PrefectureName("福島", "福島県")]
152 | Fukushima,
153 | ///
154 | /// 茨城県
155 | ///
156 | [PrefectureName("茨城", "茨城県")]
157 | Ibaraki,
158 | ///
159 | /// 栃木県
160 | ///
161 | [PrefectureName("栃木", "栃木県")]
162 | Tochigi,
163 | ///
164 | /// 群馬県
165 | ///
166 | [PrefectureName("群馬", "群馬県")]
167 | Gunma,
168 | ///
169 | /// 埼玉県
170 | ///
171 | [PrefectureName("埼玉", "埼玉県")]
172 | Saitama,
173 | ///
174 | /// 千葉県
175 | ///
176 | [PrefectureName("千葉", "千葉県")]
177 | Chiba,
178 | ///
179 | /// 東京都
180 | ///
181 | [PrefectureName("東京", "東京都")]
182 | Tokyo,
183 | ///
184 | /// 神奈川県
185 | ///
186 | [PrefectureName("神奈川", "神奈川県")]
187 | Kanagawa,
188 | ///
189 | /// 新潟県
190 | ///
191 | [PrefectureName("新潟", "新潟県")]
192 | Niigata,
193 | ///
194 | /// 富山県
195 | ///
196 | [PrefectureName("富山", "富山県")]
197 | Toyama,
198 | ///
199 | /// 石川県
200 | ///
201 | [PrefectureName("石川", "石川県")]
202 | Ishikawa,
203 | ///
204 | /// 福井県
205 | ///
206 | [PrefectureName("福井", "福井県")]
207 | Fukui,
208 | ///
209 | /// 山梨県
210 | ///
211 | [PrefectureName("山梨", "山梨県")]
212 | Yamanashi,
213 | ///
214 | /// 長野県
215 | ///
216 | [PrefectureName("長野", "長野県")]
217 | Nagano,
218 | ///
219 | /// 岐阜県
220 | ///
221 | [PrefectureName("岐阜", "岐阜県")]
222 | Gifu,
223 | ///
224 | /// 静岡県
225 | ///
226 | [PrefectureName("静岡", "静岡県")]
227 | Shizuoka,
228 | ///
229 | /// 愛知県
230 | ///
231 | [PrefectureName("愛知", "愛知県")]
232 | Aichi,
233 | ///
234 | /// 三重県
235 | ///
236 | [PrefectureName("三重", "三重県")]
237 | Mie,
238 | ///
239 | /// 滋賀県
240 | ///
241 | [PrefectureName("滋賀", "滋賀県")]
242 | Shiga,
243 | ///
244 | /// 京都府
245 | ///
246 | [PrefectureName("京都", "京都府")]
247 | Kyouto,
248 | ///
249 | /// 大阪府
250 | ///
251 | [PrefectureName("大阪", "大阪府")]
252 | Osaka,
253 | ///
254 | /// 兵庫県
255 | ///
256 | [PrefectureName("兵庫", "兵庫県")]
257 | Hyogo,
258 | ///
259 | /// 奈良県
260 | ///
261 | [PrefectureName("奈良", "奈良県")]
262 | Nara,
263 | ///
264 | /// 和歌山県
265 | ///
266 | [PrefectureName("和歌山", "和歌山県")]
267 | Wakayama,
268 | ///
269 | /// 鳥取県
270 | ///
271 | [PrefectureName("鳥取", "鳥取県")]
272 | Tottori,
273 | ///
274 | /// 島根県
275 | ///
276 | [PrefectureName("島根", "島根県")]
277 | Shimane,
278 | ///
279 | /// 岡山県
280 | ///
281 | [PrefectureName("岡山", "岡山県")]
282 | Okayama,
283 | ///
284 | /// 広島県
285 | ///
286 | [PrefectureName("広島", "広島県")]
287 | Hiroshima,
288 | ///
289 | /// 山口県
290 | ///
291 | [PrefectureName("山口", "山口県")]
292 | Yamaguchi,
293 | ///
294 | /// 徳島県
295 | ///
296 | [PrefectureName("徳島", "徳島県")]
297 | Tokushima,
298 | ///
299 | /// 香川県
300 | ///
301 | [PrefectureName("香川", "香川県")]
302 | Kagawa,
303 | ///
304 | /// 愛媛県
305 | ///
306 | [PrefectureName("愛媛", "愛媛県")]
307 | Ehime,
308 | ///
309 | /// 高知県
310 | ///
311 | [PrefectureName("高知", "高知県")]
312 | Kouchi,
313 | ///
314 | /// 福岡県
315 | ///
316 | [PrefectureName("福岡", "福岡県")]
317 | Fukuoka,
318 | ///
319 | /// 佐賀県
320 | ///
321 | [PrefectureName("佐賀", "佐賀県")]
322 | Saga,
323 | ///
324 | /// 長崎県
325 | ///
326 | [PrefectureName("長崎", "長崎県")]
327 | Nagasaki,
328 | ///
329 | /// 熊本県
330 | ///
331 | [PrefectureName("熊本", "熊本県")]
332 | Kumamoto,
333 | ///
334 | /// 大分県
335 | ///
336 | [PrefectureName("大分", "大分県")]
337 | Oita,
338 | ///
339 | /// 宮崎県
340 | ///
341 | [PrefectureName("宮崎", "宮崎県")]
342 | Miyazaki,
343 | ///
344 | /// 鹿児島県
345 | ///
346 | [PrefectureName("鹿児島", "鹿児島県")]
347 | Kagoshima,
348 | ///
349 | /// 沖縄県
350 | ///
351 | [PrefectureName("沖縄", "沖縄県")]
352 | Okinawa,
353 | ///
354 | /// 不明
355 | ///
356 | [PrefectureName("不明", "不明")]
357 | Unknown = 99,
358 | }
359 | ///
360 | /// 都道府県のenumに対する拡張
361 | ///
362 | public static class PrefectureExtensions
363 | {
364 | ///
365 | /// 正式名称を取得する
366 | ///
367 | public static string GetLongName(this Prefecture pref)
368 | {
369 | var attr = pref.GetType().GetField(pref.ToString())?.GetCustomAttribute();
370 | if (attr == null)
371 | return "不明";
372 | return attr.LongName;
373 | }
374 | ///
375 | /// 短縮名を取得する
376 | ///
377 | public static string GetShortName(this Prefecture pref)
378 | {
379 | var attr = pref.GetType().GetField(pref.ToString())?.GetCustomAttribute();
380 | if (attr == null)
381 | return "不明";
382 | return attr.ShortName;
383 | }
384 | }
385 | }
386 |
--------------------------------------------------------------------------------