Javaからgnuplotへデータを送る

Javaで生成したデータを与えgnuplotにチャートを描かせてJava側でそれを表示する。
gnuplotによって正弦の計算をさせるのではなく、
Java側で正弦の値を一周期21点分π/5ラジアンごとに計算してgnuplotに与える。
基本的には以前に示したコードそのままである。
随分前なのでいちおう全文示しておく。
変更点は、クラス名と環境で変わるであろうgnuplotの位置以外は、
Javaからplotコマンドを出力するところから、
gnuplotの出力をフラッシュするところまでと、
その間にデータを出力するための静的メソッドwriteDataを加えただけである。

GnuplotUser2.java
import java.awt.BorderLayout;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;

public class GnuplotUser2 implements Runnable {
    private static final String GNUPLOT = "/usr/bin/gnuplot";

    /** plot a sine curve for one period */
    private static void writeData(PrintWriter out) {
        for (int i = 0; i <= 20; i++) {
            double x = 2 * Math.PI * i / 20;
            double y = Math.sin(x);
            out.println(x + " " + y);
        }
    }

    public static void main(String[] args) throws IOException {
        Process p = new ProcessBuilder(GNUPLOT, "-").start();
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(p.getOutputStream())));
        BufferedInputStream in = new BufferedInputStream(p.getInputStream());
        BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        StringWriter log = new StringWriter();
        out.println("set terminal png");    // output png to stdout
        out.println("plot '-' with linespoints"); // plot data following with lines and points
        writeData(out);                     // output data
        out.println("e");                   // mark end-of-data
        out.println("set output");          // flush gnuplot output
        out.close();                        // flush and close stdout
        ImageIcon icon = new ImageIcon(ImageIO.read(in));   // create icon from stdin
        in.close();                                         // close stdin
        while (true) {                      // create log from stderr
            int c = err.read();
            if (c == -1) break;
            log.append((char)c);
        }
        err.close();
        try {
            p.waitFor();
        } catch (InterruptedException e) {
        }
        SwingUtilities.invokeLater(new GnuplotUser2(icon, log.toString()));
    }

    private ImageIcon icon;
    private String message;

    private GnuplotUser2(ImageIcon icon, String message) {
        this.icon = icon;
        this.message = message;
    }

    public void run() {
        JFrame f = new JFrame("sin(x)");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new JLabel(icon), BorderLayout.CENTER);
        f.add(new JScrollPane(new JTextArea(message, 4, 40)), BorderLayout.SOUTH);
        f.pack();
        f.setVisible(true);
    }
}

この変更はJavaでどうこうというよりも、
gnuplotでの操作を変えただけのことである。
gnuplotのplotコマンドにおいて、
データファイルの指定を'-'にしてやると、
その直後に直接データを入力していくモードになる。
モードの終了はeを与えてやればよい。
たとえば、以下で放物線的なプロットができる。

gnuplot> plot '-' with linespoints
input data ('e' ends) > 0 0
input data ('e' ends) > 1 1
input data ('e' ends) > 2 4
input data ('e' ends) > 3 9
input data ('e' ends) > 4 16
input data ('e' ends) > 5 25
input data ('e' ends) > e
gnuplot> 

gnuplotの対話モードなので実際に入力するのは>より右である。
上のJavaコードは、人間が標準入出力を通してgnuplotと対話するかわりに、
Javaが標準入出力を通して対話しているだけである。
したがって、gnuplotにおいて自分が行っている操作を、
Java内で文字列として構築してgnuplotに送ってやればいい。
上は正弦波形だったが、任意のプロットは与えるx座標とy座標の対を変えれば済む。
つまり、上のコードでいえばメソッドwriteDataの中身を変えるだけである。
あまりにデータ点数が多いようなら一時ファイルを作成して、
それをplotコマンドに与えた方がいいかもしれない。