第4回:  Google Maps Platform, Directions APIによる地点間情報取得(巡回セールスマン問題への下準備)

- 東京都、大阪府、名古屋県、福岡県、広島県、香川県の6都市間の距離、移動時間をGoogle MapsからDiretions APIを用いて取得する。
- また、各都市の座標はgeocoderを用いて取得する。
- これは、アニーリングを用いた巡回セールスマン問題を解くための下準備。

**前回**：[第3回: アニーリング基礎3. クラスタリング問題、2次元スピンの1次元化](https://qiita.com/opt_d/items/38830f0360af4db17484)
**参考**：[Pythonで地名から住所と座標を割り出すジオコーディングを試してみる](https://medium.com/shimakaze-soft-techblog/python%E3%81%A7%E5%9C%B0%E5%90%8D%E3%81%8B%E3%82%89%E4%BD%8F%E6%89%80%E3%81%A8%E5%BA%A7%E6%A8%99%E3%82%92%E5%89%B2%E3%82%8A%E5%87%BA%E3%81%99%E3%82%B8%E3%82%AA%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%92%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B-61fcaeb5a093), [公式チュートリアル](https://developers.google.com/maps/documentation/directions/get-directions),[Qiita: Google Map API で経路情報を取得する](https://qiita.com/keijipoon/items/0394dab6b434a34a6ae7)
**Keyword**：*Google Maps Platform*, *Directions API* 

### 1. geocoderによる座標の取得
尚、geocoderの使い方は、[Pythonで地名から住所と座標を割り出すジオコーディングを試してみる](https://medium.com/shimakaze-soft-techblog/python%E3%81%A7%E5%9C%B0%E5%90%8D%E3%81%8B%E3%82%89%E4%BD%8F%E6%89%80%E3%81%A8%E5%BA%A7%E6%A8%99%E3%82%92%E5%89%B2%E3%82%8A%E5%87%BA%E3%81%99%E3%82%B8%E3%82%AA%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%92%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B-61fcaeb5a093)を参考にした。

#### 1.1 geocoderのインストール
```Python
pip install geocoder
```

#### 1.2 座標の取得
始めに、場所を指定すれば、緯度、経度を返す関数を作成する。
```Python
import geocoder
def _latlng(_location):
  _ans = geocoder.osm(_location, timeout=1.0)
  ### geocoderから情報を取得できたか確認
  if _ans.ok == True:
    ### 取得した結果を辞書に変換
    _ans = _ans.json
    ### 後でgoogle map apiに渡すことを考え、[,]を[%2C]に置き換えて、緯度経度を並べる。
    _latlng = str(_ans["lat"]) +"%2C"+str(_ans["lng"])
  else:
    _latlng = "error"
    print("Error: ロケーションが見つかりません。")
  return _latlng
```
続いて、指定の都市の座標を取得する。
```Python
### 取得したい座標の都市をリスト化
target_city = ["東京都","大阪府","名古屋県","福岡県","広島県","香川県"]
_N = len(target_city)

### 定義した緯度経度取得するlatlng関数で緯度経度に変換する
_target_latlng = [_latlng(_tar) for _tar in target_city]
_target_latlng
```
```
<Output>
['35.6828387%2C139.7594549',
 '34.6198813%2C135.490357',
 '35.1706972%2C136.8816286',
 '33.6251241%2C130.6180016',
 '34.5666725%2C132.753565',
 '34.2480104%2C134.0586579']
```

### 2. Google / Directions APIによる地点間情報取得
尚、apiの使い方は、[公式チュートリアル](https://developers.google.com/maps/documentation/directions/get-directions)、および[Qiita: Google Map API で経路情報を取得する](https://qiita.com/keijipoon/items/0394dab6b434a34a6ae7)を参考にした。

#### 2.1 移動距離と移動時間取得する関数の定義
```Python
###ライブラリのインポート
import requests

###APIコード
google_api_key = "AIzaSyCXLky3XXXXXXXXXXXXXXXXXX" #APIキーは各自取得願います。

###移動距離と移動時間を取得する関数の定義
def _google_direction(_origin,_destination,_mode="driving"):
  ###APIリクエストの作成
  _mode = "driving"
  _url = f"https://maps.googleapis.com/maps/api/directions/json?destination={_destination}&origin={_origin}&mode={_mode}&key={google_api_key}"

  ###APIデータの取得
  _response = requests.get(_url).json()

  ###移動距離、移動時間の取得
  #Jsonファイルからルート情報の取得
  _route = _response["routes"][0]["legs"][0]
  #移動距離の抽出
  _distance = int(_route["distance"]["value"])/1000
  #移動時間の抽出
  _duration = int(_route["duration"]["value"])/60
  return[_distance,_duration]
```
#### 2.2 地点間に移動距離と移動時間の取得
都市間の移動距離、移動時間を$6×6$の行列で表現する。同じ都市は移動できない為、対角成分は0となる。また、上下三角行列は略同じ数値となる。計算結果確認の為、とりあえず、対角成分を除くすべての成分の計算を行う。
```Python
###ライブラリのインポート
import numpy as np

###出力用の行列を準備
_distance_inf = np.zeros(_N**2).reshape(_N,_N)
_duration_inf = np.zeros(_N**2).reshape(_N,_N)

###組み合わせ事に移動距離、移動時間を取得する
for _x in range(_N):
  for _y in range(_N):
    if not _x == _y:
      _result = _google_direction(_target_latlng[_x],_target_latlng[_y],"driving")
      #print(f"{_x}:{target_city[_x]}-{_y}:{target_city[_y]}:{_result}")
      _distance_inf[_x][_y] = round(_result[0],0)
      _duration_inf[_x][_y] = round(_result[1],0)

###テスト
_distance_inf
_durantion_inf
```
```
<Output>
array([[   0.,  505.,  347., 1073.,  779.,  685.],
       [ 504.,    0.,  176.,  604.,  310.,  200.],
       [ 348.,  176.,    0.,  744.,  450.,  356.],
       [1073.,  607.,  744.,    0.,  319.,  475.],
       [ 778.,  312.,  450.,  315.,    0.,  181.],
       [ 684.,  198.,  356.,  475.,  181.,    0.]])

array([[  0., 368., 258., 781., 580., 496.],
       [366.,   0., 146., 457., 255., 159.],
       [256., 146.,   0., 559., 358., 274.],
       [780., 461., 560.,   0., 268., 370.],
       [576., 256., 356., 264.,   0., 165.],
       [494., 162., 274., 368., 167.,   0.]])
```
結果を見ると。上下三角行列に於いて、若干の誤差が出ているが、往路、復路で若干の距離の違い、所要時間の違いは発生するものと考えられる。最後に、地点リストを入力すれば、距離一覧と所要時間一覧を返す一連の関数にまとめる。

### 3. 関数化
```Python
###ライブラリのインポート
import geocoder
import requests
import numpy as np

###Google API Keyの設定
google_api_key = "AIzaSyCXLky3XXXXXXXXXXXXXXXXXX" #APIキーは各自取得願います。

target_city = ["東京都","大阪府","名古屋県","福岡県","広島県","香川県"]

#===座標取得関数
def _latlng(_location):
  _ans = geocoder.osm(_location, timeout=1.0)
  ### geocoderから情報を取得できたか確認
  if _ans.ok == True:
    ### 取得した結果を辞書に変換
    _ans = _ans.json
    ### 後でgoogle mapに代入しやすくするために、検索用のフォーマットに変換する。
    _latlng = str(_ans["lat"]) +"%2C"+str(_ans["lng"])
  else:
    _latlng = "error"
    print("Error: ロケーションが見つかりません。")
  return _latlng

#==移動距離と移動時間を取得する関数
def _google_direction(_origin,_destination,_mode="driving"):
  ###APIリクエストの作成
  _mode = "driving"
  _url = f"https://maps.googleapis.com/maps/api/directions/json?destination={_destination}&origin={_origin}&mode={_mode}&key={google_api_key}"

  ###APIデータの取得
  _response = requests.get(_url).json()

  ###移動距離、移動時間の取得
  #Jsonファイルからルート情報の取得
  _route = _response["routes"][0]["legs"][0]
  #移動距離の抽出
  _distance = int(_route["distance"]["value"])/1000
  #移動時間の抽出
  _duration = int(_route["duration"]["value"])/60
  return[_distance,_duration]

def get_dist_time(*target_city):
  ###対象とする都市数の取得
  _N = len(target_city)

  ###出力用の行列を準備
  _distance_inf = np.zeros(_N**2).reshape(_N,_N)
  _duration_inf = np.zeros(_N**2).reshape(_N,_N)

  ### 定義した緯度経度取得するlatlng関数で緯度経度に変換する
  _target_latlng = [_latlng(_tar) for _tar in target_city]
  _target_latlng

  ###組み合わせ事に移動距離、移動時間を取得する
  for _x in range(_N):
    for _y in range(_N):
      if not _x == _y:
        _result = _google_direction(_target_latlng[_x],_target_latlng[_y],"driving")
        #print(f"{_x}:{target_city[_x]}-{_y}:{target_city[_y]}:{_result}")
        _distance_inf[_x][_y] = round(_result[0],0)
        _duration_inf[_x][_y] = round(_result[1],0)
  _result =[_distance_inf,_duration_inf]
  
  ###出力
  return _result

###テスト
get_dist_time(*["大阪","東京","北海道"])
```
```<Output>
[array([[   0.,  501., 1633.],
        [ 501.,    0., 1313.],
        [1811., 1300.,    0.]]),
array([[   0.,  363., 1462.],
        [ 362.,    0., 1122.],
        [1473., 1121.,    0.]])]
```
### おわりに
都市間の移動距離と移動時間を抽出する関数が整備できたので、続いて本番の巡回セールスマン問題を解いてみる。

今後の課題 : 巡回セールスマン問題
積み残し: 補助スピン$y_{i}$の活用, TTSを用いた探索