"BOKU"のITな日常

興味のむくまま気の向くままに調べたり・まとめたりしてます。

Chart.jsのshowLineオプションを使って「樹形図」を描く/django3.0+Chart.js

散布図の点を線で結んで樹木図を描こうと「showLine」オプションを使うと最初うまく描けなかったので。

f:id:arakan_no_boku:20200422010037p:plain

 

やりかたかった事とやったこと

 

樹形図を描きたかったのです。

なので、座標データを生成して、Chart.jsに渡して散布図を描きます。

最初は散布図のデフォルト(線なし)でやってみます。

f:id:arakan_no_boku:20200429215518p:plain

ただ。

線無しだと「クラゲ」みたいで、樹形図にはなりません。

点と点の間を線で結ぶことにします。

そのために、以下のオプションを追加します。

showLine: ttrue,

すると、こうなりました(笑)。

f:id:arakan_no_boku:20200429215928p:plain

線と線の間が塗りつぶされていて、想定と違います。

散布図で、showLine:true にしたときの、デフォルトがこれみたいです。

デフォルトを打ち消す設定を探してみます。

すると「fill」オプションがありました。

デフォルトが「true」なので「false」を指定してみると

showLine: ttrue,

fill: false,

うまくいきました。

 f:id:arakan_no_boku:20200429215411p:plain

「showLine」と「fill」は常にセットで使う。

そう覚えておいたほうがよさそうです。

今回のメインテーマは、これだけです。

 

今回のソースです

 

ソースは、最後のうまく描けたものだけにします。

HTML

{% block content %}
<div class="container">
    <div class="mycanvas">
        <canvas id="myChart"></canvas>
    </div>
    <input type="hidden" id="x_data_str" name="x_data_str" value="{{ x_data_str }}">
    <input type="hidden" id="y_data_str" name="y_data_str" value="{{ y_data_str }}">
    <input type="hidden" id="label_text" name="label_text" value="{{ label_text }}">
    <input type="hidden" id="title_text" name="title_text" value="{{ title_text }}">
    <input type="hidden" id="chart_size" name="chart_size" value="{{ chart_size }}">
    <input type="hidden" id="show_line" name="show_line" value="{{ show_line }}">
</div>
<script type="text/javascript" src="{% static "js/scatter01.js" %}"></script>
{% endblock %}

Chart.jsのグラフ描画は、scatter01.jsに書く想定です。

上の「hidden」タグ部分は、scatter01.js内部で読み込むパラメータとデータです。

scatter01.js

var xDataArray = String(document.getElementById('x_data_str').value).split(',');
var yDataArray = String(document.getElementById('y_data_str').value).split(',');
var labelText = String(document.getElementById('label_text').value);
var titleText = String(document.getElementById('title_text').value);
var chartSize = document.getElementById('chart_size').value;
var showLine = String(document.getElementById('show_line').value);

window.chartColors = {
    red: "#FF0000",
    blue: "#0000FF"
};

var color = Chart.helpers.color;
function generateData() {
    var data = [];
    for (let i = 0; i < xDataArray.length; i++) {
        data.push({
            x: xDataArray[i],
            y: yDataArray[i]
        });
    }
    return data;
};

var scatterChartData = {
    datasets: [{
        label: labelText,
        borderColor: window.chartColors.blue,
        backgroundColor: window.chartColors.blue,
        pointRadius: 3,
        showLine: Boolean(showLine),
        fill: false,
        borderWidth: 1,
        data: generateData()
    }]
};

window.onload = function() {
    var ctx = document.getElementById('myChart').getContext('2d');
    ctx.canvas.width=chartSize;
    ctx.canvas.height=chartSize;
    window.myScatter = Chart.Scatter(ctx, {
        data: scatterChartData,
        options: {
            title: {
                display: false,
                text: titleText
            },
            scales: {
                xAxes: [{
                    gridLines: {                       // 補助線(縦線)
                        color: "rgba(255, 0, 0, 0.2)",   // 補助線の色
                        zeroLineColor: "black"           // x=0時の(縦線の色)
                    },
                }],
                yAxes: [{
                    gridLines: {                       // 補助線(縦線)
                        color: "rgba(255, 0, 0, 0.2)",   // 補助線の色
                        zeroLineColor: "black"           // x=0時の(縦線の色)
                    },
                }],
            },
        }
    });
};

ポイントは以下の部分です。、

var scatterChartData = {
    datasets: [{
        label: labelText,
        borderColor: window.chartColors.blue,
        backgroundColor: window.chartColors.blue,
        pointRadius: 3,
        showLine: Boolean(showLine),
        fill: false,
        borderWidth: 1,
        data: generateData()
    }]
};

ここで、「showLine」と「fill」を指定しています。 

showLineに「Boolean(showLine)」となっているのは、HTMLtタグからパラメータを受け取るためです。

受け取ったパラメータが「true」の想定です。

HTMLとjavaScript側は以上です。

今度は。

python(django3.0)側の処理です。

view.py

def chart01(request):
    gr = gr_func.Chart01Function()
    c = {
        'x_data_str': gr.get_xstr(),
        'y_data_str': gr.get_ystr(),
        'label_text': "樹形図",
        'title_text': "樹形図",
        'chart_size': 500,
        'show_line': "true",
    }
    return render(request, 'chart01.html', c)

ここでは、Chart01Functionクラスを読み込んで、パラメータに値をセットしてます。 

func.py

class Chart01Function:
    def __init__(self):
        gr = make_gr_data.GrTree()
        self.tx_str, self.ty_str = gr.draw()

    def get_xstr(self):
        return self.tx_str

    def get_ystr(self):
        return self.ty_str

樹形図の座標計算の本体は 「make_gr_data.GrTree()」です。

make_gr_data.py

import numpy as np
import random

class GrTree:

    def __init__(self, lpx=320.0, lpy=380.0):
        self.tx = []
        self.ty = []
        self.tx.append(lpx)
        self.ty.append(lpy)

    def __move(self, leng, lpx, lpy, angle):
        x = lpx + leng * np.cos(angle * np.pi / 180.0)
        y = lpy + leng * np.sin(angle * np.pi / 180.0)
        self.tx.append(x)
        self.ty.append(y)
        return x, y

    def draw(
            self,
            leng=120.0,
            lpx=320.0,
            lpy=380.0,
            angle=90.0,
            scale=0.7):
        if(leng >= 5.0):
            self.tx.append(lpx)
            self.ty.append(lpy)
            lpx, lpy = self.__move(leng, lpx, lpy, angle)
            self.draw(leng * scale, lpx, lpy, angle + 20.0)
            self.draw(leng * scale, lpx, lpy, angle - 20.0)
        return ','.join(map(str, self.tx)), ','.join(map(str, self.ty))

座標計算し、結果の配列をカンマ区切りの文字列に変換して返しています。

これはHTMLのパラメータとしてデータを渡すためです。

以上です。

なお、当記事はdjango3.0環境がインストール済のwindopws10環境が前提です。

urls.pyとかdjangoで表示するためだけに必要なものは、一部割愛しています。

djangoの環境構築、および、urls.pyの書き方等についてはこちらに書いてます。

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

ではでは。