概要
とあるイベントに参加しているユーザが自由なテキストを投稿した際に、それらのワードがタグクラウドとなって会場のディスプレイに表示されたら面白そうだよね!
と言っていた方がいたので検討してみました。
実際には mentimeter を活用して当日は下記のようなものができたそうです。
大まかな仕組み
- Google Form にて一行テキストが投稿できるように設定
- 投稿の保存先をSpreadsheetにしておく
- そのSpreadsheetをWebアプリケーションとして公開しJSONを吐くようにする
- JSONを読み込むWebページ(HTML/JavaScriptで良い)を設置する
手順
Google Form で投稿フォームを作成
下記のようにします。あとで楽なので、質問のタイトルは「comment」などの半角英字にしておくと良いと思います。(本記事はここでcommentと設定した前提とします)
下記のようなフォームができます。
上記の動作をするものを下記リンクから辿れるようにしてあります
投稿の保存先をスプレッドシートにする
テスト回答して、回答を一件作成すると、回答先としてスプレッドシートが選べるようになります。
回答が記入されています。
このスプレッドシートのURLを控えておきます。
今回は:https://docs.google.com/spreadsheets/d/1XTXONJ3pMBE8qiWlATCnHNqCwbsF8bEO1WaeVQ8x6Nk/edit#gid=1574077758 というURLです。
スプレッドシートのデータをWebアプリケーションとして公開
AppsScriptを用いて、Webアプリケーションを作成します。

コードの記述
コード.gs として、以下のコードを貼り付けます。
こちらを利用させていただきました:https://gist.github.com/daichan4649/8877801
URL_BOOKの値には、先程控えたスプレッドシートのURLを入れてください。
var URL_BOOK = 'https://docs.google.com/spreadsheets/d/1XTXONJ3pMBE8qiWlATCnHNqCwbsF8bEO1WaeVQ8x6Nk/edit#gid=1574077758';
function doGet(e) {
var url = URL_BOOK;
var book = SpreadsheetApp.openByUrl(url);
var sheets = book.getSheets();
var sheet = sheets[0];
var json = convertSheet2Json(sheet);
return ContentService.createTextOutput(JSON.stringify(json))
.setMimeType(ContentService.MimeType.JSON);
}
function convertSheet2Json(sheet) {
// first line(title)
var firstRange = sheet.getRange(1, 1, 1, sheet.getLastColumn());
var firstRowValues = firstRange.getValues();
var titleColumns = firstRowValues[0];
// after the second line(data)
var lastRow = sheet.getLastRow();
var rowValues = [];
for(var rowIndex=2; rowIndex<=lastRow; rowIndex++) {
var colStartIndex = 1;
var rowNum = 1;
var range = sheet.getRange(rowIndex, colStartIndex, rowNum, sheet.getLastColumn());
var values = range.getValues();
rowValues.push(values[0]);
}
// create json
var jsonArray = [];
for(var i=0; i<rowValues.length; i++) {
var line = rowValues[i];
var json = new Object();
for(var j=0; j<titleColumns.length; j++) {
json[titleColumns[j]] = line[j];
}
jsonArray.push(json);
}
return jsonArray;
}
公開
メニューの「公開」>ウェブアプリケーションとして導入... を選択します。
設定は上記のようにします。匿名ユーザがアプリケーションにアクセスできるようにしましょう。
「導入」を選ぶと「承認が必要です」と言われるので「許可を確認」します。
警告などが出ますが、自分が作っているアプリケーションなので問題ありません。許可してください。
完了すると、WebアプリケーションのURLを取得できるので、それを控えてください。
JSON出力の確認
上記で控えたURL(今回は:https://script.google.com/macros/s/AKfycbya60HH2kMsthpWX54Ncu9efhoqHViWIh3NOBVBRdjBhDv2saQ/exec) を実行すると、JSONデータが返ってきます。
[{"タイムスタンプ":"2018-03-22T01:12:58.167Z","comment":"test"}]
試しにデータを追加してみると
[{"タイムスタンプ":"2018-03-22T01:12:58.167Z","comment":"test"},{"タイムスタンプ":"2018-03-22T01:26:44.029Z","comment":"test2"}]
このような形にかわります。
このJSONを読み込むHTML/JavaScriptを用意
今回は、d-cloudというライブラリを使ってみました。
HTML/JSのコードはこんな感じになります。
こちらを利用させていただきました: https://shimz.me/blog/d3-js/2711
<script src="../d3.v3.min.js"></script>
<script src="d3.layout.cloud.js"></script>
<script>
var spUrl = "JSONを吐いてくれるアプリケーションのURL";
d3.select('svg').append('text')
.attr({
x:50,
y:50,
fill:"black",
"font-size":40
})
.text('Loading...');
// メイン
d3.json(spUrl, function(data) {
// [{timestamp: ts, comment: comment}, ... ] を [{count:c, word: w}, ... ] に変換
var words = [];
var objMap = [];
var map = data.map(function(d) {
// 単語だけの配列:words
var i = words.indexOf(d.comment);
if (i !== -1) {
objMap[i]["count"]++;
} else {
words.push(d.comment);
objMap.push({"count": 1, "word": d.comment});
}
});
data = objMap;
var h = 400;
var w = 400;
data = data.splice(0, 1200); //処理wordを1200件に絞る
var random = d3.random.irwinHall(2)
var countMax = d3.max(data, function(d){ return d.count} );
var sizeScale = d3.scale.linear().domain([0, countMax]).range([10, 100])
var colorScale = d3.scale.category20();
var words = data.map(function(d) {
return {
text: d.word,
size: sizeScale(d.count) //頻出カウントを文字サイズに反映
};
});
d3.layout.cloud().size([w, h])
.words(words)
.rotate(function() { return Math.round(1-random()) *90; }) //ランダムに文字を90度回転
.font("Impact")
.fontSize(function(d) { return d.size; })
.on("end", draw) //描画関数の読み込み
.start();
//wordcloud 描画
function draw(words) {
d3.selectAll('text').remove();
d3.select("svg")
.attr({
"width": w,
"height": h
})
.append("g")
.attr("transform", "translate(150,150)")
.selectAll("text")
.data(words)
.enter().append("text")
.style({
"font-family": "Impact",
"font-size":function(d) { return d.size + "px"; },
"fill": function(d, i) { return colorScale(i); }
})
.attr({
"text-anchor":"middle",
"transform": function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
}
})
.text(function(d) { return d.text; });
}
});
</script>
完成
それっぽい感じになりました。
レイアウト等々はオプションで調整してみてください! 実際に利用するには自動リフレッシュとかも入れたほうがいいと思います。