【自動化】雨が降りそうになると音声通知してくれるアプリ

# 0. はじめに
本稿では、**[Yahoo!気象情報API](https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html)を用いて雨量情報を5分毎に取得し、雨が振りそうな場合に音声通知してくれるアプリの実装**を紹介します。

## 0.1 注意事項
python3を前提としています。
Yahoo!気象情報APIを利用するには、Yahoo! Client IDの取得が必要です。Yahoo! Client IDの取得に要する時間は、3分程度と比較的容易です。

# 1. 完成イメージ
居住地を中心とした2kmの範囲、全25地点の雨量情報を5分周期で収集し、必要に応じて音声アラートで通知します。
## 雨が振りそうなとき
下図のように**外側の地点で降雨を観測**すると、「it is going to rain.」という音声を流します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2756450/558c585c-fe48-f575-5e73-f734e0195132.png)

## 雨が振っている、もしくは、今にも振りそうなとき
下図のように**内側の地点で降雨を観測**すると、「it is raining.」という音声を流します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2756450/d086de7b-076e-e6d0-3d17-0fde1781ef3a.png)

今回は、居住地として静岡県藤枝駅前付近を想定します。

https://www.google.co.jp/maps/place/%E8%97%A4%E6%9E%9D%E9%A7%85/@34.8055746,138.2532562,13z/data=!4m6!3m5!1s0x601a4ff6e874ef0d:0x6467ffcf2546b5df!8m2!3d34.8491821!4d138.2520381!16s%2Fm%2F04d_ny3?hl=ja

# 2. 仕様
- 雨量情報を収集する時間
    - 5分周期
- 雨量情報を収集する地点
    - 正方格子状に25点とする。
    - 緯度経度で指定する。
    - 中心の緯度経度はファイル指定する。（latitude.txt, longitude.txt）
    - 格子間距離は約500mとする。
    - 雨量情報を収集する範囲は全長2kmの正方形の範囲とする。
- 音声アラート再生の閾値
    - 雨量情報の平均値を用いる。
    - 雨量情報の平均値は2種類（Ave_Rainfall, Ave_CoreRainfall）とする。
    - Ave_Rainfallは、全25地点の平均値とする。
    - Ave_Rainfall > 0 の場合、「it is going to rain.」という音声を流す。
    - Ave_CoreRainfallは、中心付近の9地点の平均値とする。
    - Ave_CoreRainfall > 0 の場合、「it is raining.」という音声を流す。

# 3. 準備
## 3.1 Yahoo! Client IDを取得する 
今回は、雨量情報を収集するために**Yahoo!気象情報API**を利用します。
APiを利用するためには、Yahoo! Client IDの登録が必要です。
下記の公式ページに従って、Yahoo! Client IDを取得します。
yahooのアカウントを取得済みであれば、3分程度で取得できます。

https://developer.yahoo.co.jp/yconnect/v2/

必要事項を入力すると、以下のようにアプリケーション一覧が表示されます。
項目をクリックするとClient IDを確認できます。
後ほど利用するため、Client IDはメモしておいてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2756450/37f00db6-7cd3-7b88-06a9-506c8c7f329b.png)

## 3.2 Yahoo!気象情報APIの動作確認
動作確認として、以下のコマンドを実行すると、天気に関する情報を収集できます。尚、"Your Yahoo Client ID"には、先程取得したClient IDに置き換えてください。
```
# export YAHOO_API_ID="Your Yahoo Client ID"
# curl -X GET "https://map.yahooapis.jp/weather/V1/place?coordinates=138.2532562,34.8055746&output=json&interval=5&past=0&appid=${YAHOO_API_ID}" | jq 

(省略)
            {
              "Type": "forecast",
              "Date": "202211201215",
              "Rainfall": 0.65
            }
(省略)
```
## 3.3 プログラムの確認
ファイル一覧を示します。
```
# tree
.
├── alert.py
├── latitude.txt
├── longitude.txt
├── main.py
└── yahoo_id.txt
```
### 3.3.1 設定用ファイル
テキストファイルは設定用ファイルです。
- yahoo_id.txt
Client IDを設定します。
尚、以下の文字列は筆者のClient IDではなく、適当な文字列にしています。
```
# cat yahoo_id.txt 
kobbbbbcccccccaaaaaaaaaaaUU-
```
- latitude.txt
居住地の緯度情報を設定します。今回は、静岡県藤枝駅前の緯度としました。
```
# cat latitude.txt 
34.8055746
```
- longitude.txt
居住地の経度情報を設定します。今回は、静岡県藤枝駅前の経度としました。
```
# cat longitude.txt 
138.2532562
```
### 3.3.2 Pythonプログラム
main.pyでは、上記仕様に基づき、Yahoo!気象情報APIを用いて雨量情報を収集し、必要に応じて音声アラートを再生します。
```main.py
#!/usr/bin/env python3
import math
import json
import time
import datetime
import os
import numpy as np

from alert import rain_alert

# parameters
lon_100m = 0.0011
lat_100m = 0.0009
lon_diff = lon_100m * 5 
lat_diff = lat_100m * 5
len_grid = 5
len_core = 3
hlen_grid= math.floor(len_grid/2)
hlen_core= math.floor(len_core/2)

# get rainfall data using Yahoo! Weather API
def Get_RainFall(lat,lon,ID):
    
    COMMAND = "curl -X GET "
    URL = "https://map.yahooapis.jp/weather/V1/place?coordinates="+str(lon)+","+str(lat)+"&output=json&interval=5&past=0&appid="+str(ID)
    COMMAND += "\""+URL+"\""
    COMMAND += " | jq '.Feature[] | .Property.WeatherList.Weather[]'"
    COMMAND += " | sort -r | grep -m 1 Rainfall"
    data = (os.popen(COMMAND)).read()
    rf_data =str(data).replace("\"Rainfall\":","").replace("\n","")
    return float(rf_data)    


def main(lat_b,lon_b,YAHOO_ID):
    RainMap = np.zeros([len_grid,len_grid],dtype=float) 
    CoreRainMap = np.zeros([len_core,len_core],dtype=float) 
    for i in range(len_grid):
        for l in range(len_grid):
            lat = lat_b + lat_diff * (hlen_grid-l)
            lon = lon_b + lon_diff * (i-hlen_grid)
            RainMap[i][l] = Get_RainFall(lat,lon,YAHOO_ID)
    CoreRainAve = 0.0
    for i in range(len_core):    
        for l in range(len_core):
            CoreRainMap[i][l] = RainMap[hlen_grid-hlen_core+i][hlen_grid-hlen_core+l]

    RainAve = round(np.average(RainMap),2)
    CoreRainAve = round(np.average(CoreRainMap),2)
    return RainAve, RainMap, CoreRainAve, CoreRainMap

if __name__ == '__main__':
    dt_now = datetime.datetime.now()
    dt_min = dt_now.minute
    init_diff_min = math.floor((dt_min+5) / 5) * 5 - dt_min
    INIT = 1
    
    # set yahoo id
    f = open("yahoo_id.txt","r")
    YAHOO_ID = f.read().replace('\n', '')
    f.close()
    # set latitude
    f = open("latitude.txt","r")
    lat = float(f.read().replace('\n', ''))
    f.close()
    # set longitude
    f = open("longitude.txt","r")
    lon = float(f.read().replace('\n', ''))
    f.close()
    while(1):
        rf_ave, rf_map, crf_ave,crf_map = main(lat,lon,YAHOO_ID)
        print("Latitude : ",lat)
        print("Longitude : ",lon)
        print("--RainMap--")
        print(rf_map)
        print("-----------")
        print("Ave_Rainfall:",rf_ave)
        print("--CoreRainMap--")
        print(crf_map)
        print("-----------")
        print("Ave_CoreRainfall:",crf_ave)
        if (rf_ave + crf_ave) > 0.0:
            rain_alert(crf_ave)

        if INIT == 1:
            time.sleep(init_diff_min*60)
            INIT = 0
        else:
            time.sleep(60*5)
```
alert.pyには、音声アラートを再生する関数を実装しています。
```alert.py
from gtts import gTTS
from pygame import mixer
import time

def rain_alert(crf_ave):
    if crf_ave > 0.0:
        text = 'it is raining.'
    else:
        text = 'it is going to rain.'
    print(text)
    gt = gTTS(text, lang='en')
    gt.save('rain_alert.mp3')

    mixer.init()
    mixer.music.load('rain_alert.mp3')
    mixer.music.play()
```
# 4. プログラムの実行
下記のようにプログラムを実行します。
```
python3 main.py
```

## 実行結果の例１：雨が振りそうなとき
「it is going to rain.」という音声が流れます。
以下はコンソールの表示です。
```
--RainMap--
[[0.1 0.1 0.  0.  0. ]
 [0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0. ]]
-----------
Ave_Rainfall: 0.01
--CoreRainMap--
[[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]
-----------
Ave_CoreRainfall: 0.0
it is going to rain.
```
## 実行結果の例2：雨が振っている、もしくは、今にも振りそうなとき
「it is raining.」という音声が流れます。
以下はコンソールの表示です。
```
--RainMap--
[[0.45 0.45 0.45 0.45 0.45]
 [0.45 0.45 0.45 0.45 0.45]
 [0.45 0.45 0.45 0.45 0.55]
 [0.45 0.45 0.45 0.45 0.55]
 [0.45 0.45 0.45 0.45 1.25]]
-----------
Ave_Rainfall: 0.49
--CoreRainMap--
[[0.45 0.45 0.45]
 [0.45 0.45 0.45]
 [0.45 0.45 0.45]]
-----------
Ave_CoreRainfall: 0.45
it is raining.
```
# 5. 実検証
今回は、居住地として静岡県藤枝駅前付近を仮設定し、プログラムを実行しました。
実行時の静岡県の雨情報を[tenki.jp](https://tenki.jp/)で確認すると、静岡県は雨が振っていると分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2756450/decf6926-c606-60f8-1524-7a2afccb2ce4.png)

```
--RainMap--
[[1.65 1.65 2.38 2.38 1.75]
 [1.65 1.65 2.38 2.38 1.75]
 [2.38 2.38 2.38 2.38 2.63]
 [2.38 2.38 2.38 2.38 2.63]
 [2.13 2.13 2.13 2.13 2.38]]
-----------
Ave_Rainfall: 2.19
--CoreRainMap--
[[1.65 2.38 2.38]
 [2.38 2.38 2.38]
 [2.38 2.38 2.38]]
-----------
Ave_CoreRainfall: 2.3
it is raining.
```
プログラムを実行すると、雨が振っていることを検知し、音声アラート（it is raining.）が無事に流れることを確認できました。

# 6. まとめ
Yahoo!気象情報APIを用いて雨雲が近づいているかを音声通知するアプリの実装を紹介しました。

# 参考

https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html

https://gb-j.com/column/python-gtts/