C#から2次元データファイルをGNUPLOTでプロットさせる

 測定プログラム共通で使いまわせるようC#上からGNUPLOTコンソールへコマンドを送り、データをグラフ出力させる機能を持ったGnuplotGraphクラスを書いてみた。ある測定システムを組んでる際、何も考えずに組んだら鬼のようなスパゲッティーコードになってしまったので、少しでも改善できるようなるべくGNUPLOT関連以外の事柄と切り離して書きなおしたものです。結構長いので先に利用側のコードを…。

GnuplotGraph利用側のコードC

using Gnuplot;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

namespace GnuplotUsing
{
    [TestFixture]
    class GnuplotGraphTest
    {
        [Test]
        public void GnuplotGraphDrawTest()
        {
            var gnuGraph = new GnuplotGraph();
            var sampleDataInc = new GraphData(@"[datafilepath]",
                "NaCl Singlecrystal #1 increase",GraphData.Axis.inverse);
            var sampleDataDec = new GraphData(@"[datafilepath",
                "NaCl Singlecrystal #1 decrease", GraphData.Axis.inverse);

            gnuGraph.AddGraphData(sampleDataInc);
            gnuGraph.AddGraphData(sampleDataDec);
            gnuGraph.Draw();
        }
    }
}

 こんなかんじに、データのパス、ラベルなど、データに関するものはGraphDataオブジェクトにぶち込んで、それをGnuplotGraphのインスタンスに追加して、最期にDraw()するという用な感じで、結構お気に入り。GnuplotGraphとGraphDataクラスの実装は以下の様な感じ(LINQ勉強中なので一箇所無理やり使ってみたんですが気にしないでください)。

GnuplotGraphクラスとGraphDataクラスC

using System.Linq;
using System.Collections.Generic;

namespace Gnuplot
{
    /// <summary>
    /// GNUPLOT Graph Class
    /// </summary>
    public class GnuplotGraph
    {
        #region enum
        /// <summary>
        /// Key position of graph
        /// </summary>
        public enum Position
        {
            left,
            right,
            top,
            bottom,
            outside,
            below,
            unset,
        }
        #endregion



        #region Internal Constant
        private static string gnuplotExeFilePath = "gnuplot.exe";
        private string graphOutputPath = "graph.png";
        private List<GraphData> plotData = new List<GraphData>();
        #endregion



        #region Property
        //Graph Settings
        public int Width { get; set; }
        public int Height { get; set; }
        public decimal FontScale { get; set; }
        public bool SetXaxisLogScale = false;
        public bool SetYaxisLogScale = false;

        //Graph Labels
        public string GraphTitle = "";
        public string XaxisLabel = "";
        public string YaxisLabel = "";

        //Key Setting(When KeyPosition was set to 'unset', unset key of graph.)
        public Position KeyPosition = Position.below;
        #endregion



        #region Public Method
        /// <summary>
        /// Constructor of this class
        /// </summary>
        /// <param name="width">graph width</param>
        /// <param name="height">graph height</param>
        /// <param name="fontScale">graph fontscale</param>
        public GnuplotGraph(int width = 800, int height = 600, decimal fontScale = 1.2M)
        {
            this.Width = width;
            this.Height = height;
            this.FontScale = fontScale;
        }

        /// <summary>
        /// Drawing graph. This will take some seconds, so wait some soconds to open the graph file.
        /// </summary>
        public void Draw(
            bool redirectStandardInput = true,
            bool createNoWindow = true,
            bool useShellExecute = false)
        {
            //Initialization for calling GNUPLOT execution file
            var GnuConsole = new System.Diagnostics.Process();
            GnuConsole.StartInfo.FileName = gnuplotExeFilePath;
            GnuConsole.StartInfo.CreateNoWindow = createNoWindow;
            GnuConsole.StartInfo.UseShellExecute = useShellExecute;
            GnuConsole.StartInfo.RedirectStandardInput = redirectStandardInput;
            GnuConsole.Start();

            WriteCommandsForGnuConsole(GnuConsole);
            GnuConsole.StandardInput.WriteLine("exit");
            GnuConsole.WaitForExit();
        }

        /// <summary>
        /// Add Graph Data Object to this class
        /// </summary>
        /// <param name="graphData">Graph Data</param>
        public void AddGraphData(GraphData graphData)
        {
            plotData.Add(graphData);
        }

        /// <summary>
        /// Set Gnuplot Execution File Path
        /// </summary>
        /// <param name="path">Gnuplot Execution File Path</param>
        public void SetGnuplotExeFilePath(string path)
        {
            if(System.IO.File.Exists(path))
                GnuplotGraph.gnuplotExeFilePath = path;
            else
                throw new System.IO.FileNotFoundException();
        }

        /// <summary>
        /// Set graph output file path
        /// </summary>
        /// <param name="path">Path</param>
        public void SetGraphOutputPath(string path)
        {
            this.graphOutputPath = path;
        }
        #endregion



        #region Private Method
        private void WriteCommandsForGnuConsole(System.Diagnostics.Process gnuConsole)
        {
            //Output File Settings
            gnuConsole.StandardInput.WriteLine("set terminal 'pngcairo' monochrome size {0},{1} enhanced font 'Times' fontscale {2}", Width, Height, FontScale);
            gnuConsole.StandardInput.WriteLine(@"set datafile separator ','");
            //gnuConsole.StandardInput.WriteLine(@"set output '{0}'", graphOutputPath);  

            //Axis Settings
            if (SetXaxisLogScale)
                gnuConsole.StandardInput.WriteLine("set logscale x");
            if (SetYaxisLogScale)
                gnuConsole.StandardInput.WriteLine("set logscale y");

            //Label Settings
            if (!XaxisLabel.Equals(""))
                gnuConsole.StandardInput.WriteLine(@"set xlabel '{0}'", XaxisLabel);
            if (!YaxisLabel.Equals(""))
                gnuConsole.StandardInput.WriteLine(@"set ylabel '{0}'", YaxisLabel);
            if (!GraphTitle.Equals(""))
                gnuConsole.StandardInput.WriteLine(@"set title '{0}'", GraphTitle);

            //Key Settings
            if (!KeyPosition.Equals(Position.unset))
                gnuConsole.StandardInput.WriteLine("set key {0}", KeyPosition.ToString());

            //Data Plotting
            plotData.Select<GraphData, int>(
                (data, index) =>
                {
                    string plotQuery;

                    if (index.Equals(0))
                        plotQuery= string.Format(@"plot '{0}' using {1} w p title '{2}'", data.DataPath, data.PlotUsing, data.DataLabel);
                    else if (!index.Equals(plotData.Count - 1))
                        plotQuery = string.Format(@"replot '{0}' using {1} w p title '{2}'", data.DataPath, data.PlotUsing, data.DataLabel);
                    else
                    {
                        gnuConsole.StandardInput.WriteLine(@"set output '{0}'", graphOutputPath);
                        plotQuery = string.Format(@"replot '{0}' using {1} w p title '{2}'", data.DataPath, data.PlotUsing, data.DataLabel);
                    }

                    gnuConsole.StandardInput.WriteLine(plotQuery);
                    return 0;
                }
            ).ToArray();
        }
        #endregion
    }



    /// <summary>
    /// GNUPLOT Graph Data Class(CSV)
    /// </summary>
    public class GraphData
    {
        public enum Axis
        {
            linear,
            inverse,
        }

        public int XDataColumn { get; protected set; }
        public int YDataColumn { get; protected set; }
        public Axis XAxis { get; set; }
        public Axis YAxis { get; set; }
        public string DataLabel { get; protected set; }
        public string DataPath { get; protected set; }

        public string PlotUsing
        {
            get
            {
                return 
                XAxis.Equals(Axis.linear) ?
                    YAxis.Equals(Axis.linear) ? string.Format("{0}:{1}", XDataColumn, YDataColumn) : string.Format("(${0}):(1.0/${1})", XDataColumn, YDataColumn) :
                    YAxis.Equals(Axis.linear) ? string.Format("(1.0/${0}):(${1})", XDataColumn, YDataColumn) : string.Format("(1.0/${0}):(1.0/${1})", XDataColumn, YDataColumn);
            }
        }


        /// <summary>
        /// Constructor of this class.
        /// </summary>
        /// <param name="dataPath">CSV-Formatted Data File Path</param>
        /// <param name="dataLabel">Data Label</param>
        /// <param name="xAxis">X-axis Type</param>
        /// <param name="yAxis">Y-axis Type</param>
        /// <param name="xDataColumn">X-data column of data file</param>
        /// <param name="yDataColumn">Y-data column of data file</param>
        public GraphData(
            string dataPath,
            string dataLabel = "",
            Axis xAxis = Axis.linear,
            Axis yAxis = Axis.linear,
            int xDataColumn = 1,
            int yDataColumn = 2
            )
        {
            DataPath = dataPath;
            DataLabel = dataLabel;
            XAxis = xAxis;
            YAxis = yAxis;
            XDataColumn = xDataColumn;
            YDataColumn = yDataColumn;
        }
    }
}

もうちょい改善の余地はあるけど、ある程度使い物にはなるレベルにはなったかな…。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください