棒グラフの見た目を調節する

前回作った棒グラフの見た目を調節していきます。

色について

ここでは、最終的にはスミ1色のグラフを作って印刷に使うことを目標にします。SVG は、Web など画面に表示することをメインに想定しているため、色の指定は基本的には RGB(sRGB)です。iccプロファイルを利用した色指定もできるというようなことが書かれているのですが、よくわかりません。SVG を illustrator に持って行ってから、すべてのオブジェクトをグレースケールに変換したのちに書類のカラーモードをCMYKに変えることにします。

illustorator のカラー設定「プリプレス用−日本2」で、ドキュメントのカラー設定をCMYKに変えたときにスミの濃度10%刻みになるRGB値とカラーコードが以下の表です。10の位が奇数の%では、0.2%ほど誤差がでます。255を10分割すると25.5で小数点が出るためです。

スミ濃度%RGB値カラーコード
0255#FFFFFF
10229#e5e5e5
20204#cccccc
30178#b2b2b2
40153#999999
50128#808080
60102#666666
7077#4d4d4d
8051#333333
9026#1a1a1a
1000#000000

マージンとサイズの設定を変更する

四六判の単行本の版面に合わせたサイズに変更します。仮に本文13Q、行送り21Hとして、左右18行天地20字分のサイズでつくってみます。左右幅は、21×17+13=370Q、92.5mm、72ppiでは約262pxです。天地は、13×20=260Q、65ミリ、72ppiで約184pxです。マージンもサイズに合わせて調節します。

//マージン設定
const margin = { left:40, right:0, top:25, bottom:40 };

//SVGのサイズ設定
const svgWidth = 262;
const svgHeight = 184;

グラフにタイトルをつける

タイトル文字を入れる

グラフにタイトルを付けます。text 要素を追加し、属性を指定します。x座標・y座標・文字サイズ・フォント・ウェイトの属性を指定し、テキスト内容を入れます。

svg.append("text")
    .attr("x", 0)
    .attr("y", 10)
    .attr("font-size", "10px")
    .attr("text-anchor", "top")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 700)
    .text("年齢5歳階級別人口[東京都]");

“font-weight”の指定について

“font-weight” は、100から900まで100きざみの数値か、boldとかのキーワードで指定できるらしいです。web の技術由来なので、特定のフォントの特定のウェイトをピンポイントで指定するのは難しいみたいです。たずがね角ゴシックInfoは10のウェイトがありますが、100から900で指定して使えたウェイトは7つで、Thin・ Book・Heavyのウェイトは使えませんでした。また、ブラウザ(chrome)ではウェイトの指定が反映されず、illustrator にコピーしたらウェイト指定が反映されました。

“font-weight” 指定 たずがねのウェイト
100UltLt
200UltLt
300Light
400 Regular
500 Medium
600Medium
700Bold
800Black
900XBlack

タイトル文字の下に線を引く

タイトル文字の下に線を引きます。line要素を追加して、始点のxy座標、終点のxy座標、線の色、線の太さを指定します。

svg.append("line")
    .attr("x1", 0)
    .attr("x2", svgWidth)
    .attr("y1", 15)
    .attr("y2", 15)
    .attr("stroke", "#000000")
    .attr("stroke-width", "0.1mm");

グラフの背景に地色をつける

グラフのグループ要素にスミ10%の地色をつけます。

g.append("rect")
 .attr("class", "background")
 .attr("x", 0)
 .attr("y", 0)
 .attr("width", chartWidth)
 .attr("height", chartHeight)
 .attr("fill", "#e5e5e5");

軸にラベルをつける

縦横の軸になにを表しているかラベルをつけます。テキスト要素を追加し座標や書式の属性を指定します。縦軸のラベルは縦書きにします。

//軸ラベルを追加
//横軸
g.append("text")
    .attr("y", chartHeight + 40)
    .attr("x", chartWidth / 2)
    .attr("font-size", "10px")
    .attr("text-anchor", "middle")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 300)
    .text("年齢");

// 縦軸
g.append("text")
    .attr("y", chartHeight/2)
    .attr("x", -30)
    .attr("font-size", "10px")
    .attr("text-anchor", "middle")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 300)
    .attr("writing-mode", "tb")
    .text("人口【千人】");

メモリの見た目を調節する

縦横各軸のメモリの見た目を調節していきます。まず、縦軸のスケールの上限を前回はデータの最大値に設定しましたが、このあと東京都以外の県のグラフも作っていきたいので、グラフ間でメモリの値が変わらないように最大値を1200(千人)に固定してしまいます。

const yScale = d3.scaleLinear()
    .domain([0, 1200])
    .range([chartHeight, 0]);

メモリの長さを変えます。 横軸はメモリを消します (サイズ指定を0にする) 。 縦軸はメモリを反対の端まで伸ばします。メモリのサイズは外側に向かって伸びるので、反対の端まで内側に伸ばすためグラフの横幅分のマイナスで指定します。

const xAxisCall = d3.axisBottom(xScale)
                    .tickSize(0);

const yAxisCall = d3.axisLeft(yScale)
                    .tickSize(-chartWidth);

前回は軸を呼び出しておしまいでしたが、見た目を調節するため、呼び出した軸を変数におさめて、軸の中の各種要素の属性を変更します。

const xAxis = g.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + chartHeight +")")
    .call(xAxisCall);

const yAxis = g.append("g")
    .attr("class", "y axis")
    .call(yAxisCall);

横軸の調節

横線(path)を消し、テキストの位置と書式を調節します。svg では縦組み中の欧文回転や縦中横の処理が困難なので、今回は横組みのまま斜めに回転させる方式にしてごまかします。

xAxis.selectAll("path")
        .attr("stroke", "none");
xAxis.selectAll("text")
    .attr("x", "0")
    .attr("y", "1")
    .attr("text-anchor", "end")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 300)
    .attr("font-size", "7px")
    .attr("x", "-1")
    .attr("y", "1")
    .attr("transform", "rotate(-45)");

縦軸の調節

メモリの線の色と太さを指定し、縦線を消します。テキストの位置と書式を指定します。文字を右揃えにします。

yAxis.selectAll("line")
        .attr("stroke", "#ffffff")
        .attr("stroke-width", "0.1mm");
yAxis.selectAll("path")
        .attr("stroke", "none");
yAxis.selectAll("text")
    .attr("x", "-3")
    .attr("y", "0")
    .attr("text-anchor", "end")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 300)
    .attr("font-size", "7px");

グラフの棒に数値を表示する

グラフの棒にそれぞれの数値をいれます。新しいtext要素をデータと結びつけます。enter()で text要素を作ります。数字の桁が大きくて棒の横幅には入らないので、横倒しにして棒の中に入れます。(“transform”, “rotate(90)”) で座標を90度回転します。座標が回転しているので、x座標に- y 、y座標にxを指定します。それぞれ2pxずらして文字位置を微調整しています。

//値のラベルをつける
const valueLabel = g.selectAll("text.value")
    .data(data);

valueLabel.enter()
    .append("text")
    .attr("class", "value")
    .attr("fill", "#ffffff")
    .attr("transform", "rotate(90)")
    .attr("x", function(d){ return yScale(d["人口【千人】"])+2; })
    .attr("y", function(d){ return -xScale(d["年齢5歳階級"])-2; })
    .attr("text-anchor", "top")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 500)
    .attr("font-size", "7px")
    .text(function(d){ return d["人口【千人】"]; });

グラフの上下をケイ線で区切る

グラフの上下に0.3ミリのケイ線を入れます。後に書いたものが上になるので、棒の描画より後にします。

g.append("line")
    .attr("x1", 0)
    .attr("x2", chartWidth)
    .attr("y1", 0)
    .attr("y2", 0)
    .attr("stroke", "#000000")
    .attr("stroke-width", "0.3mm");
g.append("line")
    .attr("x1", 0)
    .attr("x2", chartWidth)
    .attr("y1", chartHeight)
    .attr("y2", chartHeight)
    .attr("stroke", "#000000")
    .attr("stroke-width", "0.3mm");

完成したグラフをブラウザから illustrator へ

完成したコードが以下です。

//マージン設定
const margin = { left:40, right:0, top:25, bottom:40 };

//SVGのサイズ設定
const svgWidth = 262;
const svgHeight = 184;

//グラフのサイズ設定
const chartWidth = svgWidth - margin.left - margin.right;
const chartHeight = svgHeight - margin.top - margin.bottom;

//SVG要素を追加
const svg = d3.select("body").append("svg")
            .attr("width", svgWidth)
            .attr("height", svgHeight);

//タイトルを追加
svg.append("text")
    .attr("x", 0)
    .attr("y", 10)
    .attr("font-size", "10px")
    .attr("text-anchor", "top")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 700)
    .text("年齢5歳階級別人口[東京都]");

svg.append("line")
    .attr("x1", 0)
    .attr("x2", svgWidth)
    .attr("y1", 15)
    .attr("y2", 15)
    .attr("stroke", "#000000")
    .attr("stroke-width", "0.1mm");



//グループ要素の追加
const g = svg.append("g")
        .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");

//背景グレーを追加
g.append("rect")
 .attr("class", "background")
 .attr("x", 0)
 .attr("y", 0)
 .attr("width", chartWidth)
 .attr("height", chartHeight)
 .attr("fill", "#e5e5e5");


//軸ラベルを追加
// X 軸
g.append("text")
    .attr("y", chartHeight + 40)
    .attr("x", chartWidth / 2)
    .attr("font-size", "10px")
    .attr("text-anchor", "middle")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 300)
    .text("年齢");

// Y 軸
g.append("text")
    .attr("y", chartHeight/2)
    .attr("x", -30)
    .attr("font-size", "10px")
    .attr("text-anchor", "middle")
    .attr("font-family", "Tazugane Info Std N")
    .attr("font-weight", 300)
    .attr("writing-mode", "tb")
    .text("人口【千人】");

//CSVファイルを読み込み
d3.csv("data/tokyo.csv").then(function(data){
    
    //人口【千人】列の値を数値に変換する
    data.forEach(function(d) {
        d["人口【千人】"] = +d["人口【千人】"];
    });
    
    //以下読み込んだデータを使って処理を行う
    // Xスケール
    const xScale = d3.scaleBand()
        .domain(data.map(function(d){ return d["年齢5歳階級"] }))
        .range([0, chartWidth])
        .padding(0.2);
    
    // Yスケール
    const yScale = d3.scaleLinear()
        .domain([0, 1200])
        .range([chartHeight, 0]);

    // 横軸
    const xAxisCall = d3.axisBottom(xScale)
                        .tickSize(0);
    const xAxis = g.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + chartHeight +")")
        .call(xAxisCall);
    
    xAxis.selectAll("path")
            .attr("stroke", "none");
    xAxis.selectAll("text")
        .attr("x", "0")
        .attr("y", "1")
        .attr("text-anchor", "end")
        .attr("font-family", "Tazugane Info Std N")
        .attr("font-weight", 300)
        .attr("font-size", "7px")
        .attr("x", "-1")
        .attr("y", "1")
        .attr("transform", "rotate(-45)");
    
    // 縦軸
    const yAxisCall = d3.axisLeft(yScale)
                        .tickSize(-chartWidth);
    const yAxis = g.append("g")
        .attr("class", "y axis")
        .call(yAxisCall);
    
    yAxis.selectAll("line")
            .attr("stroke", "#ffffff")
            .attr("stroke-width", "0.1mm");
    yAxis.selectAll("path")
            .attr("stroke", "none");
    yAxis.selectAll("text")
        .attr("x", "-3")
        .attr("y", "0")
        .attr("text-anchor", "end")
        .attr("font-family", "Tazugane Info Std N")
        .attr("font-weight", 300)
        .attr("font-size", "7px");
    
    // 棒を描く
    const rects = g.selectAll("rect.bar")
        .data(data);
        
    rects.enter()
        .append("rect")
            .attr("class", "bar")
            .attr("y", function(d){ return yScale(d["人口【千人】"]); })
            .attr("x", function(d){ return xScale(d["年齢5歳階級"]); })
            .attr("height", function(d){ return chartHeight - yScale(d["人口【千人】"]); })
            .attr("width", xScale.bandwidth)
            .attr("fill", "#666666");
    
    //値のラベルをつける
    const valueLabel = g.selectAll("text.value")
        .data(data);
    
    valueLabel.enter()
        .append("text")
        .attr("class", "value")
        .attr("fill", "#ffffff")
        .attr("transform", "rotate(90)")
        .attr("y", function(d){ return -xScale(d["年齢5歳階級"])-2; })
        .attr("x", function(d){ return yScale(d["人口【千人】"])+2; })
        .attr("text-anchor", "top")
        .attr("font-family", "Tazugane Info Std N")
        .attr("font-weight", 500)
        .attr("font-size", "7px")
        .text(function(d){ return d["人口【千人】"]; });
    
    //グラフの上下をケイ線で区切る
    g.append("line")
        .attr("x1", 0)
        .attr("x2", chartWidth)
        .attr("y1", 0)
        .attr("y2", 0)
        .attr("stroke", "#000000")
        .attr("stroke-width", "0.3mm");
    g.append("line")
        .attr("x1", 0)
        .attr("x2", chartWidth)
        .attr("y1", chartHeight)
        .attr("y2", chartHeight)
        .attr("stroke", "#000000")
        .attr("stroke-width", "0.3mm");
    
    
})

ローカルウェブサーバーを起動し、グラフをchromeで表示します。右上のメニューから、その他のツール>デベロッパーツールを選びます。デベロッパーツールの elements タブで、svg 要素を選択し、右クリックで、Copy>Copy Element を選んで svg 要素をコピーします。

illustrator でRGBの新規書類を作ります。コピーした svg 要素をペーストします。

全選択の状態で編集メニューから、カラーを編集>グレースケールに変換を選びます。最後にドキュメントのカラーモードをCMYKに変更して、スミ1色の棒グラフが得られます。

次回はブラウザを通さずに svg をファイルに保存するようにします。