最終課題に向けて: 芸大電力消費量のデータをProcessing.jsでビジュアライズする 02
今回も引き続き、芸大の電力使用料のデータを使用したProcessing.jsによるビジュアライズにとりくみます。今回は、より自由な発想でデータを使った表現に挑戦していきましょう。実際に動くサンプルを紹介しながら、考えていきます。
制作テンプレート
前回解説した、全てのデータファイルを順番に読み込んで「data[行][列]」という二次元配列の形式に変換するところまでの、テンプレートを作成しました。この制作テンプレートを出発点にオリジナルの表現に挑戦すると良いでしょう。データファイルを読み込むためのクラス「EneTable」クラスは、前回のものをそのまま使用しています。下記からZip形式でデータ一式をダウンロード可能です。
[code lang=”java”]
/*
* 情報編集(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]);
}
}
[/code]
放射状に並べる
前回最後に紹介した、それぞれの場所ごとの棒グラフの配置を、放射状に並べ替えてみました。データ抽出のためのクラス「EneTable」は、前回のものと同じものをそのまま使用しています。
[code lang=”java”]
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]);
}
}
[/code]
円の大きさで表現する
それぞれの場所を一つの円とします。この円をランダムな場所に配置して、電力使用の変化を半径に対応させてみます。
[code lang=”java”]
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]);
}
}
[/code]
円を動かす
さらに円の位置をアニメーションしてみましょう。
[code lang=”java”]
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]);
}
}
[/code]
色と大きさでプロット
今度は時間変化に注目します。左から右へ24時間分のデータを並べます。数値が大きいほど、上の位置に大きなサイズで点をうつようにします。
[code lang=”java”]
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]);
}
[/code]
パーティクル!
[code lang=”java”]
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;
}
}
}
[/code]
サンプルファイルのダウンロード
ここまでで紹介した全てのサンプルは、下記からダウンロード可能です。