「駅すぱあとWebサービス」でいろんな経路探索をしてみる

[国産Web API Advent Calendar 2018](https://qiita.com/advent-calendar/2018/apijp)は「駅すぱあとWebサービス」をご紹介します!!

![EWS_mini.png](https://qiita-image-store.s3.amazonaws.com/0/159912/6b5a4e55-513b-f205-e503-b59cb8911c89.png)

「駅すぱあとWebサービス」は、経路探索サービス「駅すぱあと」の持つ機能や情報をWebAPIで利用できるサービスです。

# 駅すぱあとWebサービスってなにができるの?
**経路探索**はもちろん、以下のような機能を簡単に使うことができます。

* 条件を指定した経路探索
* 定期券関連の機能(「定期券を使ったら運賃はXX円」みたいな)
* 範囲探索(「◯◯駅からX分の駅」みたいな)
* 駅や路線の情報の取得(名称、位置、路線など)
* 時刻表の取得
* 鉄道運行情報の取得
などなど

なんでもござれです。

様々な経路探索サービスや交通費精算サービスなどで使って頂いている他、弊社([ヴァル研究所](https://www.val.co.jp/))が提供しているサービスのバックエンドとしても利用されています。

ドキュメントは[こちら](http://docs.ekispert.com/v1/)
詳しい情報やアクセスキーのお申し込みは[こちら](https://ekiworld.net/service/lp/webservice/)から

申し込み不要で「駅すぱあとWebサービス」を体験できる「API Tester」というツールもあります。↓
[アクセスキー要らず!!API Testerで 駅すぱあとWebサービスに簡単入門する](https://qiita.com/kumatira/items/5771a8a637963aab91f9)

# ただの経路探索だけじゃない
一般に経路探索と言えば、出発駅と到着駅を指定してその間の経路を調べることを想像すると思います。「駅すぱあとWebサービス」ではその他にも様々なアレンジを加えた探索を行うことができます。今回は以下を紹介します。

* 普通の経路探索
* 「普通列車だけで行く」 conditionDetailパラメータ
* 「絶対この列車に乗りたい」 `/course/edit`機能


## まずは普通の経路探索
まずは普通に探索してみましょう。本記事では、弊社(ヴァル研究所)のある高円寺駅から大阪駅をサンプルにしたいと思います。
経路探索機能を示すパス`/v1/{format}/search/course/extreme`に以下のリクエストをGETで投げます。
なお`viaList`パラメータは`東京:大阪`のように駅名を入れることもできますが、駅をより一意に定めるため駅コードの利用を推奨しています。

|パラメータ|値 |備考|
|:-----------------|:------------------|:------------------|
|format|JSON|JSON or XML|
|viaList|22671:25853|出発駅と到着駅の駅コードを指定。駅コードは[駅簡易情報機能](http://docs.ekispert.com/v1/api/station/light.html)などで取得可能。|
|key|-|アクセスキーは[ここ](https://ekiworld.net/service/lp/webservice/)からGet!|

```http:request
https://api.ekispert.jp/v1/json/search/course/extreme?viaList=22828:25853&key={your_accessskey}
```

```json:response

{
  ResultSet: {
    (略)
    Course: [
      {
        searchType: "departure",
        dataType: "onTimetable",
        SerializeData: "VkV4QaECp3DIAsMCpgEz8NIDpgEz8NEEkcIBQwAAAAKmATPw0QPJAQECAQMBBAEHAQgBCgIPQv9_EKX_9wWWwQKlWSzBAqVlesECpWT9wQKlbnvBAqVnnMECpWOSBpLDAQECAQPIAaUqzAIBAwYEFQUPBkMyMUEIAwkGwwEBAgEDxwGlgyUCFwMYBQQGRTM0NTFNBwUIBAeSxgGmATPw2wOlAjoEpQLTBQAGAggAxgGmATPw2wIBA6UC2QSlAt0FAAgACJLFBAEFAQYBB6SGCAHGAgEEAgUBBgEHpLEIAgmRwgIBAwEPkcQBkwABAgKSwwEAAgADAMMBAQIBAwEDksMBAAIAAwDDAQECAQMBBJEA--T3221233232319:F332112212000:A23121141:--855888cf7a611e7b9e380ad0a11f313fcd688655--0--0--0--273",
        Price: (略)
        Route: {
          timeOther: "6",
          timeOnBoard: "157",
          (略)
          Line: [
            {
              (略)
              Name: "ＪＲ中央・青梅線快速・東京行",
              Destination: "東京",
            },
            {
              (略)
              Name: "ＪＲ新幹線のぞみ",
              Destination: "博多",
            },
            {
              （略）
              Name: "ＪＲ京都線新快速・姫路行",
              Destination: "姫路",
            }
          ],
        Point: [
       {
         Station: {
           code: "22671",
           Name: "高円寺",
           (略)
         },
         Prefecture: {
           code: "13",
           Name: "東京都"
         },
         GeoPoint: {
           longi_d: "139.653027",
           lati_d: "35.70211",
           (略)
         }
       },
       {
         Station: {
           code: "22828",
           Name: "東京",
           (略)
         },
         (略)
       },
       {
         Station: {
           code: "25978",
           Name: "新大阪",
           (略)
         },
         (略)
       },
       {
         Station: {
           code: "25853",
           Name: "大阪",
           (略)
         },
         (以下略)
```

返されるJSONが巨大なので色々省きましたが、新幹線のぞみに乗って、新大阪駅でJR神戸線新快速に乗り換えるという結果が返っていることがわかります。

路線図にするとこんな感じです。

<img width="1433" alt="スクリーンショット 2018-12-13 7.11.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/159912/406d4564-57b6-f3b3-a55f-e145597b819a.png">

いいですね〜。ただ、これだけだとただの経路探索です。

## 「普通列車だけで行く」 conditionDetailパラメータ
次は経路探索機能にある。探索に条件をつけるconditionDetailパラメータを利用します。このパラメータには[　ドキュメント](http://docs.ekispert.com/v1/api/search/course/extreme.html)にある通り、[探索条件生成](http://docs.ekispert.com/v1/api/toolbox/course/condition.html)機能で作った「詳細探索条件データ」と呼ばれる文字列を指定します。

### 探索条件生成機能

今回は例として、普通列車(各駅停車や快速を含む乗車券だけで乗れる列車)に絞った探索をしてみましょう。(「青春18きっぷ」が使える列車ですね。)
探索条件生成機能を表す`/v1/{format}/toolbox/course/condition`に以下のリクエストを投げます。
ドキュメントは[こちら](http://docs.ekispert.com/v1/api/toolbox/course/condition.html)

|パラメータ|値 |備考|
|:-----------------|:------------------|:------------------|
|format|JSON|JSON or XML|
|plane|never|飛行機を利用しない設定。|
|highwayBus|never|高速バスを利用しない設定。|
|shinkansen|never|新幹線を利用しない設定。|
|limitedExpress|never|有料特急を利用しない設定。|
|key|-|アクセスキーは[ここ](https://ekiworld.net/service/lp/webservice/)からGet!|
そのほかにも利用の有無を決められるパラメータはありますが、とりあえずこれで

```http:reqest
https://api.ekispert.jp/v1/json/toolbox/course/condition?shinkansen=never&limitedExpress=never&highwayBus=never&plane=never&key={your_accessskey}
```

```json:response
{
  ResultSet: {
    (略)
    Condition: "T1121113232319:F332112212000:A23121141:"
  }
}
```

`$.ResultSet.Condition`要素に「詳細探索条件データ」文字列が帰ってきました。

続いて前項で投げた`/v1/{format}/search/course/extreme`へのリクエストへ以下のパラメータを追加してリクエストします。

### 経路探索機能
では探索してみましょう。前項と同じように経路探索機能を示すパス`/v1/{format}/search/course/extreme`に以下のリクエストをGETで投げます。

|パラメータ|値 |備考|
|:-----------------|:------------------|:------------------|
|format|JSON|JSON or XML|
|viaList|22671:25853|出発駅と到着駅の駅コードを指定。駅コードは[駅簡易情報機能](http://docs.ekispert.com/v1/api/station/light.html)などで取得可能。|
|conditionDetail|T1121113232319:F332112212000:A23121141:|詳細探索条件データ|
|key|-|アクセスキーは[ここ](https://ekiworld.net/service/lp/webservice/)からGet!|

```http:request
https://api.ekispert.jp/v1/json/search/course/extreme?viaList=22828:25853&conditionDetail=T1121113232319:F332112212000:A23121141:&key={your_accessskey}
```

```json:response
{
  ResultSet: {
    apiVersion: "1.27.0.0",
    engineVersion: "201812_03a",
    Course: [
      {
        (略)
        Route: {
          timeOther: "42",
          timeOnBoard: "526",
          transferCount: "7",
          (略)
          Line: [
            {
              Name: "ＪＲ中央・青梅線快速・東京行",
              Destination: "東京",
              (略)
            },
            {
              Name: "ＪＲ東海道本線・熱海行",
              Destination: "熱海",
              (略)
            },
            {
              Name: "ＪＲ東海道本線・沼津行",
              Destination: "沼津",
              (略)
            },
            {
              Name: "ＪＲ東海道本線・浜松行",
              Destination: "浜松",
              (略)
            },
            {
              Name: "ＪＲ東海道本線・豊橋行",
              Destination: "豊橋",
              (略)
            },
            {
              Name: "ＪＲ東海道本線新快速・大垣行",
              Destination: "大垣",
              (略)
            },
            {
              Name: "ＪＲ東海道本線・米原行",
              Destination: "米原",
              (略)
            },
            {
              Name: "ＪＲ琵琶湖線新快速・網干行",
              Destination: "網干",
          (以下略)
```

高円寺駅から7:30に乗れば、大阪駅に15:17に到着します。乗車時間526分、待ち時間42分、7回乗り換え:thinking:
ちょっとお尻が心配ですね。

こちらも路線図にしてみました。

<img width="1435" alt="スクリーンショット 2018-12-13 7.13.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/159912/a0238558-00ca-c7bc-338c-1361f7d92259.png">

確かに在来線を乗りついで大阪へ向かう経路探索ができました!!

##  「絶対この列車に乗りたい」 `/course/edit`機能
最後は1つ目に紹介した新幹線を利用する経路を「改造」する方法です!!

ユーザーからいただくニーズの中に「新幹線や飛行機でどの便に乗るか、乗る前の待ち時間をどのぐらいにするか、は自分で選びたい」というものがあります。

<img width="750" alt="スクリーンショット 2018-12-13 7.32.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/159912/2d9bf884-78e8-107f-769a-981077be5db7.png">
([駅すぱあと for WEB](https://roote.ekispert.net/ja)より)

「駅すぱあとWebサービス」では、上図の2経路目(新幹線の区間)のみ後ろの時間に倒す、すなわち新幹線より前の区間は変えず、新幹線とその後ろの区間は再探索する、といったことを実現することができます。

いくつかの機能を組み合わせていきます。
1. 経路探索機能で元の経路を作る。
2. 平均路線情報機能で得られる時刻表から、乗る列車を特定する。
3. 「経路の再現／定期券・指定列車利用」機能で指定区間の列車を置き換え再探索する。

まずはSTEP1

### 経路探索機能で元の経路を作る
これは1つ目に紹介した新幹線を利用する経路を流用しましょう。
レスポンスの中、`$. ResultSet.Course[*].SerializeData`要素になにやら長い文字列が入っています。これは該当経路をシリアライズ化したもので、この文字列を他の機能で指定することで、経路を使って様々なことをすることができます。今回もその一つです。

### 平均路線情報機能で得られる時刻表から、乗る列車を特定する
続いては平均路線情報機能を利用して、新幹線の区間の時刻表を得て、さらに利用したい列車の「列車等を特定するコード」をゲットします。

まずは平均路線情報機能を示すパス`/v1/{format}/rail`に以下のリクエストを投げます。
ドキュメントは[こちら](http://docs.ekispert.com/v1/api/rail.html)

|パラメータ|値 |備考|
|:-----------------|:------------------|:------------------|
|format|JSON|JSON or XML|
|serializeData|(なんか長い文字列)|元の経路から得られるシリアライズデータ|
|sectionIndex|2|経路のうち、どの区間の時刻表を得たいか指定する。1始まり。新幹線の区間は2区間めのため2を指定。|
|key|-|アクセスキーは[ここ](https://ekiworld.net/service/lp/webservice/)からGet!|

```http:request
https://api.ekispert.jp/v1/json/rail?serializeData={your_serializeData}&sectionIndex=2&key={your_accessskey}
```

```json:response
{
  ResultSet: {
    apiVersion: "1.27.0.0",
    engineVersion: "201812_03a",
    Line: [
      {
        code: "Both:0",
        Name: "ＪＲ新幹線のぞみ",
        Type: {
          text: "train",
          detail: "shinkansen"
        },
        ArrivalState: {
          no: "23・24",
          Datetime: {
            text: "08:22:00+09:00",
            operation: "today"
          }
        },
        Number: "1",
        DepartureState: {
          no: "14",
          Datetime: {
            text: "06:00:00+09:00",
            operation: "today"
          }
        },
        Color: "051102255"
      },
      {
        code: "Both:1",
        Name: "ＪＲ新幹線のぞみ",
        Type: {
          text: "train",
          detail: "shinkansen"
        },
        ArrivalState: {
          no: "23・24",
          Datetime: {
            text: "08:40:00+09:00",
            operation: "today"
          }
        },
        Number: "3",
        DepartureState: {
          no: "15",
          Datetime: {
            text: "06:16:00+09:00",
            operation: "today"
          }
        },
        Color: "051102255"
      },
    (以下略)
```

すると以上のように、その区間の一日の時刻表を得ることができます。確かに朝6時の始発ののぞみから順番に各列車の情報が返っていますね!!

ここでは利用したい列車の`$.ResultSet.Line[*].code`要素に入っている「列車等を特定するコード」を取得します。例えば、12:00に東京駅を出発し、14:33に大阪駅に到着する「のぞみ227号」は`Both:63`が「列車等を特定するコード」になります。(このコードはリクエストごとに変わるのでご注意ください)

### 「経路の再現／定期券・指定列車利用」機能で指定区間の列車を置き換え再探索する
では1で取得した経路に2で取得した列車を利用して、再探索を行いましょう。
「経路の再現／定期券・指定列車利用」機能を示すパス`/v1/{format}/course/edit`に以下のリクエストを投げます。
ドキュメントは[こちら](http://docs.ekispert.com/v1/api/course/edit.html#linecode)

|パラメータ|値 |備考|
|:-----------------|:------------------|:------------------|
|format|JSON|JSON or XML|
|serializeData|(なんか長い文字列)|手順1で得たシリアライズデータ|
|sectionIndex|2|経路のうちどの区間を置き換えたいか指定する。|
|lineCode|(例)Both:63|手順2で得た「列車等を特定するコード」|
|assignInstruction|SearchAfter|再探索をどのように行うかを指定する。今回は置き換え区間のとその後だけを再探索したいので`SearchAfter`を指定。|


```http:request
https://api.ekispert.jp/v1/course/edit?sectionIndex=2&lineCode=Both:63&assignInstruction=SearchAfter&serializeData={your_serializeData}&sectionIndex=2&key={your_accessskey}
```

レスポンスは手順1で使った経路探索機能とほぼ同様なので省略します。1区間目は元の経路、2区間目以降は、置き換えたい列車とそれ以降の最適な列車に置き換わったでしょうか?

本機能は自動で次の列車で再探索など便利な様々な探索が使えます!!その分少し難解なドキュメントになっていますが、読み込んでぜひ活用してください!!

# まとめ
ということでここまで「駅すぱあとWebサービス」でできる様々な経路探索をご紹介しました。最後はかなりマニアックな経路探索にもチャレンジしましたが、「駅すぱあとWebサービス」が経路探索にこだわったサービスだとわかっていただけると嬉しいです!!
この他にも

* 特定の路線を必ず通る
* 特定の路線を通らない
* 定期券を割り当てて経路探索する
* 探索せずに自力で経路を作って(!)、金額だけ調べる

等々経路探索に限っても様々なことができます。

「駅すぱあとWebサービス」には

 * 無料で一部の機能が使えるフリープラン
 * より便利な機能をお手軽に使えるSaaSストアプラン
 * 全ての機能を使えるスタンダードプラン

などいくつかのプランを用意しています。
まずは[入門記事](https://qiita.com/kumatira/items/5771a8a637963aab91f9)などを参考に、経路探索の世界へ飛び込んでみましょう!!