トレイルトップに戻る    印刷する

目ごとのデータを図形の大きさで比較ーDatamateを使って書く

ラーニングトレイル2の(1)項目比較で、データを図形の大きさで表しました。 この時、図形を描く位置は スケッチ内で計算しました。つまり、位置の指定を自分で書いたのですが、 Datamateライブラリ が提供する機能を使うと、その処理を個別に書かずに、Datamateに任せることができます。

Datamateライブラリを使う、つまりDatamate.jsファイルを参照できるように指定するには、2つの方法があります。

年ごとに円の大きさを切り変える

リストT2-1-2は、水生物の平均寿命を、図形の大きさで表した例です。 このプログラムをDatamate.jsの機能を使って書き換えてみます。


クリックで開始⇔停止

[1]視覚化の対象となる内部テーブルを作る

5種の水生動物の寿命データを次のようなテーブルと考えます。
名前イカマグロオオサンショウウオウニ シロナガスクジラ
寿命1.020.055.070.0100.0

このテーブルの行と列には、それぞれヘッダ(行の名前、列の名前)があります。 ヘッダのあるなしは任意に指定できますが、この例のようにヘッダ行、ヘッダ列があるテーブルがデフォルトです。
ヘッダ行、ヘッダ列を除いたデータ部分のことをデータテーブルと呼んで区別します。この表の場合、データテーブルは一行だけです。

Datamatemのmake()関数を使って、1行ごとに、行の名前とデータを指定します。
  Datamate.make('行の名前', [データの配列]);
文字列は引用符で囲みます(二重"でも一重'でもいい)。このデータの場合、次のようにmake()関数を3回実行します。 ひとつ目のmake()関数は、ヘッダ行を作るもので、列のヘッダを配列内に指定しています。
  Datamate.make("名前", ["イカ", "マグロ", "オオサンショウウオ", "ワニ", "シロナガスクジラ"]);
  Datamate.make("寿命", [1.0, 20.0, 55.0, 70.0, 100.0]);

このようにして作られたデータテーブルの列や行の情報は次の関数で知ることができます。

[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]データの描画をする関数を定義する

データを円の大きさとして描画するこの例の描画処理を分解すると、次のようになります。 この処理をエリアごとに繰り返すので、エリアの番号を引数として渡し、 これらの描画処理を関数としてまとめて定義しておきます。 関数として描画処理をまとめておくと、それをdraw()関数から呼び出すだけでよく、エリアごとの描画処理をdraw()関数内で記述する必要がなくなります。

この例では、寿命データのインデックスとエリアの番号が一致しているので、このインデックスを 関数の引数として渡すことにします。関数名は任意です。
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オブジェクト)には、次のような変数に領域の情報が保存されています。 エリアの中央に円を描くなら、変数centerXとcenterYを使って、次のように書きます。
  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個の円を並べることで表してみよう。


copyright © info