LINE Messaging APIを使ってBOTに現在位置の雨雲レーダー画像を表示させる

# はじめに

前回、LINE Messaging APIでX-Line-Signatureの署名検証をしました。
[LINE Messaging APIでX-Line-Signatureの署名検証を行う(AzureFunctions/Node.js）](http://qiita.com/yorifuji/items/71e31baf896adb69f567)

今回はLINEから位置情報を送ると、その場所の雨雲レーダーの画像を返してくる機能を実装してみます。動作例はこちらです。
![out.gif](https://qiita-image-store.s3.amazonaws.com/0/104863/34642f35-ef9f-9501-5087-de7bc3c4e943.gif)

YahooのスタティックマップAPIを使って生成した画像のURLをタイムラインに表示しています。

# 必要なもの

<Font color="red">**注意事項**</font>
Yahoo!デベロッパーネットワークで提供されるAPIを使ったアプリケーションを「公開」する場合は[Yahoo!デベロッパーネットワークのガイドライン](http://docs.yahoo.co.jp/docs/info/terms/chapter1.html#cf5th)に従う必要があります。アプリケーション公開の際には「クレジット表示ガイドライン」と「クレジット配置ルール」を順守しなければなりません（本記事のサンプルではクレジットは表示していません）。詳細はガイドラインをご確認ください。

----

位置情報を元に雨雲レーダーの画像が取得できるAPIが必要です。
いろいろ探してみると、 Yahoo!が提供しているスタティックマップAPIが使えそうです。


[YOLP(地図):Yahoo!スタティックマップAPI - Yahoo!デベロッパーネットワーク]( http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/static.html)

```
Yahoo!スタティックマップAPIでできること

- Yahoo! JAPANの提供する地図画像をウェブページに自由にはり付けて利用できます。
- Yahoo!スタティックマップAPI（以下、スタティックマップAPI）が出力する地図は静的な画像ですので、JavaScriptの実行環境は必要ありません。
- 地図上にマーカー、ポリライン、ポリゴン、円を描画できます。
- インターネット上に公開された地理情報をAPIプロキシを通して地図上に読み込み、表示することが可能です。

雨雲レーダーを表示する

オーバーレイ（overlay）パラメータに雨雲（rainfall）を指定することで、地図に現在時刻の雨雲レーダーを表示することができます。属性により過去の日時（date）の指定や、日時ラベルの表示（datelabel）も指定ができます。さらに地図の種類（mode）を指定することにより、航空写真にも雨雲レーダーを表示できます。
※雨雲レーダーの表示は、気象レーダーで観測された降水の強さを時間雨量（mm/h）に換算した値で、実際の雨量とは異なります。 

```

静的なURLを生成することができるのでタイムラインにそのまま貼り付けることができます。
いっぽう、LINE Messaging APIのSend Message Objectの仕様を読むと、画像のURLはhttpsである必要があります。

https://devdocs.line.me/ja/#image

```
originalContentUrl	String	画像のURL HTTPS

Imageの例
{
    "type": "image",
    "originalContentUrl": "https://example.com/original.jpg",
    "previewImageUrl": "https://example.com/preview.jpg"
}
```

Yahoo!スタティックマップAPIで生成できるURLはhttpなのでこのままでは使えません。
サーバー上でファイルに落としてhttpsでアクセスできるようにすれば良いですが、少しイマイチなので別の方法を考えてみました。

URLがhttpsであれば良いのなら、短縮URLを使ってhttpsに変換したらどうかと思いました。
試しにgoogleの短縮URLサービスを使ってスタティックマップのURLを短縮URLに変換して送信したところ、タイムラインに画像が表示されました。これでいけそうです。

ただ、短縮URLに変換する処理はBOT側で行う必要があります。Googleのドキュメントを調べると、短縮URLを生成するサービスがWebAPIとして提供されていました。

[URL Shortener  |  Google Developers](https://developers.google.com/url-shortener/)

最終的には次のような流れとなります。

端末から位置情報を送信 --> BOTで位置情報を元にYahoo!雨雲レーダーのURLを生成(http) --> URL Shortenerを使って短縮URLに変換(https) --> ImageとしてLINEに返信

Yahoo!スタティックマップAPIを利用するためにはAPIの利用登録が必要です。
登録すると「アプリケーションID」が払い出されます。APIを呼び出すときに必要になります。
[Yahoo!デベロッパーネットワーク](http://developer.yahoo.co.jp/)

また、GoogleDeveloperの登録を行うとURL Shortenerを利用するための「API key」が払い出されます。

詳細は各サイトをご確認ください。

# ソースコード

GitHubはこちらです。[yorifuji/azure-functions-line-weather-map-bot](https://github.com/yorifuji/azure-functions-line-weather-map-bot)

なお、各APIを利用するために以下のキーを環境変数に登録する必要があります。

| キー| 内容 | 用途 |
|:--|:--|:--|
|YAHOO_APP_ID|アプリケーションID|スタティックマップのURL生成|
|GOOGLE_API_KEY|API キー|URL Shortener API|

その他にも以下のキーを登録してください。

| キー| 内容 | 用途 |
|:--|:--|:--|
|LINE_CHANNEL_SECRET|LINE developersのChannel Secretの値|x-line-signatureの署名検証|
|LINE_CHANNEL_ACCESS_TOKEN|LINE developersのChannel Access Tokenの値|メッセージの返信|

Azure Functionsへの配置方法などは前々回の記事を参照。
[Azure Functionsを使って非同期処理のLINE BOTを作成する - Qiita](http://qiita.com/yorifuji/items/7f892564958cf464a812)

# 実装詳細

LINEから「位置情報」を送ると、LocationMessageが送信されます。

```
Location Messageの例
{
  "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  "type": "message",
  "timestamp": 1462629479859,
  "source": {
    "type": "user",
    "userId": "U206d25c2ea6bd87c17655609a1c37cb8"
  },
  "message": {
    "id": "325708",
    "type": "location",
    "title": "my location",
    "address": "〒150-0002 東京都渋谷区渋谷２丁目２１−１",
    "latitude": 35.65910807942215,
    "longitude": 139.70372892916203
  }
}
```

message.latitude、message.longitudeが地図で示した緯度・経度の値です。

次に、Yahooスタティックマップの使い方です。URLでパラメータを指定します。
lat, lonで緯度、経度を指定します。zは地図の縮尺、widht、heightは画像の大きさの指定です。
"overlay"パラメータに"type:rainfall"を付与することで雨雲レーダーがオーバーレイした地図になります。
"datelabel:on"は地図上にテキストを表示する指定です。
最後に"date:"に続けて時刻を指定すると、当該時刻の雨雲の様子が出力されます（過去の時間でもOK）。

なお、Azure上の環境ではDate()でUTCが帰ってくるのでJSTに調整が必要です。

```js:QueueTriggerNodeJS1/index.js

function make_date_str() {
    var d = new Date();
    d.setTime(d.getTime() + 1000 * 60 * 60 * 9); // UTC --> JST

    var year  = d.getFullYear();
    var month = d.getMonth() + 1;
    var date  = d.getDate();
    var hour  = d.getHours();
    var min   = d.getMinutes();

    if (month < 10) month = '0' + month;
    if (date  < 10) date  = '0' + date;
    if (hour  < 10) hour  = '0' + hour;
    if (min   < 10) min   = '0' + min;

    return [year, month, date, hour, min].reduce((pre, cur) => pre + cur.toString());
}

function make_yahoo_api_map_rainfall_url(lat, lon) {
    const yahoo_api_map_url = "http://map.olp.yahooapis.jp/OpenLocalPlatform/V1/static?";
    const zoom   = 11;
    const width  = 600;
    const height = 800;
    const query_str = querystring.stringify({
        "appid"   : process.env.YAHOO_APP_ID,
        "lat"     : lat,
        "lon"     : lon,
        "z"       : zoom,
        "width"   : width,
        "height"  : height,
        "pointer" : "on",
        "mode"    : "map",
        "overlay" : "type:rainfall|datelabel:on|date:" + make_date_str()
    });
    return yahoo_api_map_url + query_str;
}
```

このURLをhttpsに変換する必要があります。httpsへの変換はGoogleの短縮URLのAPIを使います。

```
function location_handler(context, event) {

    const url = make_yahoo_api_map_rainfall_url(event.message.latitude, event.message.longitude);
    context.log(url);

    return new Promise((resolve, reject) => {
        var req = https.request({
            host: 'www.googleapis.com',
            path: '/urlshortener/v1/url?key=' + process.env.GOOGLE_API_KEY,
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
        }, res => {
            var body = '';
            res.on('data', chunk => {
                body += chunk.toString();
            });
            res.on('end', () => {
                var d = JSON.parse(body);
                event.message = {
                    "type"               : "image",
                    "originalContentUrl" : d.id,
                    "previewImageUrl"    : d.id
                };
                resolve(event);
            });
            res.on('error', err => {
                event.message = {
                    "type" : "text",
                    "text" : err.message
                };
                reject(event);
            });
        });
        req.write(JSON.stringify({"longUrl" : url}));
        req.end();
    });
}
```

httpsモジュールを使ってAPIを呼び出します。requestを使う方が簡単だと思いますが、httpsはインストールが不要です。
API呼び出しにはAPI KEYが必要です。

```
            path: '/urlshortener/v1/url?key=' + process.env.GOOGLE_API_KEY,
```

成功するとレスポンスに短縮URLが含まれています。
LINEに画像として送りためには以下のようなフォーマットで送信します。

```
                var d = JSON.parse(body);
                event.message = {
                    "type"               : "image",
                    "originalContentUrl" : d.id,
                    "previewImageUrl"    : d.id
                };
```

# 注意事項

送信される画像のURLにYahooのアプリケーションIDが含まれます。アプリケーションIDはそれがわかるとAPIが利用できてしまうので、不特定多数に利用されてしまいます。その点ご注意ください。

# まとめ

Yahoo!スタティックマップAPIを使ってLINEから位置情報を送ると雨雲レーダーの画像を返してくれる機能をついかしました。URLをhttpsに変換するためにGoogleURLShortenerを使いました。
内容に誤りがあればコメントいただけると幸いです。

# 予定

- テキストの内容に基づいて応答を変える機能