夜田たんの知的生活実践

ほそく・ながく・ゆるく続くブログを目指します(だいたい週一で更新中)

ライフログの現在地取得を見直し

man looking at map

Photo by Annie Spratt on Unsplash

IFTTT+Googleスプレッドシート+Google Apps Engine(GAS)で、移動ログ(ジオログ)を作ることにしました。

というのも、だいぶ前からSwarmのフィードがサービス終了してしまっていました。
そのため、以前書いた記事のとおりにSwarmで非公開チェックインしても、IFTTTで引っかけられるトリガーがありません。

どうにか楽に自分の足跡を記録しておけないか……と考えて、今回のようなものを作った次第です。

全体の流れ

こんな感じのものを作ります。

  1. IFTTTでボタンウィジェットを作って、スマホでボタンを押したときに「時間・緯度・経度」の3つをGoogleスプレッドシートに書き込む
  2. 1日の終わりに、IFTTTのWebhookでGASのスクリプトを呼び出す
  3. スクリプトでは、以下のことをする
    i. 書き込まれた緯度・経度から、住所と地図上の位置を割り出す
    ii. シートの内容をEvernoteへ送信する
    iii. シートの内容をクリアする

IFTTTのアプレット

新たに2個用意します。

ボタンウィジェット

setting on IFTTT

ボタンを押すと、「location」という名前のスプレッドシートに現在時刻・緯度・経度が追記されます。

location records on google spreadsheet

スプレッドシートの中身はこんな感じです。
1行目の見出しは、あらかじめ手入力しておきます。

Webhook

setting on IFTTT

23時45分になったら動くようにしておきます。
URLは、スクリプトをデプロイするときに発行されたものです(後述)。
ここでMethodを「Get」にしておきます。

スクリプト

はじめてGASを触ってみました。
個人的にJavaScriptに強烈な苦手意識があるので、おっかなびっくりやっています。
先述した「location」シートの「ツール」メニューから「スクリプトエディタ」を開いてコードを書いていきます。

function doGet(e){
  if (SpreadsheetApp.getActiveSheet().getLastRow() > 1){
    var maps = setAddress();
    sendToEvernote(maps);
    clearAll();
  }
}

// references: https://developers.google.com/apps-script/reference/maps/geocoder
function setAddress() {
  const ADDRESS_NUM = 3; // とりあえず住所は3候補ぐらいとる
  var geocoder = Maps.newGeocoder()
    .setLanguage('ja-JP')
    .setRegion('ja');
  var addressArr = [];
  var mapArr = [];

  // 緯度・経度を表から取得(1行目はヘッダなので飛ばす)
  var sheet = SpreadsheetApp.getActiveSheet();
  var latlngs = sheet.getRange(2, 2, sheet.getLastRow() - 1, 2).getValues();
  for (var i = 0; i < latlngs.length; i++){
    // 緯度・経度
    var lat = latlngs[i][0];
    var lng = latlngs[i][1];

    // 緯度・経度にマーカーを打つ
    var map = Maps.newStaticMap()
      .setLanguage('ja-JP')
      .setFormat(Maps.StaticMap.Format.PNG);
    map.addMarker(lat, lng);

    // 緯度・経度から住所割り出し
    var response = geocoder.reverseGeocode(lat, lng);
    var address = [];
    for (var j = 0; j < ADDRESS_NUM; j++) {
      address.push(response.results[j].formatted_address);
      //Logger.log(response.results[j]);
    }
    // それぞれ配列に格納
    addressArr.push(address);
    mapArr.push(map.getBlob());
  }
  // 住所配列をセルに書き込み
  sheet.getRange(2, 4, addressArr.length, addressArr[0].length).setValues(addressArr);
  // 地図配列を戻す
  return mapArr;
}


// references: https://developers.google.com/apps-script/reference/gmail/gmail-app
function sendToEvernote(maps){
  const MAIL_ADDRESS = "hogehoge@m.evernote.com";
  const TITLE = "DailyLog @lifelog +";
  const SENDER_NAME = "GAS geocode";
  var today = new Date();
  const BODY = "";
  var html_body = "";

  var sheet = SpreadsheetApp.getActiveSheet();
  var data = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();

  html_body +=  "<h1>Locations of " + today.getFullYear() + "/" + (today.getMonth()+1) + "/" + today.getDate() + "</h1>";
  // 表に整形
  html_body += "<table>";
  for (var i = 0; i < data.length; i++){
    html_body += "<tr>";
    for (var j = 0; j < data[i].length; j++){
      html_body += "<td>" + data[i][j] + "</td>";
    }
    // 地図画像を挿入
    if(i != 0){
      html_body += "<td><img src ='cid:map" + (i-1) + "'></td>";
    } else {
      html_body += "<td> </td>";
    }
    html_body += "</tr>";
  }
  html_body += "</table>";

  // 地図の一覧を作る
  var mapCidDic = {};
  for(var i = 0; i < maps.length; i++){
    mapCidDic["map" + i] = maps[i];
  }

  // メール送信
  GmailApp.sendEmail(MAIL_ADDRESS, TITLE, BODY,
                     {htmlBody: html_body,
                      inlineImages: mapCidDic,
                      name: SENDER_NAME});
}

function clearAll(){
  var sheet = SpreadsheetApp.getActiveSheet();
  sheet.clear();
  sheet.appendRow(["date","latitude","longitude","address"]);
}

doGet関数から各処理を呼び出しています。
「公開」メニューから「Webアプリケーションとして導入」を選んでデプロイすると、実行用のURLが発行されます。
ここで、実行権限をデフォルトの「Me」から「Anyone, even anonymous」に設定しておけば、IFTTTからでも実行可能になります。

特に工夫していることはないのですが、データは配列に格納してから処理するようにして、シートへの読み書きを最小限にしています。

当初は、各関数内で処理を完結させるために、地図画像をシートに一旦貼り付けて、それを再度読み込んでEvernoteへ送信、とやる予定でした。
しかし、シートに貼り付けた画像(OverGridImageクラス)はBlob型に倒せないようなので、地図のBlob配列を関数の間で渡すようになっています。

reverseGeocodeメソッドでは、例えば自宅などで試すと11個ぐらいの住所が取得できます。
しかし、後半は「日本、(都道府県名)」のようにどんどん大雑把になってくるので、今回は前半3つを取得することにしました。

hogehoge@m.evernote.comのところはEvernote投稿用アドレスです。
実際のコードには自分のアカウントに設定されているアドレスを入れています。

実行結果

locations with maps on Evernote

Evernoteに側の表示はこんな感じになりました。
メール件名の末尾に+を入れて、既存ノート(他のライフログを書き込んでいるノート)に追記するようにしています。

その他

GASの環境内だけでも、日付をトリガーにしたスクリプトの実行が可能なようです。
個人的にトリガー関係はすべてIFTTTに集めておきたいので、今回はこのような感じになりました。

参考資料

大変お世話になりました!

  • Like!