部屋の温度を可視化するアプリを作ってみた(2/3)の続きです。

Sinatra + D3.js

Sinatraをインストール

sinatraをインストールする。

$ sudo yum install -y ruby20-devel gcc-c++
$ gem install bundler io-console
$ mkdir sensor-app && cd sensor-app
$ mkdir config views
$ bundle init
$ vi Gemfile
#以下を追加
gem "sinatra"
gem "json"
gem "mysql2"
gem "activerecord"

$ bundle install --path vendor/bundler

Sinatraアプリを作る

MySQLからデータを取得してjsonに変換するメソッドを作る。

app.rb
require 'sinatra'
require 'mysql2'
require 'active_record'
require 'json'
require 'time'

ActiveRecord::Base.configurations = YAML.load_file('config/database.yml')
ActiveRecord::Base.establish_connection(:production)

class Temperature < ActiveRecord::Base
end

get '/' do
erb :index
end

get '/data.json' do
content_type :json, :charset => 'utf-8'
temps = Temperature.where(imsi: '44010**********')
.select("send_time", "temp")
.order("send_time DESC")
.limit(1440)
temps.to_json(:root => false)
end

/data.jsonでMySQLから1日分(1440件*30秒/件)のデータを取得し、jsonに変換します。

データベースの接続設定ファイルを作る。

config/database.yml
production:
adapter: mysql2
encoding: utf8
pool: 5
host: localhost
database: sensordb
username: username
password: password

viewを追加する

d3.jsのExamplesにあるLine Chartを変更して作成する。

Line ChartのExamplesはtsvを表示するため、jsonを表示するように変更し、X軸を調整した。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<title>部屋の気温</title>
<style>
body {
font: 10px sans-serif;
}

.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}

.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script type="text/javascript">
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

var formatDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ");

var x = d3.time.scale()
.range([0, width]);

var y = d3.scale.linear()
.range([height, 0]);

var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");

var yAxis = d3.svg.axis()
.scale(y)
.orient("left");

var line = d3.svg.line()
.x(function(d) { return x(d.send_time); })
.y(function(d) { return y(d.temp); });

var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.json("data.json", function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.send_time = formatDate.parse(d.send_time);
d.temp = +d.temp;
});

x.domain(d3.extent(data, function(d) { return d.send_time; }));
y.domain(d3.extent(data, function(d) { return d.temp; }));

svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);

svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text('温度(℃)');

svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
</script>
</body>
</html>

実行

$ nohup bundle exec ruby app.rb -o 0.0.0.0 &

4567ポートに接続してグラフが表示されればOK。

まとめ

とりあえず可視化までできた。集めたセンサーデータを使って可視化以外で何かできればおもしろそうですね。

終わり