Processingのテキストをそのままやるだけ②(航空機の飛行ログデータの可視化など)
こちらはジャンルなしオンラインもくもく会 Advent Calendar 2024 6日目の記事になります。
最近大学の授業でProcessingというグラフィックプログラミング言語?を使っています。なんと3日後までに自由課題を出さないといけないのですが、まだProcessingが全然わからなくてまずいので、3日後までに完全に理解したいと思っています。
前回の記事は以下になります。
今回はテキストの以下の章に沿って、前回より少し複雑なデータの可視化に挑戦したいと思います。①地震の発生記録の可視化 ②航空機の飛行ログデータの可視化 の2つに、取り組んでみようと思います。
10分経過
地震の発生記録のコードについては、前回記事のために作業した時の人口密度のコードとほとんど同じ(扱う変数が人口密度か、マグニチュードかという違いだけ)なので、前回のコードを流用しつつガーッと作業しています。
PImage 日本地図の画像 = loadImage("map-japan.png");
size(822, 850);
image(日本地図の画像, 0, 0);
Table 表;
表 = loadTable("jishin.csv", "header");
int 行数 = 表.getRowCount();
float[] マグニチュードリスト = new float[行数];
for (int i=0;i<行数;i++){
マグニチュードリスト[i] = 表.getRow(i).getFloat("mag");
}
float マグニチュードMax = max(マグニチュードリスト);
noStroke();
fill(250, 100, 100, 100);
for (int i=0;i<行数;i++){
float 緯度 = 表.getRow(i).getFloat("latitude");
float 経度 = 表.getRow(i).getFloat("longitude");
float マグニチュード = マグニチュードリスト[i];
float x = map(経度, 122, 148, 0, 822);
float y = map(緯度, 24, 46, 850, 0);
float 円の大きさ = map(マグニチュード, 0, マグニチュードMax, 0, 50);
ellipse(x, y, 円の大きさ, 円の大きさ);
}
現状のコードを実行するとこのような感じです。
(Processing、行末にセミコロンを付けないと烈火のように怒ってきて実行すらできないのに、変数名日本語でも動くの意外だなぁ)
20分経過
以下のようにマグニチュードと円の直径を比例させると円の大きさにあまり違いが出ず、なぜだろう?と思っていたのですが、
float 円の大きさ = map(マグニチュード,0 , マグニチュードMax, 0, 50);
マグニチュードの最大値と最小値を表示させるとこのようになっていて、マグニチュードが3未満のものはそもそもデータに入っていないようです
マグニチュードMax: 7.3
マグニチュードMin: 3.3
テキストのコードと同じようにマグニチュードが3.3のときに円の大きさが0に近づくようにすると、テキストと同様に円の大小がはっきりした感じになりました(どちらがいいかは好みだと思います)
float 円の大きさ = map(マグニチュード,マグニチュードMin, マグニチュードMax, 0, 50);
テキストのコードと同じものは大体できました。データのCSVファイルを見てみると、depth(震源の深さ)という項目もありました。今はこの項目は使っていないですが、せっかくなのでこの項目も可視化に含めてみたいと思います。
30分経過
PImage 日本地図の画像 = loadImage("map-japan.png");
size(822, 850);
image(日本地図の画像, 0, 0);
Table 表;
表 = loadTable("jishin.csv", "header");
int 行数 = 表.getRowCount();
float[] マグニチュードリスト = new float[行数];
+ float[] 震源の深さリスト = new float[行数];
for (int i=0;i<行数;i++){
マグニチュードリスト[i] = 表.getRow(i).getFloat("mag");
+ 震源の深さリスト[i] = 表.getRow(i).getFloat("depth");
}
float マグニチュードMax = max(マグニチュードリスト);
float マグニチュードMin = min(マグニチュードリスト);
+ float 震源の深さMax = max(震源の深さリスト);
+ float 震源の深さMin = min(震源の深さリスト);
noStroke();
- fill(250, 100, 100, 100);
for (int i=0;i<行数;i++){
float 緯度 = 表.getRow(i).getFloat("latitude");
float 経度 = 表.getRow(i).getFloat("longitude");
float マグニチュード = マグニチュードリスト[i];
+ float 震源の深さ = 震源の深さリスト[i];
float x = map(経度, 122, 148, 0, 822);
float y = map(緯度, 24, 46, 850, 0);
float 円の大きさ = map(マグニチュード,マグニチュードMin, マグニチュードMax, 0, 50);
+ float 青みの強さ = map(震源の深さ, 震源の深さMin, 震源の深さMax, 0, 255);
+ fill(255 - 青みの強さ, 0, 青みの強さ, 100);
ellipse(x, y, 円の大きさ, 円の大きさ);
}
震源が浅いほど赤っぽく、震源が深いほど青っぽく表示されるようにしました。
地震の発生記録の可視化については、写経+αができたので一旦良しとしたいと思います。
次は飛行機の飛行ログデータの可視化の部分を取り組んでみたいと思います。
40分経過
飛行機の飛行ログデータは以下のような形式です。特定の1つの飛行機の便について、時刻、緯度、経度、高度、速度などの情報が入っていて、順番にみていくとルートがたどれるようになっています。
とりあえず前回からの流用コードで緯度経度のプロットだけしてみます。
PImage 日本地図の画像 = loadImage("map-japan.png");
size(822, 850);
image(日本地図の画像, 0, 0);
Table 表 = loadTable("fly.csv", "header");
int 行数 = 表.getRowCount();
float[] 経度リスト = new float[行数];
float[] 緯度リスト = new float[行数];
for (int i = 0; i<行数; i++){
経度リスト[i] = 表.getRow(i).getFloat("Lon");
緯度リスト[i] = 表.getRow(i).getFloat("Lat");
}
for (int i = 0; i<行数; i++){
float 経度 = 経度リスト[i];
float 緯度 = 緯度リスト[i];
float x = map(経度, 122, 148, 0, 822);
float y = map(緯度, 24, 46, 850, 0);
ellipse(x, y, 10, 10);
}
実行すると、こんな感じになりました。これだけでもルートがはっきり分かります。
50分経過
テキストと同じように高度によって色を変化させるのも楽しそうなのですが、せっかくなのでテキストに沿って作業するだけでなく自分で考えて作業もしてみようと思いました。
そこで考えたのは、位置のプロットを、待ち時間を入れながらゆっくり行うことで、ルートを示す簡単なアニメーションのようにできないか、ということです。
↓イメージ図。現在位置を示すアイコンがゆっくり動いていく、みたいにしたい
なのですが...Processingで待ち時間を制御する方法がいまいちわからない...
アニメーション的なことをしたい場合はdraw()という関数を使うようなので、それをまずは取り入れてみようと思います。以下のテキストをちゃんと読み返します...
1時間経過
雰囲気で、初期設定のsetup()関数、繰り返しの動きのdraw()関数に切り分けてみたのですが、配列の定義や代入の部分でずっと怒られています...
多分これはProcessingの特徴というより(Processingのベースとなっている)Javaの特徴だと思うのですが、配列を定義するときに配列の長さも定義しなくてはいけない(そのため、あらかじめ配列の必要な長さをだいたい知っていないといけない)ということが、PythonやJavaScriptに慣れた身からすると、すごく難しく感じられます...
PImage 日本地図の画像;
int 行数;
float[] 経度リスト = new float[行数]; //この行で問題
float[] 緯度リスト = new float[行数]; //この行で問題
void setup(){
日本地図の画像 = loadImage("map-japan.png");
size(822, 850);
Table 表 = loadTable("fly.csv", "header");
行数 = 表.getRowCount();
image(日本地図の画像, 0, 0);
noStroke();
for (int i = 0; i<行数; i++){
経度リスト[i] = 表.getRow(i).getFloat("Lon"); //この行で問題
緯度リスト[i] = 表.getRow(i).getFloat("Lat"); //この行で問題
}
}
int i = 0;
void draw(){
if (i < 行数){
float 経度 = 経度リスト[i];
float 緯度 = 緯度リスト[i];
float x = map(経度, 122, 148, 0, 822);
float y = map(緯度, 24, 46, 850, 0);
ellipse(x, y, 10, 10);
i++;
} else {
exit();
}
}
1時間10分経過
「行数」の値を決める前に「行数」を参照していたので、怒られていたのですが、「行数」の値を決めるためにはファイルの読み込みが必要で、ファイルの読み込みを関数外で行ったらなんか怒られてしまったので、かんしゃくを起こしてしまい、以下のようなコードにしました。(Processingを使ったことがある方がもしいたら、もっといいやり方があったら教えていただきたいです...)
長さ10000の、配列や!
- float[] 経度リスト = new float[行数];
- float[] 緯度リスト = new float[行数];
+ float[] 経度リスト = new float[10000];
+ float[] 緯度リスト = new float[10000];
なんとかエラーが出なくなりました。
そして遂に、その瞬間(とき)が...
と、飛んだぞ~~~
現在位置のアイコン入れたり、時間経過をちゃんと時刻データに比例させたりなど(現状のコードだと時間の流れがちょっとおかしい)、まだまだ改善の余地はあるのですが、なにはともあれ、なんとなくイメージに近いものはできたので、にっこりしています。
最後までお読みいただき、ありがとうございました!
Discussion