目ごとのデータを図形の大きさで比較ーDatamateを使って書く
ラーニングトレイル2の(1)項目比較で、データを図形の大きさで表しました。 この時、図形を描く位置は スケッチ内で計算しました。つまり、位置の指定を自分で書いたのですが、 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-1-2は、水生物の平均寿命を、図形の大きさで表した例です。 このプログラムをDatamate.jsの機能を使って書き換えてみます。クリックで開始⇔停止
[1]視覚化の対象となる内部テーブルを作る
5種の水生動物の寿命データを次のようなテーブルと考えます。名前 | イカ | マグロ | オオサンショウウオ | ウニ | シロナガスクジラ |
---|---|---|---|---|---|
寿命 | 1.0 | 20.0 | 55.0 | 70.0 | 100.0 |
このテーブルの行と列には、それぞれヘッダ(行の名前、列の名前)があります。 ヘッダのあるなしは任意に指定できますが、この例のようにヘッダ行、ヘッダ列があるテーブルがデフォルトです。
ヘッダ行、ヘッダ列を除いたデータ部分のことをデータテーブルと呼んで区別します。この表の場合、データテーブルは一行だけです。
Datamatemのmake()関数を使って、1行ごとに、行の名前とデータを指定します。
Datamate.make('行の名前', [データの配列]);文字列は引用符で囲みます(二重"でも一重'でもいい)。このデータの場合、次のようにmake()関数を3回実行します。 ひとつ目のmake()関数は、ヘッダ行を作るもので、列のヘッダを配列内に指定しています。
Datamate.make("名前", ["イカ", "マグロ", "オオサンショウウオ", "ワニ", "シロナガスクジラ"]); Datamate.make("寿命", [1.0, 20.0, 55.0, 70.0, 100.0]);
このようにして作られたデータテーブルの列や行の情報は次の関数で知ることができます。
- ・Datamate.columnCount():列の数を返す
- ・Datamate.rowCount():行の数を返す
- ・Datamate.columnName(インデックス):列のヘッダを返す
- ・Datamate.rowName(インデックス):行のヘッダを返す
[2]データを表示する領域(レイアウトスペース)を作る
表示のための領域をレイアウトスペースと呼び、この領域を複数のエリアに分割して、エリアの中に、この例だとデータの大きさを表す円を描きます。Datamate.makeArea()関数を使って、レイアウトスペース作り、エリアに分割します。
Datamate.makeAreas(x座標, y座標, 横幅, 高さ, 横方向のエリア数, 縦方向のエリア数);キャンバス内でのレイアウトスペースの(x,y)座標と、幅と高さ、そしてその領域を何列に分けるか、何行に分けるかを指定します。 キャンバス全体を横方向に5つのエリアに分割するには次のように書きます。
Datamate.makeArea(0, 0, width, height, 5, 1);この例では、円の下に種類と数値を表示するため、円を描くエリアの高さを、キャンバスの高さより50ピクセルだけ小さくしたいので、エリアの高さをheight-50としました。
Datamate.makeAreas(0, 0, width, height-50, 5, 1);この例では、水生生物の数が5なので、分割するエリアの列数を5と指定しました。データの種類(データテーブルの列数)が増えたら分割したいエリア数も増えます。データの変更を考えると、具体的な数を書かずに、次のようにDatamate.columnCount()関数を使って、列数を指定する方が変更に柔軟に対応するプログラムと言えます。
Datamate.makeAreas(0, 0, width, height-50, Datamate.columnCount(), 1);
[3]分割したエリアに番号を振る
Datamate.bindAreas()関数を使って、エリアに番号を振ります。Datamate.bindAreas([番号, 番号,..]);データをどのエリアに表示するかを指定するときに、この番号を使います。 デフォルトでは0から順番に番号が振られています。つまり、次のようにbindAreas()関数を実行したのと同じことです。リストT2-7-1では、bindAreas()関数の実行を省略しています。
Datamate.bindAreas([0, 1, 2, 3, 4]);番号は必ずしも、0から始める必要はなく、連続の数字である必要もありませんが、データエリアに振られるデータのインデックス(0から順に振られる)と合わせておく、つまりデフォルトのままにするのがわかりやすいでしょう。
リストT2-7-1で、次をsetup()関数に入れると、水生生物の表示順が逆転します。
Datamate.bindAreas([4, 3, 2, 1, 0]);
[4]テーブルから値を取り出す
データを反映したグラフィックスをエリアに描画するために、テーブルから値を取り出すにはvalue()関数を使います。Datamate.value(行の指定, 列の指定); 行の指定:行の名前あるいはデータテーブルのインデックス(0から振られる。ヘッダ行の次が0) 列の指定:列の名前あるいはデータテーブルのインデックス(0から振られる。ヘッダ列の次が0)例えば、イカの寿命データを取り出すには、次のように書きます。
Datamate.value("寿命", 0) または Datamate.value(Datamate.rowName(0), 0);rowName()関数は行のヘッダ(名前)を返す関数です。列のヘッダはcolumnName()関数で得られます。
この寿命データの最小値は1年、最大値は100年で、これを円の直径の最小値10ピクセルから最大値120ピクセルの間の値に比例換算します。
Datamate.value()関数は、この比例換算をする機能を持っています。次のように第3引数に換算結果の最小値、第4引数に換算結果の最大値を指定します。
let diameter = Datamate.value("寿命", index, min, max); //円の直径を計算すると、行のデータの最小値と最大値を元に、比例換算した結果を返します。
map関数を使って、次のように書いたのと同じです。
let diameter = map("寿命", 1, 100, min, max); //円の直径を計算
[5]データの描画をする関数を定義する
データを円の大きさとして描画するこの例の描画処理を分解すると、次のようになります。- ・テーブル内のデータを取り出す
- ・そのデータから表示する円の直径を計算する
- ・色を指定し、表示エリアの中央に、円を描く
- ・色を指定し、表示エリアの下に、データ名と数値を描く
この例では、寿命データのインデックスとエリアの番号が一致しているので、このインデックスを 関数の引数として渡すことにします。関数名は任意です。
function drawData(index) { ..... }例えば、イカのデータを番号0のエリアに表示するには、次のように引数を指定します。
drawData(0); // 表示エリア0に'イカ'寿命データを表示
[6]エリアの中央の位置、大きさなどの情報を得る
描画処理を行う関数(上の[5]で定義したdrawData()関数)内で、 描画をする際には、エリアの情報(中央位置の座標や下辺のy座標)が必要になります。 Datamate.area()関数は、描画エリアの情報(位置や大きさの情報を保持するareaオブジェクト)を返します。Datamate.area(エリアに振った番号);この例ではdrawData()関数内で、引数で渡されたエリア番号を使って、次のように領域情報を得ます。
const area = Datamate.area(index);Datamate.area()関数が返す値(areaオブジェクト)には、次のような変数に領域の情報が保存されています。
- ・左上隅の座標: x, y
- ・中央の座標: centerX, centerY
- ・左辺,右辺のx座標: left, right
- ・上辺,下辺のy座標: top, bottom
- ・幅と高さ: width, height
ellipse(area.centerX, area.centerY, diameter, diameter);
【リストT2-7-1】 let currentD = []; //項目ごとの途中の高さ let henka = 1.0; const min = 10.0; //円の最小直径 const max = 120.0; //円の最大直径 function setup() { createCanvas(700, 250); Datamate.make("名前", ["イカ", "マグロ", "オオサンショウウオ", "ワニ", "シロナガスクジラ"]); Datamate.make("寿命", [1.0, 20.0, 55.0, 70.0, 100.0]); Datamate.makeAreas(0, 0, width, height-50, Datamate.columnCount(), 1); //↑Datamate.columnCount()はデータの列数 noStroke(); textAlign(CENTER, CENTER); //文字を表示する際、中心の座標を指定 for (let i=0; i<Datamate.columnCount(); i++) { currentD[i] = 0.0; //最初は0 } frameRate(10); } function draw() { background(240); for (let i=0; i<Datamate.columnCount(); i++) { drawData(i); } } function drawData(index) { const area = Datamate.area(index); let diameter = Datamate.value(Datamate.rowName(0), index, min, max); // ↑ map(Datamate.value("寿命", index), 1, 100, min, max); と同じ if(currentD[index] >= diameter){ //最終直径になったら henka = 0; //大きさを変えない fill(0, 0, 200); //青にする } else { henka = 1; //そうでないなら1ずつ増やす fill(200, 0, 0); //赤で描く } currentD[index] = currentD[index] + henka; ellipse(area.centerX, area.centerY, currentD[index], currentD[index]); fill(0); text(Datamate.value("名前", index), area.centerX, area.bottom+15); if (henka == 0) { //増加が終わったら寿命年を表示 text(Datamate.value("寿命", index) +"年", area.centerX, area.bottom+30); } }
演習問題
【問題T2-7-1】問題T2-1-2を、Datamate.jsを使って書き直してみよう。
【問題T2-7-2】リストT2-2-1で使っている9年間の猫と犬の飼育推計数の変化を、次のように9個の円を並べることで表してみよう。