こんにちは。ネクストスケープの金子と申します。
この記事は「NEXTSCAPE Advent Calendar 2025」の10日目の記事です。
はじめに
任意の場所の海況情報を取得して返すLINE botを作ってみました。
開発背景
海水魚採集では、実際に海へ出向いて採集を行います。
しかし、行こうとしている海がいつも採集に適した状態とは限りません。
そこで出発前には、必ず海況(海の状態)を確認し、採集に向いたコンディションかどうかを判断する必要があります。
たとえば、神奈川県 三浦半島の相模湾側(赤線のエリア)で、海に潜って魚を採集するケースを考えた場合、以下の観点が必要です。

天気
晴れが望ましい。
曇りだと光量不足、雨だと真水の流入で海中の視認性が悪くなるので、できれば避けたい。
雷は感電による水難事故の確率が格段に上がるので絶対に避ける。
風向き・風速
凪(ほぼ風が吹いていない状態)が望ましい。
風が吹かないので海が荒れず、潜りやすくなります。

南~西北西の向きで風が吹いている場合は避けたほうがよいです。
海から陸に向かって風が吹くので、風に運ばれた海水が陸にぶつかることにより、海が荒れやすくなります。
風速は3.0m/sくらいが許容範囲

北~東南東の向きであれば、とんでもなく強い風でなければ問題ないことが多いです。
陸から海に向かって吹く風なので、上記と比較すると海は荒れづらい…かもしれない。
風速は7.0m/sくらいが許容範囲

潮回り
潮回りとは、いわゆる潮汐情報のことです。
何時に潮が引く/満ちるのか、そしてどれくらい水位が変化するのかといった情報は、採集において非常に重要です。

三浦半島のように、ある程度水深のあるポイントで潜る場合は、潮が引いている時間帯が狙い目です。
水位が低いほど海底までの距離が短くなり、潜って魚を捕まえやすくなるためです。
また、潮の満ち引きは月の満ち欠けと連動しており、月の状態によって干満差(干潮と満潮の水位の差)が大きく変化します。
満月や新月のときは「大潮」と呼ばれ、干満差が最も大きく、干潮では大きく水位が下がり、満潮では大きく上がります。
なぜ作ったのか
上記の情報をいろいろなサイトをめぐって集めるのですが、どうせなら一回で全部集められる仕組みを作りたいなと考えたのでいろいろ調べた結果、天候や潮汐情報を配信しているAPIがあったので、それを利用して作ってみようと思いました。
また、この1年は主にインフラ周りの業務を担当していた関係でコードを書く機会に乏しかったのと、現在の所属部署の技術テーマがクリーンコード・クリーンアーキテクチャなので、自分で何か作ってみてそのあたりの概念をしっかり学んでみよう、と考えたというのもあります。
どんなアプリか
地名を入力して送信すると、その場所の海況(天気・潮汐)情報を返してくれるアプリです。
手軽に必要な情報を確認したいなと思い、この仕組みをLINE Botとして開発することにしました。
下記の情報を取得するようにしています。
- 天気
- 気温
- 風速
- 風向き
- 降水量
- 潮回り(どれくらいの干満差があるか)
- 大潮・中潮・小潮・若潮・長潮 の5つで判定
- 日の出・日の入りの時間
- 最干潮時間と水位
- 最満潮時間と水位
使用技術
サーバーサイド
.NET 8.0
インフラ
Amazon Web Service
大まかな構成図
LINEと外部のサービスを連携をさせるには、LINE Developersに登録する必要があります。
今回の構成では、LINEのメッセージをAPI Gateway経由でLambdaに渡して処理しているため、その入り口としてMessaging APIを使用しています。
使用した外部API
なるべくお金かからなさそうなやつを選んで採用しました。
レスポンスはどのAPIもJSON形式で取れます。
①Google Maps GeoCoding API
メッセージとして送信した地名から、住所と緯度・経度を取得するのに使用しました。
使用にはGoogle CloudのアカウントとAPIキーが必要。
https://maps.googleapis.com/maps/api/geocode/json?address={地名・住所など}&key={APIキー}&language=ja
雑に地名を入れてもかなり高精度の位置情報を返してくれるので個人的には便利だなと思います。
②日本沿岸736港の潮汐表 - tide736.net
潮汐情報を取得するのに使用しました。
使用にアカウント登録やAPIキーの取得などは不要。
https://tide736.net/api/get_tide.php?pc={都道府県コード}&hc={港コード}&yr={年}&mn={月}&dy={日}&rg={取得したい日数分}
普段潮回りの確認に使っている潮MieYellも同じAPIを使ってるみたいなのでこちらを採用しました。
パラメータの港コードについては後述。
③OpenWeatherMap WeatherAPI
天候情報(天気・気温・風)を取得するのに使用しました。
使用にはアカウント登録およびAPIキーが必要。
https://api.openweathermap.org/data/3.0/onecall?lat={緯度}&lon={経度}&appid={APIキー}
ただし、天気は天気コード、気温はケルビン、風向きは角度で返ってくるので、都度変換する処理を実装する必要があります。
地点間の距離を緯度経度を用いて計算する
潮汐APIに使用する港コードですが、「港情報」としてS3にJSON形式で保存しています。
例:神奈川県の港情報
[
{
"prefecture_code": 14,
"harbor_code": 1,
"harbor_namej": "塩浜運河",
"harbor_name": "Siohama Unga",
"latitude": 35.51,
"longitude": 139.74,
"tide_type": 1
},
{
"prefecture_code": 14,
"harbor_code": 2,
"harbor_namej": "末広",
"harbor_name": "Suehiro",
"latitude": 35.47,
"longitude": 139.68,
"tide_type": 2
},
{
"prefecture_code": 14,
"harbor_code": 3,
"harbor_namej": "新港",
"harbor_name": "Sinko^",
"latitude": 35.45,
"longitude": 139.64,
"tide_type": 1
},
...(中略)
{
"prefecture_code": 14,
"harbor_code": 19,
"harbor_namej": "江ノ島",
"harbor_name": "E-no-Sima",
"latitude": 35.29,
"longitude": 139.48,
"tide_type": 2
},
{
"prefecture_code": 14,
"harbor_code": 20,
"harbor_namej": "真鶴",
"harbor_name": "Manaduru",
"latitude": 35.15,
"longitude": 139.14,
"tide_type": 2
}
]
GeoCoding APIで取得した位置情報から、最も近い場所にある港情報を取りたかったのでどうすればいいか調べたところ、地点間の距離を緯度経度を使って計算する方法があることを知りました。
いくつかあるうち、今回はハーバーサイン法と呼ばれる算出法を採用しました。
ざっくり言うと、地球の表面を球体と仮定して、緯度・経度を用いて大円距離(地球の表面に沿った最短距離)を計算する方法です。
$D = 2r \times \arcsin\left(\sqrt{\sin^2\frac{\phi_2 - \phi_1}{2} + \cos(\phi_1)\cos(\phi_2)\sin^2\frac{\lambda_2 - \lambda_1}{2}}\right)$
上記が公式なのですが、見ても何をやってるかさっぱりわからなかったので実装例がないか探したところ、Pythonで実装されている方がいました。
それを参考に、C#で実装したのが以下になります。
例:東京駅と新大阪駅の距離を計算する
public class DistanceCalculator
{
// 地球の半径(メートル)
private const double earthRadius = 6371000.0;
// 東京駅の緯度・経度
private const double TokyoLat = 35.68;
private const double TokyoLon = 139.76;
// 新大阪駅の緯度・経度
private const double ShinOsakaLat = 34.73;
private const double ShinOsakaLon = 135.50;
/// <summary>
/// 二地点の距離を計算する
/// </summary>
/// <returns>距離</returns>
public static double CalculateDistance()
{
// 東京駅・新大阪駅の緯度経度をラジアンに変換
var tokyoLatRad = ConvertToRadians(TokyoLat);
var tokyoLonRad = ConvertToRadians(TokyoLon);
var shinOsakaLatRad = ConvertToRadians(ShinOsakaLat);
var shinOsakaLonRad = ConvertToRadians(ShinOsakaLon);
// 緯度の差、経度の差を計算
var differenceLat = shinOsakaLatRad - tokyoLatRad;
var differenceLon = shinOsakaLonRad - tokyoLonRad;
// ハーバーサイン法の中間値aを計算
var a = Math.Pow(Math.Sin(differenceLat / 2), 2) +
Math.Cos(tokyoLatRad) * Math.Cos(shinOsakaLatRad) *
Math.Pow(Math.Sin(differenceLon / 2), 2);
// 中心角cを計算
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
// 2地点間の距離を計算
var distance = earthRadius * c;
// kmに変換
return distance / 1000.0;
}
/// <summary>
/// 座標(緯度・経度)をラジアンに変換する
/// </summary>
/// <param name="coordinate">座標(緯度・経度)の数値</param>
/// <returns>ラジアンに変換した座標(緯度・経度)の数値</returns>
private static double ConvertToRadians(double coordinate) => coordinate * Math.PI / 180.0;
}
上記実装で試すと401.71kmと算出されます。
大体合ってるんじゃないかと思います。
実際に使ってみる
東京都の離島である「伊豆大島」と入力した結果です。
潮汐情報の観測には、伊豆大島の玄関港である「岡田港」のデータを使っています。

また、海無し県の地名を入力した場合は天候だけ返すようになっています。

今後増やしたい機能
- 緯度経度を直接入力して検索する機能
- 日付・時間帯を指定して検索する機能
- 潮汐グラフ画像の取得 など
とはいえこのあたりの機能を実現しようとするとLINE botでは限界がある気がするので、Webアプリとして作るほうがよいんじゃないかなという気がしてます。
おわりに
長くなってしまいましたが、お読みいただきありがとうございました。
さほど凝った構成や実装などはしていないのと、記事の内容としては局所的であまり参考にはならないかもですが、似たようなことをされている方などに少しでも参考になればと思います。
余談
緯度経度から地点間の距離を求めるライブラリが.NET Framework 4.0の時代まではあったみたいでした。


