16
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Processingでお手軽3次元データビジュアライズ(地形編)

Last updated at Posted at 2021-12-20

本記事は、Processing Advent Calendar 2021の21日目の記事です。

昨年一昨年に続き、Processingでお手軽に3次元データビジュアライズするシリーズです。

はじめに

昨今、「VR」「メタバース」「デジタルツイン」という言葉に代表されるように3次元表現が注目されつつありますが、Processingでも意外と簡単に3次元の地形を再現することができます。今回は「標高モデル」というオープンデータのビジュアライズとクリエイティブ・コーディングを組み合わせる内容です。

データさえ用意すれば日本全国どこの地形でも表示できますが、今回はお正月向けに富士山周辺の地形を使って遊んでみます。

サンプルコードはこちらからダウンロードできます。
文中のサンプルコードのファイル名と対応しているので適宜参照しながらお読みください。

完成例

step4.gif

前提条件

使用するライブラリは次の通りです。マウスで視点操作を行うので、ライブラリはPeasyCamを使います。

  • PeasyCam 302

素材の準備

使用するデータ

富士山周辺の標高データを国土地理院のサイトから取得します。今回利用するのは、基盤地図情報数値標高モデル(表赤枠部分)を使います。(表の引用元利用規約

table.jpg

標高データを探す

タイル座標確認ページにアクセスして、富士山周辺で目的の標高データが含まれるタイル(区画)を調べます。

map.png

今回は、「10/906/404」と表示されているタイルが良さそうです。この3つの数値はそれぞれズームレベル(Z)、 X座標(X)、Y座標(Y)を表します。これらは次のステップで使います。

データの取得

目的のタイルを調べたら、次のURLにアクセスします。(URLの最後の部分が先ほど調べたタイルのZ/X/Yに一致しています)

https://cyberjapandata.gsi.go.jp/xyz/dem_png/10/906/404.png

表示されたPNG画像を保存しておきましょう。

10-906-404.png

画像の色情報から標高を求める

等高線のように見えるこの画像は、縦横が256ピクセルの画像で、1つのピクセルの色情報がその位置の標高の数値に対応しています。
色情報(RGB)から標高を求めるには、標高(m) = R * 256 * 256 + G * 256 + B *0.01 の計算式を使います。

注:(2021/12/31追記)
海面(標高0)や標高が無効のピクセルは(R,G,B) = (128,0,0)となっています。
本記事のサンプルでは海面を含まないのでこの条件式を加えていませんが、海面を含むタイルを使う場合は、上の計算式に加えて、この条件で標高が0になるように条件式を加えてください。

標高タイルの詳細仕様

様々な作例

ダウンロードした標高タイル画像をProcessingにロードして遊んでみましょう。

1. 標高データでboxを並べる

まずは、標高の位置にboxを並べてみましょう。タイル画像の色情報を読み出し、前述した計算式で標高を求めています。(サンプルコード:terrain3d_step1)

結果
step1.png
少し高さを強調していますが、富士山の美しい形が現れました。
以下はコードの中身です。Processingで画像を読み込み、ピクセルの色情報にアクセスするコードを書いたことがある人にはとっては特に難しくはないのではないでしょうか。

terrain3d_step1.pde
import peasy.*; //PeasyCamライブラリをインストールしてインポート
PeasyCam cam;

PImage loadPng;

void setup() {
  size(1024, 768, P3D);
  //カメラの初期化
  cam = new PeasyCam(this, 700);
  //標高タイル画像を読み込む
  loadPng = loadImage("10-906-404.png");
}

void draw(){
  background(0);
  noStroke();
  
  float pixMargin = 5.0;
  for(int j=0; j<loadPng.height; j++){
    for(int i=0; i<loadPng.width; i++){
      
      //画像からピクセルのカラー(RGB)を取得
      color pxColor = loadPng.get(i,j);
      float red = red(pxColor);
      float green = green(pxColor);
      float blue = blue(pxColor);
      
      float x = (i - loadPng.width /2) * pixMargin;
      float y = (j - loadPng.height /2) * pixMargin;
      //R,G,Bの値から高さを求める
      float z = (red * 256 * 256 + green * 256 + blue) *0.0005;
      pushMatrix();
      
      translate(x,y,z);
      box(2);
      
      popMatrix();
    }
  }
  
}

2. メッシュで表示

大量のboxを表示するとややパフォーマンスが落ちるので、PShapeを使って点群やメッシュで表示してみます。(サンプルコード:terrain3d_step2)

結果

点群で表示
step1_points.png

メッシュで表示
step2_mesh.png

3. 富士山にノイズを加える

なんと罰当たりなことに富士山にノイズを加えてみます。いやいや、上下を反転すれば湖面に映る逆さ富士のようで風情あるイメージになりますよ(たぶん)。(サンプルコード:terrain3d_step3)
step3.png

4. 富士山に粉雪を舞い散らかす

アニメーションする点群を用意して、雪のように舞い散らかします。これはだいぶ風流ですね!(サンプルコード:terrain3d_step4)

step4.gif

5. より広域を表示する

周辺のタイル画像をダウンロードして結合すれば、ソースコードにほとんど手を加えることなく広域の地形を再現できます。
ここでは、9枚の画像を結合して768 x 768のタイル画像を用意しました。(サンプルコード:terrain3d_step5)

タイル画像

mtfuji_10-904402-904404.png

結果

ライティングなどを調整した結果はこのようになりました。リアルな地形を様々な視点から眺めることができます。

step5.png

終わりに

オープンデータを使って、データビジュアライズとクリエイティブコーディングを融合させる表現はまだまだ探究の余地がありそうです。ぜひ面白い作品にトライしてください。

16
17
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?