芸大 – 情報編集(Web) 2014
最終課題に向けて: 芸大電力消費量のデータをProcessing.jsでビジュアライズする 02
今回も引き続き、芸大の電力使用料のデータを使用したProcessing.jsによるビジュアライズにとりくみます。今回は、より自由な発想でデータを使った表現に挑戦していきましょう。実際に動くサンプルを紹介しながら、考えていきます。
制作テンプレート
前回解説した、全てのデータファイルを順番に読み込んで「data[行][列]」という二次元配列の形式に変換するところまでの、テンプレートを作成しました。この制作テンプレートを出発点にオリジナルの表現に挑戦すると良いでしょう。データファイルを読み込むためのクラス「EneTable」クラスは、前回のものをそのまま使用しています。下記からZip形式でデータ一式をダウンロード可能です。
/* * 情報編集(web)2014 * 最終課題制作テンプレート * 2014.07.11 * */ EneTable ene; // ファイル読込のためのクラス String[] filelist; // データファイル名リスト int dataNum = 0; // データ番号 int hour = 0; // 現在の時間(0〜23) // 初期化 void setup() { // 画面初期化 size(800, 600); noStroke(); frameRate(30); textSize(14); // ファイルリストの読み込み filelist = loadStrings("filelist.txt"); // データ読み込み用クラスEneTableをインスタンス化(初期化) ene = new EneTable(filelist[dataNum]); } // 結果を表示 void draw() { // 背景 background(0); // ================================================ // !!!!! このループ内でデータを表現する !!!!! for (int i = 0; i < ene.data.length-1; i++) { // データの数値を表示 // ene.data[i][hour] で値を取得する } // ================================================ // 日付を画面に出力 fill(255); text(ene.date + " " + hour + ":00", 5, 20); // データ番号を更新 hour++; if (hour > 23) { hour = 0; dataNum++; // もしファイル数よりも多ければ最初に戻る if (dataNum > filelist.length - 1) { dataNum = 0; } // クラスを再度初期化して次のデータファイルを読み込む ene = new EneTable(filelist[dataNum]); } }
放射状に並べる
前回最後に紹介した、それぞれの場所ごとの棒グラフの配置を、放射状に並べ替えてみました。データ抽出のためのクラス「EneTable」は、前回のものと同じものをそのまま使用しています。
EneTable ene; String[] filelist; int dataNum = 0; int hour = 0; void setup() { // 画面初期化 size(800, 600); noStroke(); frameRate(30); textSize(14); filelist = loadStrings("filelist.txt"); // データ読み込み用クラスEneTableをインスタンス化(初期化) ene = new EneTable(filelist[dataNum]); } // 結果を表示 void draw() { // 背景 background(0); // 日付 fill(255); text(ene.date + " " + hour + ":00", 5, 20); pushMatrix(); translate(width/2, height/2); // 行と列の数だけ繰り返し for (int i = 0; i < ene.data.length-1; i++) { // データの数値を表示 float graphWidth = map(ene.data[i][hour], 0, 1000, 0, width/2); if (graphWidth > width) { graphWidth = width; } float value = map(ene.data[i][hour], 0, 100, 0, 255); if (value > 255) { value = 255; } fill(value, value, 0, value); rect(0, -1, graphWidth, 2); rotate(PI * 2.0 / 109.0); } popMatrix(); // 一つ先のファイルを再読み込み // データ番号を更新 hour++; if (hour > 23) { hour = 0; dataNum++; // もしファイル数よりも多ければリセット if (dataNum > filelist.length - 1) { dataNum = 0; } // クラスを再度初期化する ene = new EneTable(filelist[dataNum]); } }
円の大きさで表現する
それぞれの場所を一つの円とします。この円をランダムな場所に配置して、電力使用の変化を半径に対応させてみます。
EneTable ene; String[] filelist; int dataNum = 0; int hour = 0; PVector[] pos = new PVector[110]; void setup() { // 画面初期化 size(800, 600); noStroke(); frameRate(30); textSize(14); for (int i = 0; i < pos.length; i++) { pos[i] = new PVector(); pos[i].x = random(width); pos[i].y = random(height); } filelist = loadStrings("filelist.txt"); // データ読み込み用クラスEneTableをインスタンス化(初期化) ene = new EneTable(filelist[dataNum]); } // 結果を表示 void draw() { // 背景 background(0); // 行と列の数だけ繰り返し for (int i = 0; i < ene.data.length-1; i++) { // データの数値を表示 float diameter = map(ene.data[i][hour], 0, 1000, 0, 1000); float value = map(ene.data[i][hour], 0, 400, 0, 255); if (value > 255) { value = 255; } fill(31, 127, 255, 200); ellipse(pos[i].x, pos[i].y, diameter, diameter); } // 日付 fill(255); text(ene.date + " " + hour + ":00", 5, 20); // 一つ先のファイルを再読み込み // データ番号を更新 hour++; if (hour > 23) { hour = 0; dataNum++; // もしファイル数よりも多ければリセット if (dataNum > filelist.length - 1) { dataNum = 0; } // クラスを再度初期化する ene = new EneTable(filelist[dataNum]); } }
円を動かす
さらに円の位置をアニメーションしてみましょう。
EneTable ene; String[] filelist; int dataNum = 0; int hour = 0; PVector[] pos = new PVector[110]; PVector[] vel = new PVector[110]; void setup() { // 画面初期化 size(800, 600); noStroke(); frameRate(30); textSize(14); for (int i = 0; i < pos.length; i++) { pos[i] = new PVector(); pos[i].x = random(width); pos[i].y = random(height); vel[i] = new PVector(); vel[i].x = random(-2, 2); vel[i].y = random(-2, 2); } filelist = loadStrings("filelist.txt"); // データ読み込み用クラスEneTableをインスタンス化(初期化) ene = new EneTable(filelist[dataNum]); } // 結果を表示 void draw() { // 背景 background(0); // 行と列の数だけ繰り返し for (int i = 0; i < ene.data.length-1; i++) { // データの数値を表示 float diameter = map(ene.data[i][hour], 0, 1000, 0, 1000); float value = map(ene.data[i][hour], 0, 400, 0, 255); if (value > 255) { value = 255; } fill(31, 127, 255, 200); ellipse(pos[i].x, pos[i].y, diameter, diameter); pos[i].add(vel[i]); if (pos[i].x < 0 || pos[i].x > width) { vel[i].x *= -1; } if (pos[i].y < 0 || pos[i].y > height) { vel[i].y *= -1; } } // 日付 fill(255); text(ene.date + " " + hour + ":00", 5, 20); // 一つ先のファイルを再読み込み // データ番号を更新 hour++; if (hour > 23) { hour = 0; dataNum++; // もしファイル数よりも多ければリセット if (dataNum > filelist.length - 1) { dataNum = 0; } // クラスを再度初期化する ene = new EneTable(filelist[dataNum]); } }
色と大きさでプロット
今度は時間変化に注目します。左から右へ24時間分のデータを並べます。数値が大きいほど、上の位置に大きなサイズで点をうつようにします。
EneTable ene; String[] filelist; int dataNum = 0; int hour = 0; void setup() { // 画面初期化 size(800, 600); noStroke(); frameRate(30); textSize(14); background(0); filelist = loadStrings("filelist.txt"); // データ読み込み用クラスEneTableをインスタンス化(初期化) ene = new EneTable(filelist[dataNum]); } // 結果を表示 void draw() { // 背景 fill(0, 14); rect(0, 0, width, height); fill(255); // 行と列の数だけ繰り返し for (int i = 0; i < ene.data.length-1; i++) { // データの数値を表示 float y = map(ene.data[i][hour], 0, 1600, height, 0); float x = map(hour, 0, 23, 0, width); float col = map(ene.data[i][hour], 0, 1200, 0, 255); float diameter = map(ene.data[i][hour], 0, 1500, 1, 50); fill(col, col, 255-col); ellipse(x, y, diameter, diameter); } // 日付 fill(0); rect(0, 0, 200, 40); fill(255); text(ene.date + " " + hour + ":00", 5, 20); // 一つ先のファイルを再読み込み // データ番号を更新 hour++; if (hour > 23) { hour = 0; dataNum++; } // もしファイル数よりも多ければリセット if (dataNum > filelist.length - 1) { dataNum = 0; } // クラスを再度初期化する ene = new EneTable(filelist[dataNum]); }
パーティクル!
EneTable ene; String[] filelist; int dataNum = 0; int hour = 0; ParticleGen[] particle = new ParticleGen[110]; void setup() { // 画面初期化 size(800, 600); noStroke(); frameRate(30); textSize(14); for (int i = 0; i < particle.length; i++) { particle[i] = new ParticleGen(); } filelist = loadStrings("filelist.txt"); // データ読み込み用クラスEneTableをインスタンス化(初期化) ene = new EneTable(filelist[dataNum]); } // 結果を表示 void draw() { // 背景 background(0); // 行と列の数だけ繰り返し for (int i = 0; i < ene.data.length-1; i++) { // データの数値を表示 int value = int(map(ene.data[i][hour], 0, 1200, 0, 1000)); float diameter = map(ene.data[i][hour], 0, 1200, 5, 100); particle[i].maxParticle = value; particle[i].centerDiameter = diameter; particle[i].draw(); } // 日付 fill(255); text(ene.date + " " + hour + ":00", 5, 20); // 一つ先のファイルを再読み込み // データ番号を更新 hour++; if (hour > 23) { hour = 0; dataNum++; // もしファイル数よりも多ければリセット if (dataNum > filelist.length - 1) { dataNum = 0; } // クラスを再度初期化する ene = new EneTable(filelist[dataNum]); } } class ParticleGen { PVector center; PVector centerVel; float centerDiameter; ArrayList <PVector> pos; ArrayList <PVector> vel; int maxParticle; ParticleGen() { center = new PVector(random(width), random(height)); centerVel = new PVector(random(-0.1, 0.1), random(-0.1, 0.1)); pos = new ArrayList<PVector>(); vel = new ArrayList<PVector>(); maxParticle = 10; centerDiameter = 0; } void draw() { fill(200, 100); ellipse(center.x, center.y, centerDiameter, centerDiameter); PVector v = new PVector(random(-0.5, 0.5), random(-0.5, 0.5)); PVector p = new PVector(center.x, center.y); if (random(100) < maxParticle) { vel.add(v); pos.add(p); if (pos.size() > maxParticle) { pos.remove(0); vel.remove(0); } } for (int i = 0; i < pos.size (); i++) { fill(31, 127, 255, 200); ellipse(pos.get(i).x, pos.get(i).y, 2, 2); pos.get(i).add(vel.get(i)); } center.add(centerVel); if (center.x < 0 || center.x > width) { centerVel.x *= -1; } if (center.y < 0 || center.y > height) { centerVel.y *= -1; } } }
サンプルファイルのダウンロード
ここまでで紹介した全てのサンプルは、下記からダウンロード可能です。