時系列のデータを図形の大きさで表すーDatamateを使って書く
ラーニングトレイル2の(2)時系列変化で、データの時系列変化を、図形の大きさで表しました。 この時、図形を描く位置やデータを切り替えるタイミング(アニメーションさせるスピード)は スケッチ内で計算しました。つまり、位置やアニメーションの指定を自分で書いたのですが、 Datamateライブラリ が提供する機能を使うと、その処理を個別に書かずに、Datamateに任せることができます。Datamateライブラリを使う、つまりDatamate.jsファイルを参照できるように指定するには、2つの方法があります。
- 【方法1】CDN(Content Delivery Network)を通して、ライブラリを取得して使用
ライブラリのコピーを持っておくのではなく、ネットワークから読み込む方法。次のscriptタグをHTMLファイルのhead要素内に書く。 Processingエディタのindex.htmlタブを開き、次のように<script>タグを追加する。<!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN --> <script language="javascript" type="text/javascript" src="https://cdn.jsdelivr.net/gh/datamate-js/release@latest/dist/datamate.js"> </script>
- 【方法2】ライブラリをダウンロードし、保存して使用
- (1)まず、 DatamateライブラリのwebサイトからDatamate.jsをダウンロードし、スケッチフォルダ内のlibrariesフォルダに保存する。
- (2)Processingエディタのindex.htmlタブを開き、次のように<script>タグを追加する。
<!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN --> <script language="javascript" type="text/javascript" src="libraries/Datamate.js"> </script>
年ごとに円の大きさを切り変える
リストT2-2-1は、年ごとに変化するデータを、図形の大きさで表した例です。 このプログラムをDatamate.jsの機能を使って書き換えてみます。クリックで開始⇔停止
[1]視覚化の対象となる内部テーブルを作る
9年分の年、猫の数、犬の数のデータを次のようなテーブルと考えます。年 | 2013年 | 2014年 | 2015年 | 2016年 | 2017年 | 2018年 | 2019年 | 2020年 | 2021年 |
---|---|---|---|---|---|---|---|---|---|
猫 | 8409 | 8425 | 8297 | 8333 | 8672 | 8849 | 8764 | 8628 | 8946 |
犬 | 8714 | 8200 | 7994 | 8008 | 7682 | 7616 | 7579 | 7341 | 7106 |
このテーブルの行と列には、それぞれヘッダ(行の名前、列の名前)があります。 ヘッダのあるなしは任意に指定できますが、この例のようにヘッダ行、ヘッダ列があるテーブルがデフォルトです。
ヘッダ行、ヘッダ列を除いたデータ部分のことをデータテーブルと呼んで区別します。
Datamatemのmake()関数を使って、1行ごとに、行の名前とデータを指定します。
Datamate.make('行の名前', [データの配列]);文字列は引用符で囲みます(二重"でも一重'でもいい)。このデータの場合、次のようにmake()関数を3回実行します。 ひとつ目のmake()関数は、ヘッダ行を作るもので、列のヘッダを配列内に指定しています。
Datamate.make('年', ['2013年', '2014年', '2015年', '2016年', '2017年', '2018年','2019年', '2020年', '2021年']); Datamate.make('猫', [8409, 8425, 8297, 8333, 8672, 8849, 8764, 8628, 8946]); Datamate.make('犬', [8714, 8200, 7994, 8008, 7682, 7616, 7579, 7341, 7106]);
[2]データを表示する領域(レイアウトスペース)を作る
表示のための領域をレイアウトスペースと呼び、この領域を複数のエリアに分割して、エリアの中に、この例だとデータの大きさを表す赤い円を描きます。Datamate.makeArea()関数を使って、レイアウトスペース作り、エリアに分割します。
Datamate.makeAreas(x座標, y座標, 横幅, 高さ, 横方向のエリア数, 縦方向のエリア数);キャンバス内でのレイアウトスペースの(x,y)座標と、幅と高さ、そしてその領域を何列に分けるか、何行に分けるかを指定します。 キャンバス全体を横方向に2つのエリアに分割するには次のように書きます。
Datamate.makeArea(0, 0, width, height, 2, 1);
[3]分割したエリアに番号を振る
Datamate.bindAreas()関数を使って、エリアに番号を振ります。Datamate.bindAreas([番号, 番号,..]);猫と犬の2つデータをどのエリアに表示するかを指定するときに、この番号を使います。
Datamate.bindAreas([0, 1]);番号は必ずしも、0から始める必要はなく、連続の数字である必要もありません。デフォルトでは、0から順番に番号が振られるので、そのままでいい場合、bindAreas()関数の実行は省略できます。
[4]テーブルから値を取り出す
データを反映したグラフィックスをエリアに描画するために、テーブルから値を取り出すにはvalue()関数を使います。Datamate.value(行の指定, 列の指定); 行の指定:行の名前あるいはデータテーブルのインデックス(0から振られる。ヘッダ行の次が0) 列の指定:列の名前あるいはデータテーブルのインデックス(0から振られる。ヘッダ列の次が0)例えば、猫の2013年のデータを取り出すには、次のように書きます。
Datamate.value('猫', 0); または Datamate.value(Datamate.rowName(0), 0);rowName()関数は行のヘッダ(名前)を返す関数です。列のヘッダはcolumnName()関数で得られます。
[5]エリアの描画をする関数を定義する
データを円の大きさとして描画するこの例の描画処理を分解すると、次のようになります。- ・テーブル内のデータを取り出す
- ・そのデータから表示する円の直径を計算する
- ・色を指定し、表示エリアの中央に、円を描く
- ・色を指定し、表示エリアの中央に、データ名と数値を描く
関数内での描画処理には、2つの情報が必要です。
- ・猫、犬どちらのデータを取り出すか(行の名前)
- ・円を描く表示エリアの番号(猫が0、犬が1)
function drawData(syurui, number) { ..... }例えば、猫のデータを左側のエリアに表示するには、次のように引数を指定します。
drawData('猫', 0); // 表示エリア0に'猫'データを表示 または drawData(Datamate.rowName(0), 0);
[6]エリアの中央の位置、大きさなどの情報を得る
描画処理を行う関数(上の[5]で定義したdrawData()関数)内で、 描画をする際には、エリアの情報(中央位置や幅、高さ)が必要になります。 Datamate.area()関数は、描画エリアの情報(位置や大きさの情報を保持するareaオブジェクト)を返します。Datamate.area(エリアに振った番号);この例ではdrawData()関数内で、引数で渡されたエリア番号を使って、次のように領域情報を得ます。
const area = Datamate.area(block);Datamate.area()関数が返す値(areaオブジェクト)には、次のような変数に領域の情報が保存されています。
- ・左上隅の座標: x, y
- ・中央の座標: centerX, centerY
- ・左辺,右辺のx座標: left, right
- ・上辺,下辺のy座標: top, bottom
- ・幅と高さ: width, height
ellipse(area.centerX, area.centerY, w, w); //wは直径
[7]データを順に表示する(再生する)
テーブルのデータを順に表示することで、データの変化をアニメーションとして描画できます。 この例だと、横方向(行)に注目するデータを変えることで、時系列(年ごと)変化を表示できます。Datamateには、今注目している(focusを当てている)データのインデックスを返す関数foucsX()とfoucsY()があります。引数に0を指定すると今フォーカスしている値のインデックスを返します。引数に1を指定するとフォーカスしている値の次の値のインデックスを返します(つまり、引数はフォーカスしている値からの距離を意味する)。
const indexX = Datamate.foucsX(0); //フォーカス中のデータの横方向(列)のインデックスを返す const indexY = Datamate.foucsY(0); //フォーカス中のデータの縦方向(行)のインデックスを返すそして、play()関数を使って、フォーカスするデータを順に動かします(これを再生と呼びます)。再生することで、関数foucsX()とfoucsY()が返すインデックスが順に変化します。 play()の引数には、1秒間に何回データを動かす(次のデータを取り出す)のかを指定します。
Datamate.play(1秒間に横方向に動かす回数, 1秒間に縦方向に動かす回数);例えば、引数に2を指定すると0.5秒ごとに、引数に0.4を指定すると2.5秒ごとに、foucsX()関数、foucsY()関数で取り出されるデータのインデックスが順番に進みます。 play()関数の引数に0を指定するとデータは動きません。
再生しない(Datamate.play()関数を実行しない)場合、foucsX()、foucsY()関数で取り出されるデータのインデックスは先頭のままです。
再生をループしたい時は、loop()関数を使います。
Datamate.loop(横方向の再生をループするか, 縦方向の再生をループするか);引数をtrueとすると再生がテーブルの最後の値までいくと先頭に戻ります。デフォルト(指定しない場合)はfalseで、最後のインデックスで止まります。
リストT2-2-1では、スケッチが起動されてからの経過時間を計算して、3秒たったらデータを切り替えるといった処理を書いていましたが、 データ切り替えの処理はDatamate.play()関数がするので、その記述は不要になります。
【リストT2-5-1】 const title = "猫と犬の飼育推計数"; const titleX = 120; //この↑表題を表示するx座標 function setup() { createCanvas(450, 250); Datamate.make('年', ['2013年', '2014年', '2015年', '2016年', '2017年', '2018年', '2019年', '2020年', '2021年']); Datamate.make('猫', [8409, 8425, 8297, 8333, 8672, 8849, 8764, 8628, 8946]); Datamate.make('犬', [8714, 8200, 7994, 8008, 7682, 7616, 7579, 7341, 7106]); Datamate.makeAreas(0, 0, width, height, 2, 1); Datamate.bindAreas([0, 1]); Datamate.play(0.3, 0); //横方向に動かす Datamate.loop(true); //ループする。Datamate.loop(true, false); と同じ。第2引数は省略可 noStroke(); textAlign(CENTER, CENTER); } function draw() { background(210); drawData('猫', 0); // 表示域0に'猫'データを表示 drawData(Datamate.name(0), 0); drawData('犬', 1); // 表示域1に'犬'データを表示 drawData(Datamate.name(1), 1); } function drawData(syurui, block) { const area = Datamate.area(block); //表示エリアをとりだす const index = Datamate.focusX(0); //今フォーカスしているデータのインデックス const year = Datamate.columnName(index); //列のヘッダ const value = Datamate.value(syurui, index); const w = map(value, 7000, 9000, 30, 200); //データを円の直径に換算 fill(200, 0, 0); ellipse(area.centerX, area.centerY, w, w); fill(0); text(title+"("+year+")", titleX, 30); //表題と年の表示 fill(255); text(syurui + "\n" + value + "千頭", area.centerX, area.centerY); //データの種類と頭数の表示 }
演習問題
【問題T2-5-1】リストT2-5-1を変更し、円の大きさがスムーズに変化するようにしてみよう。
今フォーカスしている値のインデックスを返すfocusX()、focusY()関数は、今のインデックスと次のインデックスとの間を補間(内挿)する機能を備えています。補間機能を有効にするには、第2引数にtrueを指定します。
【リストT2-5-1】の場合、次のように書くと、play()関数で指定したフレームレートで、focusX()が返すインデックスが徐々に動きます(整数インデックスの間の値が返る)。それをvalue()関数に指定すると、連続的に変化する値が得られ、この値を使って、円の大きさを計算すると、スムーズに変化させることができます。
const indexForHokan = Datamate.focusX(0, true); //focusX(0)とfocusX(1)の間を補間したインデックス const value = Datamate.value(syurui, indexForHokan);ただ、このvalueをそのまま数値の表示に使うと、数が大きく変化して見にくいので、数値表示用のデータは補間しない値を使う方がいいでしょう。
リストT2-2-2では、犬と猫の円の直径用に別々の変数を用意して、変化する円の直径を(自分で)計算していました。Datamate.jsを使って順次データを取り出していく場合は、この変化の計算をDatamate.focusX()の補間機能に任せるのが簡単です。
【問題T2-5-2】
問題T2-2-1を、Datamate.jsを使って書き直してみよう。 画像ファイルは、画像ページからコピーできます。