かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

TypeScriptとd3.jsでグラフを描こうぞ 2

下の続き。

y方向の値の調整

パソコンとかの描画関係のAPIの座標系は左上が0,0なのが一般的です。svgも例にもれず。でも、グラフの座標は左下が0,0だったりしますよね。ということで、y座標のスケールのrangeを逆転させると、いい感じになります。

// yの最小値とyの最大値を500~0の間にスケーリングするryを作成
var ry = d3.scale
    .linear()
    .domain([d3.min(datas, a => a.y), d3.max(datas, a => a.y)])
    .range([500, 0]); // ここを0, 500だったのを500, 0にする

これでデータの出力が逆になるので、いい感じに左下が小さい値になります。d3.jsのチュートリアルにもある通りですね。

軸の作成

あとは、チュートリアルのここをなぞっていきます。

cssを用意します。TypeScriptのプロジェクトはデフォルトでapp.cssが用意されてるのでそこに足していきます。

本当はsvgの領域内に綺麗に収まるようにデータとか軸の幅とかを調整するのが望ましいんですが、今回は手抜きでsvgを囲むdivタグに余白を設けてお茶を濁してます。これできちんと表示されてるんですがブラウザ依存かもしれません。

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

.axis text {
    font-family: 'Meiryo UI';
    font-size: 11px;
}

#content {
    margin: 50px;
}

そして、app.tsにチュートリアルの通りのコードを前回のコードに足していきます。

// x軸
var xAxis = d3.svg.axis().scale(rx).orient("bottom");
svg.append("g")
    // 軸にあてるスタイル用のクラスを設定
    .attr("class", "axis")
    // デフォルトで上に表示されるので下に移動
    .attr("transform", "translate(0, 500)")
    .call(xAxis);

// y軸
var yAxis = d3.svg.axis().scale(ry).orient("left");
svg.append("g").attr("class", "axis").call(yAxis);

これで、実行すると以下のような結果に。

f:id:okazuki:20140209211128p:plain

いい感じ・・・!あとは、細かい調整とかの範囲でなんとかなりそう。

コード

一応コード全体をのせておきます。

default.htm

<!DOCTYPE html>

<html lang="ja">
<head>
    <meta charset="utf-8" />
    <title>TypeScript HTML App</title>
    <link rel="stylesheet" href="app.css" type="text/css" />
    <script src="Scripts/d3.v3.min.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <h1>Graph</h1>
    <div id="content"></div>
</body>
</html>

app.css

body
{
    font-family: 'Segoe UI', sans-serif
}

span {
    font-style: italic
}

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

.axis text {
    font-family: 'Meiryo UI';
    font-size: 11px;
}

#content {
    margin: 50px;
}

app.ts

/// <reference path="Scripts/typings/d3/d3.d.ts" />

window.onload = () => {
    // 50セットのデータを生成
    var datas: Array<{ x: number; y: number }> = [];
    var seed = d3.random.normal(200, 200);
    for (var i = 0; i < 50; i++) {
        datas.push({ x: i * 10, y: seed() });
    }

    // xの最小値とxの最大値を0~500の間にスケーリングするrxを作成
    var rx = d3.scale
        .linear()
        .domain([d3.min(datas, a => a.x), d3.max(datas, a => a.x)])
        .range([0, 500]);
    // yの最小値とyの最大値を500~0の間にスケーリングするryを作成
    var ry = d3.scale
        .linear()
        .domain([d3.min(datas, a => a.y), d3.max(datas, a => a.y)])
        .range([500, 0]);

    // id="content"の中に500x500のsvg要素を作成
    var svg = d3.select("#content")
        .append("svg")
        .attr("width", 500)
        .attr("height", 500);

    // pathを組み立ててくれるlineを作成
    var line = d3.svg.line()
        // x座標はrxでスケーリングした結果
        .x(d => rx(d.x))
        // y座標はryでスケーリングした結果
        .y(d => ry(d.y));

    // pathを作成して
    svg.append("path")
        // データはlineで組み立てて
        .attr("d", line(datas))
        // 塗りつぶしは無しで
        .attr("fill", "none")
        // かっこいい青色で線の太さは1
        .attr("stroke", "steelblue")
        .attr("stroke-width", 1);

    // データの場所に点をうっていく
    svg.selectAll("circle")
        .data(datas)
        .enter()
        // x座標とy座標はスケーリングした結果で半径は2でかっこいい青色
        .append("circle")
        .attr("cx", d => rx(d.x))
        .attr("cy", d => ry(d.y))
        .attr("r", 2)
        .attr("fill", "steelblue");

    // x軸
    var xAxis = d3.svg.axis().scale(rx).orient("bottom");
    svg.append("g")
        // 軸にあてるスタイル用のクラスを設定
        .attr("class", "axis")
        // デフォルトで上に表示されるので下に移動
        .attr("transform", "translate(0, 500)")
        .call(xAxis);

    // y軸
    var yAxis = d3.svg.axis().scale(ry).orient("left");
    svg.append("g").attr("class", "axis").call(yAxis);
};