ダイレクトアクセスバイナリファイルから数値データを読み取る方法(Javaを利用)

ここでは、気象庁解析雨量GPVのGrib2ファイルを解凍して得られたバイナリ・ファイルを例に取り、ダイレクト・アクセス・ファイルから数値データを読み取る方法を解説していきたいと思います。

 

1. はじめに

前回の記事では、気象庁解析雨量GPVのGrib2ファイルをwgrib2というアプリを用いて圧縮のかかっていないバイナリファイルに解凍したうえ、雨量の分布状況を図化する方法を解説しました。今回は、シミュレーションや数値解析をするために欠かすことができないバイナリ・ファイルから数値データを読み取る方法をこのバイナリファイルを用いて解説していきます。

 

2. 実行環境

まず、対象とするバイナリファイルは前回の記事のときに作成した気象庁解析雨量GPVの九州地域周辺を抽出したものとします。バイナリファイルの仕様については、付属しているコントロールファイルから確認することができます。
統合開発環境は、IntelliJとし、使用言語はJavaとします。コンピュータはMacBookとします。最近、WindowsのノートPCが壊れてしまい、急遽MacBookを使うこととしました。

 

実行環境の仕様
項目 内容
対象ファイル 気象庁解析雨量GPV(九州地域周辺)
コンピュータ/OS MacBook Air(13-inch 2015) / macOS Monterey version 12.7.1
使用アプリ wgrib2(解凍)、opengrads(可視化)

 

今回のJavaのプログラムでは、バイナリファイルから数値を読み取り、単精度実数型(float)の二次元配列に格納し、それらの数値をESRI ASCII Gridフォーマットに出力します。ESRI ASCII GridフォーマットのファイルはGISアプリで読むことができるので、GISアプリで可視化して結果を確認することができます。

3. ソースコードの解説

バイナリファイルから数値データを読み取るJavaプログラム「Read_JMA_GRD.java」を下記に示します。

L9、10で、データの行列のサイズを定義しています。これがL23で宣言しているfloat型の二次元配列meshVal[][]のサイズにもなります。

L12で入力データとなるバイナリファイルのファイルパスを定めています。同様にL13で出力ファイルパスを定義しています。

L21で、バイナリデータを読み込むためのインターフェースDataInputStreamを準備します。

L22で、float型の変数を1個だけ格納できるバイト列の配列変数bufを用意します。DataInputStreamから読み取ったfloat型のデータ(バイト列)を一時的にこの配列に保存します。

L23で、最終的に読み取ったfloat型のデータが格納されるfloat型の二次元配列meshVal[][]を宣言します。配列の大きさは、バイナリデータで格納されたデータの行・列のサイズと同じです。

L25からL33の二重ループでバイナリファイルを読み取ったデータをmeshVal[][]に1個ずつ格納していきます。

L27行目でバイト列変数のバイトオーダーをLittle Endianにします。

L28行目でバイナリファイルから4バイト分のデータを読み取り、バイト列変数bufに格納します。

L29行目でバイト列変数のバイトオーダーをBig Endianにします。

L30行目でバイト列に格納されているデータをfloat型二次元配列meshVal[][]に1個格納します。

以下、この手続きを行・列分繰り返します。

L27とL29行目はIntel社のCPUをもつパソコンで実証していますが、機種依存する項目ですので他の会社のCPUでは異なる記述する必要があるかもしれません。

L35行で、テキストファイルにデータを書き出すためのインターフェースPrintWriterを宣言します。
L51からL56のループでfloat型の二次元配列meshVal[][]に格納されたデータを1個ずつテキストファイルに書き出しています。

 

package net.gofort;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class Read_JMA_GRD {
    public static void main(String[] args) {

        int NCOLS = 420;
        int NROWS = 600;

        String inFile = "Z__C_RJTD_20190630020000_SRF_GPV_Ggis1km_Prr60lv_Aper10min_ANAL_grib2_cropped_kyushu.grd";
        String outFile = "test_out.asc";

        double DX = 0.0125;
        double DY = 0.008333333333333333;
        double XLLCENTER = 128.0;
        double YLLCENTER = 30.0;

        try {
            DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(inFile)));
            ByteBuffer buf = ByteBuffer.allocate(4);
            float[][] meshVal = new float[NCOLS][NROWS];

            for (int j = 0; j < NROWS; j++) {
                for (int i = 0; i < NCOLS; i++) {
                    buf.order(ByteOrder.LITTLE_ENDIAN);
                    buf.putFloat(dis.readFloat());
                    buf.order(ByteOrder.BIG_ENDIAN);
                    meshVal[i][j] = buf.getFloat(0);
                    buf.clear();
                }
            }

            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(outFile)));

            pw.print("NCOLS   ");
            pw.println(Integer.toString(NCOLS));
            pw.print("NROWS   ");
            pw.println(Integer.toString(NROWS));
            pw.print("XLLCENTER   ");
            pw.println(Double.toString(XLLCENTER));
            pw.print("YLLCENTER   ");
            pw.println(Double.toString(YLLCENTER));
            pw.print("DX   ");
            pw.println(Double.toString(DX));
            pw.print("DY   ");
            pw.println(Double.toString(DY));
            pw.println("NODATA_VALUE  9.999E+20");

            for (int j = NROWS - 1; j >= 0; j--) {
                for (int i = 0; i < NCOLS; i++) {
                    pw.print(String.format("%6.2f  ", meshVal[i][j]));
                }
                pw.println();
            }

            pw.flush();
            pw.close();
            dis.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

 

4. サンプルプログラムの動かし方

実際にここで解説した方法を試すためのサンプルプログラムの使い方について解説します。プログラム自体は下記のダウンロードリンクからダウンロードすることができます。

本記事からダウンロードしたサンプルプログラムのファイル「read_jma_grd.zip」を解凍するとread_jma_grdというフォルダが現れます。

統合環境IntelliJがすでにインストールている場合、もっとも簡単な動かしかたは、read_jma_grdというフォルダの下にあるファイル「​​build.gradle.kts」または「settings.gradle.kts」をダブルクリックすると、自動的にIntelliJからJavaのプロジェクトを開くことができます。あとは、IntelliJのなかからソースコードを編集したり、実行したりすることができます。

JavaのビルドツールであるGradleがインストールされている場合は、サンプルソースコードのプロジェクトのトップディレクトリでターミナル画面から「gradle build」と打ち込むと、コンパイルすることができます。

そして、以下のコマンドを打ち込むと、実行することができます。

java -cp ./build/classes/java/main net/gofort/Read_JMA_GRD

サンプルプログラムが実行されると、ソースコードのトップディレクトリに「text_out.asc」というテキストファイルが出力されます。テキストファイルなので、テキストファイルで中身を見ることもできます。

プログラムを編集することにより、異なるバイナリファイルを読み取れるようになります。
具体的には、L12を編集することにより対象とするバイナリファイルを変更したり、必要に応じてL9、L10を編集してバイナリファイルの行列のサイズに合わせることができます。

出力された「text_out.asc」はESRI ASCII GridファイルなのでGISアプリから開いて、バイナリファイルに保存されている降雨データの分布状況を確認することができます。

 

GISアプリ(QGIS)で可視化したバイナリファイル

 

5. まとめ

この記事では、気象庁の解析雨量GPVをダイレクトアクセスバイナリファイルとして解凍してファイルを例として、そのファイルからJavaで数値データを読み取る方法を解説しました。ポイントをまとめると下記のようになります。

  • Javaでダイレクトアクセスバイナリファイルから数値データを読み取るには、DataInputStreamというインターフェースを使います。
  • さらに、DataInputStreamに格納されたバイトストリームのバイトオーダーを変換するために、ByteBufferというインターフェースを用います。
  • ByteBufferに格納されたバイトストリームから単精度型float型の変数に読み取った数値を代入することができます。

 

以上、最後まで読んでいただきありがとうございました。

 

 

 

最新情報をチェックしよう!