YOLP の WebAPI で1時間以内の降水予想と雨雲レーダー地図画像を取得する

## 概要

- YOLP (Yahoo! Open Local Platform) の Yahoo!ジオコーダAPI + 気象情報API + Yahoo!スタティックマップAPI を使用して1時間以内の降水予想と雨雲レーダー地図画像を取得する
- 動作確認環境: Node.js v14.9.0 + node-fetch 2.6.1

## node-fetch のインストール

```
$ npm i node-fetch
```

## サンプルコード

```js
'use strict'

const fs = require('fs')
const qs = require('querystring')
const fetch = require('node-fetch') // window.fetch 互換 Fetch API

// アプリケーションID
const APPID = 'YOUR_APPLICATION_ID'

// テキストにマッチした住所情報を取得する
async function getAddressLocation(text) {

  // URLを組み立てる
  const params = qs.stringify({
    appid: APPID, // アプリケーションID
    query: text, // 検索文字列
    al: 2, // 市区町村レベルの住所を検索
    exclude_seireishi: false, // 検索対象から政令指定都市レコードを除外するか
    results: 1, // 検索結果を1件以内に設定
    output: 'json' // レスポンスをJSONにする
  })
  const url = 'https://map.yahooapis.jp/geocode/V1/geoCoder?' + params

  // Yahoo!ジオコーダAPIをコールする
  const res = await fetch(url)
  if (!res.ok) {
    throw res
  }
  const json = await res.json()
  console.log(JSON.stringify(json, null, '  '))

  // 住所情報を取得する
  if (json.Feature && json.Feature.length != 0) {
    // ヒットした1つめの住所を使う
    var name = json.Feature[0].Name
    var ll = json.Feature[0].Geometry.Coordinates.split(',')
    var bbox = json.Feature[0].Geometry.BoundingBox
    return { address: name, lat: ll[1], lon: ll[0], bbox: bbox }
  } else {
    throw '住所にヒットしなかった'
  }
}

// 住所に紐ついた緯度経度とバウンディングボックスの4点の緯度経度をつなげた文字列を返す
function getCoordinatesFromBoundingBox(location) {
  var p = location.bbox.split(' ')
  var ll0 = p[0].split(',')
  var ll1 = p[1].split(',')
  var c =
    location.lon + ',' + location.lat + ' ' +
    ll0[0] + ',' + ll0[1] + ' ' +
    ll1[0] + ',' + ll0[1] + ' ' +
    ll0[0] + ',' + ll1[1] + ' ' +
    ll1[0] + ',' + ll1[1]
  return c
}

// 降水情報を取得する
async function getWeatherInfo(location) {

  // 雨の強さを取得したい緯度経度(10点まで可)を指定
  // フォーマット: 経度,緯度 経度,緯度 経度,緯度 経度,緯度 ... 
  // 経度・緯度の順番でコンマ区切り
  // 経度・緯度毎に半角スペース区切り
  const coordinates = getCoordinatesFromBoundingBox(location)

  // URLを組み立てる
  var params = qs.stringify({
    coordinates: coordinates,
    appid: APPID, // アプリケーションID
    output: 'json' // レスポンスをJSONにする
  })
  const url = 'https://map.yahooapis.jp/weather/V1/place?' + params

  // 気象情報APIをコールする
  const res = await fetch(url)
  if (!res.ok) {
    throw res
  }
  const json = await res.json()
  console.log(JSON.stringify(json, null, '  '))
  return json
}

// 降水情報をテキストに変換する
function getWeatherText(weather, location) {
  var ame = false // 雨が降るか否か
  // 1箇所でも雨が降る場所があるか
  for (var feature of weather.Feature) {
    for (var w of feature.Property.WeatherList.Weather) {
      if (w.Rainfall > 0) { // 降水強度が0より大きいか
        ame = true
        break
      }
    }
  }
  if (ame) {
    return location.address + "では、1時間以内に雨が降りそうです。"
  } else {
    return location.address + "では、1時間以内には雨が降らないようです。"
  }
}

// 地図画像を取得する
async function getMapImage(location) {

  // URLを組み立てる
  var params = qs.stringify({
    width: 800,
    height: 600,
    lat: location.lat,
    lon: location.lon,
    z: 12, // ズームレベル
    overlay: 'type:rainfall', // 現在時刻の雨雲レーダーを表示
    style: 'base:monotone', // モノトーンスタイル
    appid: APPID, // アプリケーションID
  })
  const url = 'https://map.yahooapis.jp/map/V1/static?' + params

  // Yahoo!スタティックマップAPIをコールする
  const res = await fetch(url)
  if (!res.ok) {
    throw res
  }
  return Buffer.from(await res.arrayBuffer())
}

(async () => {

  try {
    // コマンドライン引数を取得
    const text = process.argv[2]
    console.log('Input: ' + text)

    // 住所情報を取得
    const location = await getAddressLocation(text)

    // 降水情報を取得
    const weather = await getWeatherInfo(location)

    // 降水情報をテキストに変換
    const weatherText = getWeatherText(weather, location)
    console.log('Message: ' + weatherText)

    // 地図画像を取得
    const mapImage = await getMapImage(location)

    // 地図画像データをファイルに出力
    await fs.promises.writeFile('map.png', mapImage)

  } catch (err) {
    console.error(err)
  }
})()
```

## 実行結果

```
$ node app.js 福島市
Input: 福島市
{
  "ResultInfo": {
    "Count": 1,
    "Total": 1,
    "Start": 1,
    "Status": 200,
    "Description": "",
    "Copyright": "",
    "Latency": 0.016
  },
  "Feature": [
    {
      "Id": "07201",
      "Gid": "",
      "Name": "福島県福島市",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.47469440,37.76089730",
        "BoundingBox": "140.22998100,37.62433900 140.57092800,37.97664700"
      },
      "Category": [],
      "Description": "",
      "Style": [],
      "Property": {
        "Uid": "09564f93998c22a98d1921dbd866d75b73f7bd6a",
        "CassetteId": "b22fee69b0dcaf2c2fe2d6a27906dafc",
        "Yomi": "フクシマケンフクシマシ",
        "Country": {
          "Code": "JP",
          "Name": "日本"
        },
        "Address": "福島県福島市",
        "GovernmentCode": "07201",
        "AddressMatchingLevel": "2",
        "AddressType": "市"
      }
    }
  ]
}
{
  "ResultInfo": {
    "Count": 5,
    "Total": 5,
    "Start": 1,
    "Status": 200,
    "Latency": 0.049625,
    "Description": "",
    "Copyright": "(C) Yahoo Japan Corporation."
  },
  "Feature": [
    {
      "Id": "202009082115_140.47469_37.760897",
      "Name": "地点(140.47469,37.760897)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.47469,37.760897"
      },
      "Property": {
        "WeatherAreaCode": 3610,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    },
    {
      "Id": "202009082115_140.22998_37.624339",
      "Name": "地点(140.22998,37.624339)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.22998,37.624339"
      },
      "Property": {
        "WeatherAreaCode": 3630,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    },
    {
      "Id": "202009082115_140.57093_37.624339",
      "Name": "地点(140.57093,37.624339)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.57093,37.624339"
      },
      "Property": {
        "WeatherAreaCode": 3610,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    },
    {
      "Id": "202009082115_140.22998_37.976647",
      "Name": "地点(140.22998,37.976647)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.22998,37.976647"
      },
      "Property": {
        "WeatherAreaCode": 3520,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    },
    {
      "Id": "202009082115_140.57093_37.976647",
      "Name": "地点(140.57093,37.976647)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.57093,37.976647"
      },
      "Property": {
        "WeatherAreaCode": 3420,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 1.35
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0.75
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    }
  ]
}
Message: 福島県福島市では、1時間以内に雨が降りそうです。
```

![map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/551b9bd0-6deb-12e3-dc7b-d5e4672974f4.png)

## 参考資料

- [YOLP\(地図\):Yahoo\!ジオコーダAPI \- Yahoo\!デベロッパーネットワーク](https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/geocoder.html)
- [YOLP\(地図\):気象情報API \- Yahoo\!デベロッパーネットワーク](https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html)
- [YOLP\(地図\):Yahoo\!スタティックマップAPI \- Yahoo\!デベロッパーネットワーク](https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/static.html)
- [node\-fetch \- npm](https://www.npmjs.com/package/node-fetch)
- [Fetch API \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API)
- [Buffer \| Node\.js v14\.9\.0 Documentation](https://nodejs.org/dist/latest-v14.x/docs/api/buffer.html)
- [File system \| Node\.js v14\.9\.0 Documentation](https://nodejs.org/dist/latest-v14.x/docs/api/fs.html)