目的
Google Apps ScriptとGoogle Maps Platformの勉強のため、GoogleスプレッドシートとGoogle Distance Matrix APIを用いて、Google交通情報(旅行時間)を抽出ツールを作成してみました。
Distance Matrix API
-
今回は、Google Maps Platformの中でも「ルート」に分類される「Distance Matrix API」を使用しています。
-
Distance Matrix APIは、複数の目的地について移動時間と距離を計算します。
-
Distance Matrix APIでは、移動モードを指定することができ(今回は自動車)、出発地・目的地の座標と出発予定時刻をHTTPリクエストすると旅行時間と距離をHTTPレスポンス(JSON形式)として受け取ることができます。
-
出発予定時刻は、現在の時刻または将来の時刻を設定することができ、過去の時刻を設定することはできません。
-
現在の時刻を設定すると限りなく現在に近い交通状況を考慮した旅行時間を取得することができます。一方で、未来の時刻を設定すると過去と現在の交通状況を考慮した旅行時間を取得することができます。
※今回は、出発予定時刻に未来の時刻を設定した場合の旅行時間を抽出します。
Google Apps Script
- Google Apps Scriptは、Google社が提供するJavaScriptベースのプログラミング言語であり、クラウドサーバ上で動作します。
- Gmail、Googleスプレッドシート、Googleカレンダー、Googleドライブ、Google翻訳などのGoogleが提供する数々のアプリケーション群をプログラミングにより操作可能です。
実行環境
この記事ではGoogle Apps Scriptを使っています。
コードを実行するためにはGoogle Maps PlatformのAPIキーが必要です。
※Google Maps Platform APIキーの取得・発行についてはこちらを参照してください。
https://www.zenrin-datacom.net/business/gmapsapi/api_key/index.html
前提条件
対象区間
旅行時間を抽出する区間は、国土交通省の渋滞ランキングのとりまとめ(平成29年 年間・GW)の**一般国道の年間渋滞1位の国道16号呼塚交差点~大井交差点(千葉県柏市)**を対象としました。
抽出条件
旅行時間の抽出条件は、以下のとおりとしました。
出発予定時刻:2019/03/18 06:00:00
データ取得間隔:15分
終了予定時刻:2019/03/18 19:00:00
実行方法
入力設定
- Googleスプレッドシートのlatlngシートにjunction,direction,lat,lngを入力してください。※必ず上り・下り別に入力してください。
- latとlngの取得方法は、こちらの記事を参照ください。
- なお、latとlngは、Googleマップの検索ルート上の座標を正確に入力する必要があります(正確ではない場合、旅行時間の取得が上手くいかないようです)。
- 抽出結果を出力するresultシートを追加してください。
実行
-
Googleスプレッドシートのツール>スクリプトエディタを開いて、下記のソースを貼り付け、基本的な設定を行ったあと、スクリプトを実行してください。
-
Googleスプレッドシートへのアクセス権の確認画面が出ますので許可してください。
基本的な設定
- APIキーを入力してください。
- 出発予定時刻(Unixtime)を入力してください。
- データ取得間隔(タイムインターバル)を入力してください。
- 終了予定時刻(Unixtime)を入力してください。
function CalcBetweenTwoPointsSpeed() {
//基本的な設定
//**********************************************************************************************
// APIKEYを設定する
var APIKEY = "APIキーを入力してください"
// 出発予定時刻(Unixtime(UTC))を設定する
var departure_time_unix = 1552856400 // 2019/03/18 06:00:00
// タイムインターバルを設定する
var time_interval = 15 // 分単位
// 終了予定時刻(Unixtime(UTC))を設定する
var finish_time_unix = 1552903200 // 2019/03/18 19:00:00
//**********************************************************************************************
// latlngシートの内容を配列に格納する
var bk = SpreadsheetApp.getActiveSpreadsheet(); // アクティブなスプレッドシートを取得する
var sh = bk.getSheetByName('latlng'); // latlngシートを取得する
var rowLst = sh.getLastRow(); // 最終行を取得する
var colLst = sh.getLastColumn(); // 最終列を取得する
/*
Logger.log(rowLst);
Logger.log(colLst);
*/
// 方向別データ数のカウント
var inCnt = 0 ;
var outCnt = 0 ;
for (var i = 2; i <= rowLst; i++) {
if (sh.getRange(i, 2).getValues() == "inbound"){
inCnt = inCnt + 1;
}
if (sh.getRange(i, 2).getValues() == "outbound"){
outCnt = outCnt + 1;
}
}
/*
Logger.log(inCnt);
Logger.log(outCnt);
*/
// データを2次元配列に格納する
var ArrInLst=sh.getRange(2,1,inCnt,colLst).getValues();
var ArrOutLst=sh.getRange(2+inCnt,1,outCnt,colLst).getValues();
/*
Logger.log(ArrInLst);
Logger.log(ArrOutLst);
*/
// ループ処理
var loopNumber = ( finish_time_unix - departure_time_unix ) / ( time_interval * 60 )
// Logger.log(loopNumber);
for (var k = 1; k <= loopNumber ; k++) {
var d = new Date( departure_time_unix * 1000 );
var year = d.getFullYear();
var month = d.getMonth() + 1;
var day = d.getDate();
var hour = ( '0' + d.getHours() ).slice(-2);
var min = ( '0' + d.getMinutes() ).slice(-2);
var sec = ( '0' + d.getSeconds() ).slice(-2);
var departure_time_jst = year + '/' + month + '/' + day + ' ' + hour + ':' + min + ':' + sec; // jst
//***********************
// inboundの処理
//***********************
var ArrInRes = [];
// Distance Matrix APIの呼び出し
for (var i = 0; i <= inCnt-2; i++) {
// 緯度・経度を取得する
var Lat1 = ArrInLst[i][2]; // 出発地緯度
var Lon1 = ArrInLst[i][3]; // 出発地経度
var Lat2 = ArrInLst[i+1][2]; // 到着地緯度
var Lon2 = ArrInLst[i+1][3]; // 到着地経度
// HTTPリクエストを行う
var url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric" +
"&departure_time=" + departure_time_unix + // 出発予定時刻(Unixtime(UTC))
"&traffic_model=" + "best_guess" + // 最適な旅行時間
"&origins=" + Lat1 + "," + Lon1 + // 出発地緯度経度
"&destinations=" + Lat2 + "," + Lon2 + // 到着地緯度経度
"&key=" + APIKEY; // APIKEY
Logger.log(url);
var res = UrlFetchApp.fetch(url,{muteHttpExceptions:true}); // HTTPリクエストを行う
Logger.log(res);
var json = JSON.parse(res.getContentText()); // HTTPレスポンスを文字列として取得、JSON形式の文字列を解析してオブジェクトとして返す
Logger.log(json);
if (json["status"] == "OK") {
var str_dist = json["rows"][0]["elements"][0]["distance"]["value"];
var str_dura = json["rows"][0]["elements"][0]["duration"]["value"];
var str_dura_traff = json["rows"][0]["elements"][0]["duration_in_traffic"]["value"];
var dbl_spd = ( str_dist / 1000 ) / ( str_dura / 3600 )
var dbl_spd_traff = ( str_dist / 1000 ) / ( str_dura_traff / 3600 )
ArrInRes[i] = [];
ArrInRes[i][0] = departure_time_jst;
ArrInRes[i][1] = str_dist;
ArrInRes[i][2] = str_dura;
ArrInRes[i][3] = dbl_spd;
ArrInRes[i][4] = str_dura_traff;
ArrInRes[i][5] = dbl_spd_traff;
}
}
// 計算結果をシートに出力する
var sh = bk.getSheetByName('result'); // resultシートを取得する
var rowLst_re = sh.getLastRow(); // 最終行を取得する
// Logger.log(rowLst_re);
for (var i = 0; i <= inCnt-2; i++) {
// 値をセルに入力する
sh.getRange(i+rowLst_re+1, 1).setValue(ArrInLst[i][0]); // from
sh.getRange(i+rowLst_re+1, 2).setValue(ArrInLst[i+1][0]); // to
sh.getRange(i+rowLst_re+1, 3).setValue(ArrInLst[i][1]); // direction
sh.getRange(i+rowLst_re+1, 4).setValue(ArrInLst[i][2]); // y
sh.getRange(i+rowLst_re+1, 5).setValue(ArrInLst[i][3]); // x
sh.getRange(i+rowLst_re+1, 6).setValue(ArrInLst[i+1][2]); // y
sh.getRange(i+rowLst_re+1, 7).setValue(ArrInLst[i+1][3]); // x
sh.getRange(i+rowLst_re+1, 8).setValue(ArrInRes[i][0]); // time
sh.getRange(i+rowLst_re+1, 9).setValue(ArrInRes[i][1]); // distance
sh.getRange(i+rowLst_re+1, 10).setValue(ArrInRes[i][2]); // duration
sh.getRange(i+rowLst_re+1, 11).setValue(ArrInRes[i][3]); // speed
sh.getRange(i+rowLst_re+1, 12).setValue(ArrInRes[i][4]); // duration_in_traffic
sh.getRange(i+rowLst_re+1, 13).setValue(ArrInRes[i][5]); // speed
}
//***********************
// outboundの処理
//***********************
var ArrOutRes = [];
// Distance Matrix APIの呼び出し
for (var i = 0; i <= inCnt-2; i++) {
// 緯度・経度を取得する
var Lat1 = ArrOutLst[i][2]; // 出発地緯度
var Lon1 = ArrOutLst[i][3]; // 出発地経度
var Lat2 = ArrOutLst[i+1][2]; // 到着地緯度
var Lon2 = ArrOutLst[i+1][3]; // 到着地経度
// HTTPリクエストを行う
var url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric" +
"&departure_time=" + departure_time_unix + // 出発予定時刻(Unixtime(UTC))
"&traffic_model=" + "best_guess" + // 最適な旅行時間
"&origins=" + Lat1 + "," + Lon1 + // 出発地緯度経度
"&destinations=" + Lat2 + "," + Lon2 + // 到着地緯度経度
"&key=" + APIKEY; // APIKEY
Logger.log(url);
var res = UrlFetchApp.fetch(url,{muteHttpExceptions:true}); // HTTPリクエストを行う
Logger.log(res);
var json = JSON.parse(res.getContentText()); // HTTPレスポンスを文字列として取得、JSON形式の文字列を解析してオブジェクトとして返す
Logger.log(json);
if (json["status"] == "OK") {
var str_dist = json["rows"][0]["elements"][0]["distance"]["value"];
var str_dura = json["rows"][0]["elements"][0]["duration"]["value"];
var str_dura_traff = json["rows"][0]["elements"][0]["duration_in_traffic"]["value"];
var dbl_spd = ( str_dist / 1000 ) / ( str_dura / 3600 )
var dbl_spd_traff = ( str_dist / 1000 ) / ( str_dura_traff / 3600 )
ArrOutRes[i] = [];
ArrOutRes[i][0] = departure_time_jst;
ArrOutRes[i][1] = str_dist;
ArrOutRes[i][2] = str_dura;
ArrOutRes[i][3] = dbl_spd;
ArrOutRes[i][4] = str_dura_traff;
ArrOutRes[i][5] = dbl_spd_traff;
}
}
// 計算結果をシートに出力する
var sh = bk.getSheetByName('result'); // resultシートを取得する
var rowLst_re = sh.getLastRow(); // 最終行を取得する
// Logger.log(rowLst_re);
for (var i = 0; i <= outCnt-2; i++) {
// 値をセルに入力する
sh.getRange(i+rowLst_re+1, 1).setValue(ArrOutLst[i][0]); // from
sh.getRange(i+rowLst_re+1, 2).setValue(ArrOutLst[i+1][0]); // to
sh.getRange(i+rowLst_re+1, 3).setValue(ArrOutLst[i][1]); // direction
sh.getRange(i+rowLst_re+1, 4).setValue(ArrOutLst[i][2]); // y
sh.getRange(i+rowLst_re+1, 5).setValue(ArrOutLst[i][3]); // x
sh.getRange(i+rowLst_re+1, 6).setValue(ArrOutLst[i+1][2]); // y
sh.getRange(i+rowLst_re+1, 7).setValue(ArrOutLst[i+1][3]); // x
sh.getRange(i+rowLst_re+1, 8).setValue(ArrOutRes[i][0]); // time
sh.getRange(i+rowLst_re+1, 9).setValue(ArrOutRes[i][1]); // distance
sh.getRange(i+rowLst_re+1, 10).setValue(ArrOutRes[i][2]); // duration
sh.getRange(i+rowLst_re+1, 11).setValue(ArrOutRes[i][3]); // speed
sh.getRange(i+rowLst_re+1, 12).setValue(ArrOutRes[i][4]); // duration_in_traffic
sh.getRange(i+rowLst_re+1, 13).setValue(ArrOutRes[i][5]); // speed
}
departure_time_unix = departure_time_unix + time_interval * 60
}
}
実行結果
- 旅行時間は、L列のduration_in_traffic(s)になります。
- 旅行速度は、M列のspeed_in_traffic(km/h)になります。
- ※旅行速度は、I列のdistance(m)とL列のduration_in_traffic(s)から求めています。
※背景地図はOpenStreetMapを使用しています。
留意事項
- Google Maps PlatformのAPIを利用するには、APIキーが必須であり、無料枠内(1ヵ月 200ドルの無料クレジット)で利用するのにも請求先アカウント(クレジットカード)の設定が必要です。
- Distance Matrix APIは、交通状況を考慮しないDistance Matrixと交通状況を考慮するDistance Matrix Advancedに大別されます。
- 今回使用したDistance Matrix APIは、Distance Matrix Advancedになります。
- Distance Matrix Advancedの価格設定は以下のとおりです。
- 0-100,000ELEMENT/月(10.00米ドル/1,000ELEMENT)
- 100,001-500,000ELEMENT/月(8.00米ドル/1,000ELEMENT)
- 500,000-ELEMENT/月(問い合わせ)
- ※ELEMENTは、APIで送信されるoriginsとdestinationsの要素数のことです。
- したがって、無料枠(200米ドル)を最大限利用した場合、1ヶ月あたりに利用できるのは、20,000ELEMENT(リクエスト)となります。
※無料枠を超えてAPIを使用すると課金されますのでご注意ください(>_<)
今後の課題
参考書
詳解! GoogleAppsScript完全入門 ~GoogleApps & G Suiteの最新プログラミングガイド~
Developer Guide(Distance Matrix API)
次回の投稿予定
- 出発予定時刻を現在の時刻にした場合の旅行時間の抽出ツール
- 基本的にはDistance Matrix APIを用いますが、現在時刻の設定にはGoogle Apps Scriptのトリガー機能を用います。
または
になる予定です。