Help us understand the problem. What is going on with this article?

d3.jsを用いたIoTセンサーのリアルタイムチャート作成

More than 1 year has passed since last update.

d3.jsは、データドリブンで色々かっこいいグラフなどが書けるJavascriptのライブラリとして有名です。 このライブラリを使って、IoTセンサーのリアルタイムチャートを作成してみます。素人なので、コードには問題があるかもしれませんが、その際はご指摘いただけると幸いです><

IoTデバイスとの接続/データの取得

デバイスを使っているかは具体的には説明しません。各々のデバイス毎にしかるべき設定を行ってください。 私が使用しているデバイスは、GETリクエストを送ってやるとセンサーの値を返してくれます。 具体的に書くと以下のようになります。

 const url = "hogehoge";
 var xhr = new XMLHttpRequest();
 xhr.addEventListener('error', function() {
      alert("Not establish connection");
      // エラー処理
       });

xhr.addEventListener('loadend', function() {
   if (xhr.status === 200 && xhr.readyState === 4) {
      const res = parseFloat(xhr.responseText);
      const now = new Date();
      dataset.push({
         time: now,
         val: res
         });
    else {
       console.error(xhr.status + ' ' + xhr.statusText);
    }
});

xhr.open("GET", url + '/brightness', false);
// ここではセンサーのうちの照度センサーを使ったとします
xhr.send(null);
// 本来は非同期で送った方がいいのですが、簡単のために同期でリクエストを送ってあげます
xhr.send(null);

d3.jsの使用

d3.jsを使いましょう。ここでは、サンプルコードの量が多い、d3.jsのversion3を使用します。
CDNで用いるには次のように加えるだけで大丈夫です。

<script src="https://d3js.org/d3.v3.min.js"></script>

まずはグラフの描画の元となるものの宣言です。

var margin = {
            top: 30,
            right: 50,
            bottom: 30,
            left: 50
        };
var width = 600 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var xScale = d3.time.scale()
            .range([0, width]);
//x軸は時間のスケールになるように設定します

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


var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient("bottom")
            .tickFormat(d3.time.format('%M:%S'));

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

// 線の定義
var line = d3.svg.line()
            .x(function(d) {
                return xScale(d.time);
            })
            .y(function(d) {
                return yScale(d.val);
            })
            .interpolate("cardinal");
            // 点の間の補間をcardinalに設定

// svgの定義
var svg = d3.selectAll("#d3graph").append("svg")
// このd3のselectAllメソッドにより、基となるHTMLの <div id="d3graph"> </div>なる要素を引っ張ってきます
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

これで大体の描画は完了しました。そのあとはデータが入ってくるたびに描画するプログラムを付け足せば完了です。

 function update() {
            if (dataset.length > 20) {
                dataset.shift();
                // データ数が20を超えたら入っているデータを消します        
            }
            svg.selectAll("path").remove();
            // xy軸削除
            svg.selectAll("g").remove();
       // 線の削除

            xScale.domain(d3.extent(dataset, function(d) {
                return d.time;
            }));
            yScale.domain(d3.extent(dataset, function(d) {
                 return d.val;
             })).nice();
            //xy軸それぞれのドメインをdatasetのmin, maxの範囲に収めます

            svg.append("g")
                .attr("class", "y axis")
                .call(yAxis)
                .append("text")
                .attr("y", -10)
                .attr("x", 10)
                .style("text-anchor", "end")
                .text("");
            // y軸の再描画

            svg.append("g")
                .attr("class", "x axis")
                .call(xAxis)
                .attr("transform", "translate(0," + height + ")")
            // x軸の再描画

            // path要素をsvgに表示し、折れ線グラフを設定
            svg.append("path")
                .datum(dataset)
           // lineとdatasetのbind
                .attr("fill", "none")
                .attr("stroke", "steelblue")
                .attr("stroke-linejoin", "round")
                .attr("stroke-linecap", "round")
                .attr("stroke-width", 1.5)
                .attr("d", line);
        };

このupdate()を定期的に繰り返してあげれば描画完了です。
念のため、貼り付けるだけで使えるHTMLをサンプルとして貼っておきます。

<!DOCTYPE html>
<html lang="en">
<head>
    <style type="text/css">
        .axis text {
            font: 10px sans-serif;
        }
        .axis path {
            opacity: 0.1;
            stroke: #000;
            stroke-width: 0.1;
            shape-rendering: crispEdges;
        }
        .axis line {
            stroke: #000;
            stroke-width: 1;
            shape-rendering: crispEdges;
        }
        .line {
            fill: none;
            stroke: DarkGreen;
            stroke-width: 1.5px;
        }
    </style>
</head>

<body>
        <div id="d3graph"></div>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script>
        window.addEventListener("loaded", update, false);

        var dataset = [];

        function generateData() {
            const now = new Date();
            const data = {
                time: now,
                val: Math.random() * 10
            };
            dataset.push(data)
        }

        var margin = {
            top: 30,
            right: 50,
            bottom: 30,
            left: 50
        };
        var width = 600 - margin.left - margin.right;
        var height = 500 - margin.top - margin.bottom;

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

        var yScale = d3.scale.linear()
            .range([height, 0])
            .domain([0, 10]);

        var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient("bottom")
            .ticks(10)
            .tickFormat(d3.time.format('%M:%S'));

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

        var line = d3.svg.line()
            .x(function(d) {
                return xScale(d.time);
            })
            .y(function(d) {
                return yScale(d.val);
            })
            .interpolate("cardinal");

        var svg = d3.selectAll("#d3graph").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 + ")");

        function update() {
            if (dataset.length > width / 20) {
                dataset.shift();
            }
            svg.selectAll("path").remove();
            svg.selectAll("g").remove();

            xScale.domain(d3.extent(dataset, function(d) {
                return d.time;
            }));
             yScale.domain(d3.extent(dataset, function(d) {
                 return d.val;
             })).nice();

            svg.append("g")
                .attr("class", "y axis")
                .call(yAxis)
                .append("text")
                .attr("y", -10)
                .attr("x", 10)
                .style("text-anchor", "end")
                .text("");

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


            svg.append("path")
                .datum(dataset)
                .attr("fill", "none")
                .attr("stroke", "steelblue")
                .attr("stroke-linejoin", "round")
                .attr("stroke-linecap", "round")
                .attr("stroke-width", 1.5)
                .attr("d", line);
        };

        setInterval(generateData, 500);
        setInterval(update, 500);
    </script>
</body>
</html>

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away