普通に考えるとしまっておくのに最適な場所は配列ではないかと考えると思います。しかし、今回のプログラムはデータの大きさが既知ではないことを想定しています。通常の配列は宣言時にデータの大きさを宣言しなければならず、通常の配列は使えないと当初から思っていました。
データの大きさが決まっていないような場合に、動的にサイズを変える仕組みがある入れ物はないだろうか?と考えて、探してみたら、C#にはArrayListというものがあります。前のエントリーでちょっと出て来ましたね。
前回のエントリーに出てきたプログラムを改造して、TextFieldParserで分割したフィールドデータをArrayListに格納していくコードを作成しました。動かした結果は前回と同じですが、内部的にはArrayListの行を追加しながら画面にも表示する、という動作になっています。詳細は、プログラム中のコメントをご覧下さい。
なお、CSVファイルの中身ですが、1行の作りが
測定点のX座標,測定点のY座標,測定点のZ座標,測定点のデータ本体
という作りになっています。1行に測定点1点分のデータが入っており、測定点の数だけ行がある、という構造です。今回のプログラム評価用のデータは、全て整数です。
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.VisualBasic.FileIO; namespace getDataFromCSV { class Program { //string line = ""; //ArrayList al = new ArrayList(); public static ArrayList x_axis = new ArrayList(); // X軸データ用ArrayList public static ArrayList y_axis = new ArrayList(); // Y軸データ用ArrayList public static ArrayList z_axis = new ArrayList(); // Z軸データ用ArrayList public static ArrayList dt = new ArrayList(); // データ本体用ArrayList static void Main(string[] args) { getData("c:\\users\\gustav\\検討用テストデータ.csv"); Console.ReadKey(); } static void getData(string file) { try { using ( TextFieldParser parser = new TextFieldParser(file, System.Text.Encoding.GetEncoding("Shift_JIS")) ) { parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); // 区切り文字はコンマ parser.CommentTokens = new string[1] { "#" }; int line = 0, col = 0; while (!parser.EndOfData) { string[] row = parser.ReadFields(); x_axis.Add(row[0]); // ここでArreyListの行を追加 y_axis.Add(row[1]); z_axis.Add(row[2]); dt.Add(row[3]); Console.Write(line + " : "); //画面出力 Console.Write("X=" + (string)x_axis[line]); // メンバーの参照はこのように行う Console.Write(" | Y=" + (string)y_axis[line]); Console.Write(" | Z=" + (string)z_axis[line]); Console.Write(" | DATA=" + (string)dt[line]); Console.WriteLine(" "); ++line; } Console.WriteLine("Line 3236 " + (string)dt[3235]); // 行番号とデータが合っているかどうかの確認用 } } catch (Exception e) { Console.WriteLine(e.Message); } } } }
これでCSVから各フィールドのデータをstringデータながら拾い上げて配列におさめることができるようになりました。
今回は、さらにこの後、データを適切に型変換して、実際に使って見るところまで進めたいと思います。
以下のリストは、以下の処理をしています。
- CSVファイルからデータを読み出して、配列に格納
- 配列データを順に読んで、型変換を行う。(TextFieldParserはstring型のデータを返す仕様)
- 型変換したデータに対して、座標データはデータが持つ座標系とウインドウの座標系の変換を行う(ウインドウの座標で描画しなければならない)
- データ本体は、データの中身に応じて描画するピクセルの色を割り当てる
- 上記で得られたデータを使って、X座標とY座標とデータの中身でBitmapオブジェクトに描画
- PictureBoxのImageプロパティにBitmapを設定
- マウスカーソルの座標を取得し続け、ウインドウの上部に座標を表示
データは、あくまでも図のデータ座標系座標原点の場所が原点となっているデータなので、それをPictureBoxの座標系に変換するには、データ座標系の原点のPictureBox内に於ける座標を加えてあげれば変換できます。
このプログラムでは、Program.cs内のstatic class Constantsで計算の為のオフセット値を定数として定義しています。
また、マウスカーソルの座標を取得し続ける処理では、PictureBox座標をデータ座標に変換してから、ウインドウに表示しています。この処理も考え方は同じで、PictureBox座標で取得されるマウスカーソル座標からオフセット分を引き算して、データ座標を求めています。
ファイルProgram.cs
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace CSVtoData { static class Constants // Mainメソッドが入っているファイルで、定数の定義をしています。 { public const string FileName = "c:\\users\\gustav\\検討用テストデータ.csv"; public const int offset_x = 640 / 2; public const int offset_y = 500; public const int offset_z = 0; } static class Program { ////// アプリケーションのメイン エントリ ポイントです。 /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } }
ファイルForm1.cs
using System; using System.Collections.Generic; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.VisualBasic.FileIO; namespace CSVtoData { public partial class Form1 : Form { public static int mouse_x = 0, mouse_y = 0; // このForm1クラスの中で、広域的に参照できるようにこの定義方法です。 public static ArrayList x_axis = new ArrayList(); public static ArrayList y_axis = new ArrayList(); public static ArrayList z_axis = new ArrayList(); public static ArrayList dt = new ArrayList(); public Form1() { InitializeComponent(); this.MaximumSize = this.Size; this.MinimumSize = this.Size; Bitmap img = new Bitmap(600, 720); Color col = new Color(); try { using (TextFieldParser parser = new TextFieldParser(Constants.FileName, System.Text.Encoding.GetEncoding("Shift_JIS"))) { parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); // 区切り文字はコンマ parser.CommentTokens = new string[1] { "#" }; int line = 0; while (!parser.EndOfData) { //++line; //col = 0; string[] row = parser.ReadFields(); // 1行読んで、要素に分解、配列rowに格納 x_axis.Add(row[0]); // ArrayListにX軸座標を追加 y_axis.Add(row[1]); // ArrayListにY座標を追加 z_axis.Add(row[2]); // ArrayListにZ座標を追加 dt.Add(row[3]); // ArrayListにデータ本体を追加 ++line; } } } catch (Exception e) // TextFieldParser処理中にエラーが出たら、エラーメッセージを出してプログラム終了 { MessageBox.Show(e.Message); Environment.Exit(0); // 強制終了 } for (int i = 0; i < x_axis.Count; ++i) { int x = (int)Convert.ToDouble(x_axis[i]) + Constants.offset_x; // 座標データの型変換とオフセット加算 int y = -1 * ((int)Convert.ToDouble(y_axis[i])) + Constants.offset_y; int z = (int)Convert.ToDouble(z_axis[i]) + Constants.offset_z; int d = (int)Convert.ToDouble(dt[i]); switch (d) // ここでデータ本体と色の結びつけをしています。 { case 0: col = Color.Black; break; case 1: col = Color.Blue; break; case 2: col = Color.Red; break; case 3: col = Color.Purple; break; case 4: col = Color.Green; break; case 5: col = Color.Yellow; break; case 6: col = Color.Violet; break; case 7: col = Color.White; break; default: col = Color.Magenta; break; } img.SetPixel(x, y, col); // データがCSVに入っている順番で、プロットします。 } pictureBox1.Image = img; }
// MouseMoveイベントハンドラー private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { getMousePosition(); } private void getMousePosition() // このメソッドは、マウスの座標を取得し続けるエントリーで出てきたものそのまま。 { if (this.Focused) { System.Drawing.Point cp = pictureBox1.PointToClient(Cursor.Position); mouse_x = cp.X - Constants.offset_x; mouse_y = -1 * cp.Y + Constants.offset_y; label1.Text = "Focused"; this.Text = "x=" + mouse_x + ":y=" + mouse_y; } else { mouse_x = mouse_y = (int)0xffff; label1.Text = "Not Focused"; this.Text = "x=" + mouse_x + ":y=" + mouse_y; } } } }
ファイルForm1.Designer.cs
このリストは、Visual Studioが自動的に吐き出すもので、使用されるイベントや各コントロールのプロパティが記述されています。このファイルがないと、ビルドができません。内容は特に説明しませんが、PictureBox1の最後の行が、PictureBox1.MouseMoveイベント関連の記述です。
namespace CSVtoData { partial class Form1 { ////// 必要なデザイナー変数です。 /// private System.ComponentModel.IContainer components = null; ////// 使用中のリソースをすべてクリーンアップします。 /// /// マネージ リソースを破棄する場合は true を指定し、その他の場合は false を指定します。 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows フォーム デザイナーで生成されたコード ////// デザイナー サポートに必要なメソッドです。このメソッドの内容を /// コード エディターで変更しないでください。 /// private void InitializeComponent() { this.pictureBox1 = new System.Windows.Forms.PictureBox(); this.label1 = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // // pictureBox1 // this.pictureBox1.Location = new System.Drawing.Point(12, 9); this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.Size = new System.Drawing.Size(600, 703); this.pictureBox1.TabIndex = 0; this.pictureBox1.TabStop = false; //this.pictureBox1.MouseHover += new System.EventHandler(this.pictureBox1_MouseHover); this.pictureBox1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseMove); // // label1 // this.label1.AutoSize = true; this.label1.Font = new System.Drawing.Font ("MS UI Gothic", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128))); this.label1.Location = new System.Drawing.Point(271, 728); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(43, 15); this.label1.TabIndex = 1; this.label1.Text = "label1"; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(624, 761); this.Controls.Add(this.label1); this.Controls.Add(this.pictureBox1); this.Name = "Form1"; this.Text = "Form1"; ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.PictureBox pictureBox1; private System.Windows.Forms.Label label1; } }
上記プログラムを実行すると、このようなウインドウが出ます。見にくいですが、ウインドウの中に、小さな点が沢山描画されていて、それで四角形を形成しているのがわかります。
そして、ウインドウ左上に、マウスカーソルの座標が表示され続けます。
これで、目標仕様を実現するための要素技術として、
- ウインドウに点が打てる
- ファイルの読み出しが出来る
- ウインドウ上にあるマウスの座標が読み取れる
- ウインドウの座標系とデータの座標系の変換ができる
と、6項目中4項目が実現出来ています。
今回はここまでにします。