Yahoo! Open Local Platform (YOLP) を使ってお天気情報を取得したついでにラズパイにAquesTalkPiで喋らせてみる


ちょっと訳あって、手軽にお天気情報、なるべくなら明日とか明後日とかそういうスパンではなく、今から◯◯分後、くらいのスパンで天気予報をしてくれるものが欲しくて探していたら、Yahoo! Open Local Platform (YOLP)をみつけた。あんまり利用事例が見つからなかったのだけど、とても便利で面白かったので簡単にまとめてみる。例によって、追記したほうが良いようなことができたら [blog](http://blog.1783.org) の方に書く予定。

#【YOLPについて】

公式サイトによれば、

> Yahoo! Open Local Platform(YOLP)は、Yahoo!JAPANがデベロッパー向けに提供する地図・地域情報のAPI・SDKです。

(http://developer.yahoo.co.jp/webapi/map/ から引用)
とのこと。この中に、[気象情報API](http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html)があるのだけどこれが超絶便利。
なお、商用利用の場合については http://www.yahoo-help.jp/app/answers/detail/p/537/a_id/43405 で触れられており、ざっくりまとめると「非商用目的での利用が認められている。商用サイトや企業による利用をすべて禁じるものではなく、法人利用したい場合には各APIの問い合わせ窓口に相談してね」とのことで、いい感じに緩い。


#【気象情報を取得する】

##準備：アプリケーションIDを取得する
[「ご利用ガイド」](http://developer.yahoo.co.jp/start/)に従ってアプリケーションIDを取得する。登録内容については、特に悩むようなところはないので説明を割愛。

##Pythonスクリプトで試してみる
[気象情報APIの説明](http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html)を読みながらPythonでの取得を試してみる。

XMLでの取得の場合もJSONでの取得の場合も、ターゲットとなるURLは http://weather.olp.yahooapis.jp/v1/place となる。
ここに必要な情報をクエリにしてくっつけてGETしたらJSONが返ってくるという極めて標準的な流れ。

とりあえず動作確認という意味では、下記の3つがあれば十分。

- appid : アプリケーションID。前出の手順で取得したもの。
- coordinates : 緯度経度。経度、緯度の順序でカンマ区切り。複数地点検索する場合は半角スペースで区切る(最大10地点)
- output : 出力形式。xml / json のいずれか。

せっかくなら「今降ってる、あるいはこれから降りそう」という地点データのほうが面白いので、たまたま確認時に雨が降っている、[宮城県七ヶ宿町付近](https://goo.gl/maps/8XUxx12CdjN2)で試してみる。

```python:getOtenki.py
#!/usr/bin/python
# encoding:utf-8

import urllib
import pprint
import json

APP_ID = "***************************************"

BASE_URL = "http://weather.olp.yahooapis.jp/v1/place"
COORDINATES = "140.471414,38.026973"
OUTPUT="json"

url = BASE_URL + "?appid=%s&coordinates=%s&output=%s" % (APP_ID,COORDINATES,OUTPUT)
# print (url)

json_tree = json.loads( urllib.urlopen(url).read())
pprint.pprint(json_tree)
```

出力結果はこんな感じ。

```出力結果
{u'Feature': [{u'Geometry': {u'Coordinates': u'140.47141,38.026973',
                             u'Type': u'point'},
               u'Id': u'201609231900_140.47141_38.026973',
               u'Name': u'\u5730\u70b9(140.47141,38.026973)\u306e2016\u5e7409\u670823\u65e5 19\u664200\u5206\u304b\u308960\u5206\u9593\u306e\u5929\u6c17\u60c5\u5831',
               u'Property': {u'WeatherAreaCode': 3420,
                             u'WeatherList': {u'Weather': [{u'Date': u'201609231900',
                                                            u'Rainfall': 5.25,
                                                            u'Type': u'observation'},
                                                           {u'Date': u'201609231910',
                                                            u'Rainfall': 0.95,
                                                            u'Type': u'forecast'},
                                                           {u'Date': u'201609231920',
                                                            u'Rainfall': 2.13,
                                                            u'Type': u'forecast'},
                                                           {u'Date': u'201609231930',
                                                            u'Rainfall': 1.25,
                                                            u'Type': u'forecast'},
                                                           {u'Date': u'201609231940',
                                                            u'Rainfall': 0.55,
                                                            u'Type': u'forecast'},
                                                           {u'Date': u'201609231950',
                                                            u'Rainfall': 0.0,
                                                            u'Type': u'forecast'},
                                                           {u'Date': u'201609232000',
                                                            u'Rainfall': 1.75,
                                                            u'Type': u'forecast'}]}}}],
 u'ResultInfo': {u'Copyright': u'(C) Yahoo Japan Corporation.',
                 u'Count': 1,
                 u'Description': u'',
                 u'Latency': 0.004469,
                 u'Start': 1,
                 u'Status': 200,
                 u'Total': 1}}
```

欲しいデータは、json_tree['Feature'][0]['Property']['WeatherList']['Weather']に存在しているので、こんな感じにすると必要なデータが取得できる。

```
#!/usr/bin/python
# encoding:utf-8

import urllib
import pprint
import json

APP_ID = "******************************"

BASE_URL = "http://weather.olp.yahooapis.jp/v1/place"
COORDINATES = "140.471414,38.026973"
OUTPUT="json"

url = BASE_URL + "?appid=%s&coordinates=%s&output=%s" % (APP_ID,COORDINATES,OUTPUT)
# print (url)

json_tree = json.loads( urllib.urlopen(url).read())
#pprint.pprint(json_tree)

for var in range(0,7):
    date = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Date']
    rainfall = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Rainfall']
    type = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Type']
    print("%s,%s,%s"%(date,rainfall,type))

```

出力結果はこんな感じになる。
[レスポンスフィールド](http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html#response_field)を見てみると、Typeフィールドは、observationが実測値、forecastが予測値を示している。また、Rainfallフィールドは降水強度(観測された降水の強さをmm/hに換算した値なので降水量ではない)とのこと。ふむふむ。

```
201609231945,0.85,observation
201609231955,0.0,forecast
201609232005,2.13,forecast
201609232015,1.45,forecast
201609232025,1.45,forecast
201609232035,1.95,forecast
201609232045,0.0,forecast
```

##郵便番号から緯度経度を取得して天気予報したい
上記で最低限のやりたいことは満たせたわけだが、いちいち座標を指定するのもイケてないので、同じくYOLPに用意されている、[郵便番号検索API](http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/zipcodesearch.html)を使って「郵便番号を引数にするとその座標を勝手に引いてくれる」ようにしたい。

こちらも今から降りそうなところじゃないと面白くないので、今から降りそうな大井松田ICあたりの郵便番号を指定してみる。また、郵便番号が指定されていなければ浜離宮付近のデータを表示するようにした。

```
#!/usr/bin/python
# encoding:utf-8

import urllib
import pprint
import json
import sys

APP_ID = "***********************************"

BASE_URL = "http://weather.olp.yahooapis.jp/v1/place"
COORDINATES = "139.763707,35.659729"
OUTPUT="json"

if len(sys.argv) == 2:
    zip_code = sys.argv[1]
    ZIP_BASE_URL = "http://search.olp.yahooapis.jp/OpenLocalPlatform/V1/zipCodeSearch"
    zip_url = ZIP_BASE_URL + "?appid=%s&query=%s&output=%s" % (APP_ID,zip_code,OUTPUT)
    zip_json_tree = json.loads(urllib.urlopen(zip_url).read())
    #pprint.pprint(zip_json_tree['Feature'][0]['Geometry']['Coordinates'])
    COORDINATES = zip_json_tree['Feature'][0]['Geometry']['Coordinates']


url = BASE_URL + "?appid=%s&coordinates=%s&output=%s" % (APP_ID,COORDINATES,OUTPUT)
# print (url)

json_tree = json.loads( urllib.urlopen(url).read())
#pprint.pprint(json_tree)

for var in range(0,7):
    date = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Date']
    rainfall = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Rainfall']
    type = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Type']
    print("%s,%s,%s"%(date,rainfall,type))

```


```出力結果
# python sample.py "258-0019"
201609232015,0.0,observation
201609232025,0.0,forecast
201609232035,0.85,forecast
201609232045,16.5,forecast
201609232055,4.38,forecast
201609232105,0.0,forecast
201609232115,0.0,forecast

# python sample.py
201609232015,0.0,observation
201609232025,0.0,forecast
201609232035,0.0,forecast
201609232045,0.0,forecast
201609232055,0.0,forecast
201609232105,0.0,forecast
201609232115,0.0,forecast
```

うむ。やりたいことはできた。

#【喋らせる】
不勉強なもので今まで知らなかったのだけど、[AquesTalkPi](http://www.a-quest.com/products/aquestalkpi.html)という、ラズパイに喋らせられるプログラムがある。
先程の出力を適当に加工して喋らせてみる。
本当ならもうちょっと賢くしたいところだけど今日のところはこれで手を打つことにする。

```
#!/usr/bin/python
# encoding:utf-8

import urllib
import pprint
import json
import sys
import os

APP_ID = "********************************************"

BASE_URL = "http://weather.olp.yahooapis.jp/v1/place"
COORDINATES = "139.763707,35.659729"
OUTPUT="json"
ZIP_CODE = "100-0000"

if (len(sys.argv) == 2) or (len(ZIP_CODE) > 0):
    if len(sys.argv) < 2:
        zip_code = ZIP_CODE
    else:
        zip_code = sys.argv[1]
    ZIP_BASE_URL = "http://search.olp.yahooapis.jp/OpenLocalPlatform/V1/zipCodeSearch"
    zip_url = ZIP_BASE_URL + "?appid=%s&query=%s&output=%s" % (APP_ID,zip_code,OUTPUT)
    zip_json_tree = json.loads(urllib.urlopen(zip_url).read())
    #pprint.pprint(zip_json_tree['Feature'][0]['Geometry']['Coordinates'])
    COORDINATES = zip_json_tree['Feature'][0]['Geometry']['Coordinates']
    name = zip_json_tree['Feature'][0]['Property']['Address']
    print name


url = BASE_URL + "?appid=%s&coordinates=%s&output=%s" % (APP_ID,COORDINATES,OUTPUT)
# print (url)

json_tree = json.loads( urllib.urlopen(url).read())
#pprint.pprint(json_tree)

for var in range(0,7):
    date     = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Date']
    rainfall = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Rainfall']
    type     = json_tree['Feature'][0]['Property']['WeatherList']['Weather'][var]['Type']
    print("%s,%s,%s"%(date,rainfall,type))
    rain_level = ""
    talk = ""
    if (rainfall == 0.0):
        rain_level = "雨は降"
    elif (rainfall < 5.0) :
        rain_level = "雨がちょっと降"
    elif (rainfall < 10.0):
        rain_level = "雨が結構降"
    elif (rainfall < 20.0):
        rain_level = "やや強い雨が降"
    elif (rainfall < 30.0):
        rain_level = "土砂降りの雨が降"
    elif (rainfall < 50.0):
        rain_level = "激しい雨が降"
    elif (rainfall < 80.0):
        rain_level = "非常に激しい雨が降"
    elif (rainfall >= 80.0):
        rain_level = "猛烈な雨が降"

    if type == "observation" :
        time = "今、"
        if rainfall == 0.0:
            suffix = "っていません"
            talk = time + rain_level + suffix
        else:
            suffix = "っています"
            talk = time + rain_level + suffix
    else:
        time = str(var * 10) + "分後に、"
        if rainfall == 0.0:
            # suffix = "りません。"
            talk = ""
        else:
            suffix = "りそうです。"
            talk = time + rain_level + suffix

    print talk
    if len(talk) > 0:
        os.system('/home/pi/AquesTalkPi "' + talk + '" | aplay&')
```

おおお、「あの声」で喋る！これは面白い。

てことで今日はここで終了。