GASで自宅から近くて安くで行ける会場を調べてみた

# はじめに
とある試験を受けるために会場を選ばなくてはなりませんでしたが、
候補が多すぎるし、会場が一体どこにあるのかもわかりませんでした。
そこで自宅から近くて安くで行ける会場を調べるためにGAS (Google Apps Script)を使うことにしました。
GASを使うのは初めてなので、指摘とかあればどんどんお願いします！ :bow: 

まず、Google spreadsheetの1列目に会場をコピペしてリストアップしました。
2列目は交通費、3列目は所要時間とし、これらをGASを使って自動で取ってくるようにしました。
自動で取ってくるとは言ってもボタン1つですべての会場への所要時間と交通費を埋めてくれるわけではありません！
custom functionとしてそれぞれのデータを取得できるようにしただけです。（これだけで十分だと思ったので！）

# GASの実装

交通費と所要時間を取得するのに、`DirectionFinder`クラスを使いました。
`DirectionFinder`クラスからA地点からB地点までの経路や交通費、所要時間などの情報を取得できます。
なので方針としては、
1. 出発地と目的地などを設定してDirectionクラスのインスタンスを生成し、経路情報を取得
2. 経路情報から交通費、所要時間を取得する
という流れです。
今回、交通費と所要時間はspreadsheet上では別の列となっているので、
custom functionも交通費取得用と所要時間取得用のものをそれぞれ別で用意します。

## DirectionFinderクラスのインスタンス生成&経路情報取得
`getDirection`は出発地(`from`)、目的地(`to`)、出発時刻(`departureTime`: 文字列)を受け取ります。
`Maps.newDirectionFinder()` でインスタンスを生成してこれらの引数をセットしていきます。
`.setMode(Maps.DirectionFinder.Mode.TRANSIT)`は移動手段を公共交通に設定しています。
最後に`.getDirections()`を呼ぶことで経路情報を含んだJSONを取得しています。

ちなみにデフォルトでは`.getDirections()`は複数ある候補の中でもっともランクが高い経路のみを返します。
この挙動を変更したい場合は、`.setAlternatives(true)`を呼べばよいです。


```javascript
function getDirections(from, to, departureTime) {
    return Maps.newDirectionFinder()
    .setOrigin(from)
    .setDestination(to)
    .setDepart(new Date(departureTime))
    .setMode(Maps.DirectionFinder.Mode.TRANSIT)
    .getDirections();
}
```

## 交通費を計算する関数
`getCost`関数で`getDirections`で得られた経路情報をもとに交通費を算出します。
経路情報はJSONの`routes`に配列として持っています。
なので配列の要素それぞれの交通費を足し合わせて全体の交通費を求めています。
経路のエッジによっては交通費がない可能性もあるので三項演算子で0にバックオフしています。
あとここで注意したいのは、関数をcustom functionとしてspreadsheet内で使用したい場合は、
**コメントに`@customfunction`を記述する必要があります**

```javascript
/**
Returns cost to get from A to B departing at some specific time

@customfunction
*/
function getCost(from, to, departureTime) {
    var directions = getDirections(from, to, departureTime)
    var cost = directions.routes.reduce(
      function(total, route) {
        return (route.fare ? route.fare.value: 0) + total
      } , 0)
    return cost
}
```
## 所要時間を計算する関数
`getDuration`関数で`getDirections`で得られた経路情報をもとに所要時間を算出します。
所要時間は`routes[i].legs`の中にあります。
経路はサブ経路からなりますが、サブ経路はさらに複数の区間(leg)を持っています。
ただ、経由地点を指定しない限りはlegsの要素は1なので、あんまり気にしなくてもいいと思います。

```javascript
/**
Returns duration to get from A to B departing at some specific time

@customfunction
*/
function getDuration(from, to, departureTime) {
    var directions = getDirections(from, to, departureTime)
    var duration = directions.routes.reduce(
      function(total, route) {
        return route.legs.reduce(
          function(total, leg) {
            return total + leg.duration.value
          }, 0
        )
      }, 0
    )
    return duration / 60
}
```

custom functionを実際に使ってみたときの画面です(実際はもっと会場候補は多いです！)。
最初のエントリーだけ手動入力となりますが、
それ以降のエントリーはドラッグしていくだけでよいので非常に楽になりました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/87888/71b61bdb-37a9-c337-ebc7-f38f2b602073.png)

注意点としては候補が多い場合API呼び出しの制限がかかってしまいエラーになることがあります。
なので、`Utilities.sleep`などで呼び出しに間をもたせたほうがいいかもしれません。

# まとめ
GASを使って自宅から近くて安くで行ける会場を調べてみました。
GASは初めてだったんですが、所感としてはこんな感じです！

* :thumbsup:  Googleアプリに認証なしでアクセスできるので楽。認証のコードとか書く必要がない。
* :thumbsdown: javascriptでは書けるとはいってもアロー関数 (`const f = () => {...}`)などES6の機能が使えないっぽくて書きづらかった。(追記： [clasp](https://github.com/google/clasp/blob/master/docs/typescript.md)を使えばtypescriptで書けるみたいです)

GASでできることは無限にありそうなので、これからも色々おもしろいもの実装していきたいと思います！