/// <summary> /// 2つの座標をセットするコンストラクタ /// <para>指定した座標の大小関係からマップの座標を自動的に決定します。</para> /// </summary> /// <param name="x1">座標1</param> /// <param name="x2">座標2</param> public MapField(Pos x1, Pos x2) { this.param = new FieldParameter(); this.param.leftX = this.param.rightX = x1.x; // 座標をセットしておく this.param.upperY = this.param.lowerY = x1.y; SetPos(x2, ref this.param); // もう一点は判定ののちに追加する return; }
/// <summary> /// DEMファイルを管理するための構造体のコンストラクタ /// </summary> /// <param name="mapName">地図名<para>例:熊本</para></param> /// <param name="id">ID番号</param> /// <param name="size">マップサイズ</param> /// <param name="field">コーナーの座標</param> /// <param name="model">DEMモデル名</param> public Info(string mapName, int id, Pos size, RectangleField field, Model model = Model.Unknown) : this() { this.MapName = mapName; this.ID = id; this.Size = size; this.Field = field; this.MapModel = model; }
/* メソッド ************************************/ /// <summary> /// 引数で渡した座標との距離を返す /// </summary> /// <param name="origin">始点</param> /// <returns>距離</returns> public double GetDistance(Pos origin) { return Math.Sqrt((this.x - origin.x) * (this.x - origin.x) + (this.y - origin.y) * (this.y - origin.y)); }
/// <summary> /// 渡されたマップを用いて初期化する /// <para>呼び出すのは一回きりにしてください</para> /// </summary> /// <param name="info">マップの情報</param> private void Init(Info info) { if (this.dict.Count == 0) { this.mapSize = info.Field.Size; this.amountOfMeshInMap = info.Size; this.meshSize = new Blh(this.mapSize.B / (double)this.amountOfMeshInMap.y, this.mapSize.L / (double)this.amountOfMeshInMap.x); this.offset = this.GetMapOffset(info); } return; }
/// <summary> /// 指定されたマップアドレスから、そのアドレスのマップの領域情報を返す /// </summary> /// <param name="address">マップアドレス</param> /// <returns>領域</returns> private RectangleField GetFieldFromAddress(Pos address) { double lowerLeftLat = (double)address.y * this.mapSize.B + this.offset.B; double lowerLeftLon = (double)address.x * this.mapSize.L + this.offset.L; return new RectangleField(new Blh(lowerLeftLat, lowerLeftLon), new Blh(lowerLeftLat + this.mapSize.B, lowerLeftLon + this.mapSize.L)); }
/// <summary> /// 指定領域のDEMを可能ならばロードする /// </summary> /// <param name="field">指定領域</param> public void Load(RectangleField field) { //System.Diagnostics.Stopwatch sw2 = System.Diagnostics.Stopwatch.StartNew(); Pos upperRightPos = this.GetMapAddress(field.UpperRight); Pos lowerLeftPos = this.GetMapAddress(field.LowerLeft); /* for (int x = lowerLeftPos.x; x <= upperRightPos.x; x++) { for (int y = lowerLeftPos.y; y <= upperRightPos.y; y++) { Pos address = new Pos(x, y); if (this.dict.ContainsKey(address) == true) { if (this.dict[address].IsLoaded == false && this.dict[address].Error == false) { this.dict[address].Load(); // まだデータを読み込んでいなければ読み込みを実施します。 } } } }*/ // 並列処理で書くならこうかく。あとで速度のテストをする予定。 Parallel.For(lowerLeftPos.x, upperRightPos.x + 1, x => // 可能なら並列処理でデータを格納する { Parallel.For(lowerLeftPos.y, upperRightPos.y + 1, y => { Pos address = new Pos(x, y); if (this.dict.ContainsKey(address) == true) { if (this.dict[address].IsLoaded == false && this.dict[address].Error == false) { this.dict[address].Load(); // まだデータを読み込んでいなければ読み込みを実施します。 } } }); }); //sw2.Stop(); // ストップウォッチを止める //Console.WriteLine("マップの読み込みにかけた処理時間: " + sw2.Elapsed); // 結果を表示する return; }
/// <summary> /// 指定領域のカバー率を返す /// <para>指定された領域全てのDEMマップを保持している場合は1.0を返します。</para> /// </summary> /// <param name="field">指定領域</param> /// <returns>カバー率</returns> public double GetCoverRate(RectangleField field) { Pos upperRightPos = this.GetMapAddress(field.UpperRight); Pos lowerLeftPos = this.GetMapAddress(field.LowerLeft); double coverCount = 0; for (int x = lowerLeftPos.x; x <= upperRightPos.x; x++) { for (int y = lowerLeftPos.y; y <= upperRightPos.y; y++) { Pos address = new Pos(x, y); if (this.dict.ContainsKey(address) == true) coverCount += 1.0; } } return coverCount / (double)((upperRightPos.x - lowerLeftPos.x + 1) * (upperRightPos.y - lowerLeftPos.y + 1)); }
/// <summary> /// 指定領域でマップを作成します /// <para>指定領域を含むマップを結合することでマップを生成します。</para> /// <para>該当データが存在しない領域はfloat.NaNで埋めます。</para> /// </summary> /// <param name="field">指定領域</param> /// <returns>Mapオブジェクト</returns> public MapDem CreateMap(RectangleField field) { this.Load(field); //System.Diagnostics.Stopwatch sw2 = System.Diagnostics.Stopwatch.StartNew(); Pos upperRightPos = this.GetMapAddress(field.UpperRight); Pos lowerLeftPos = this.GetMapAddress(field.LowerLeft); Pos fieldMaxIndex = upperRightPos - lowerLeftPos; // マップ単位でカウントした領域サイズ - 1 Pos mapSize = new Pos((fieldMaxIndex.x + 1) * this.amountOfMeshInMap.x, (fieldMaxIndex.y + 1) * this.amountOfMeshInMap.y); // 生成するマップのサイズを決定する MapDem ansMap = new MapDem(new RectangleField(this.GetFieldFromAddress(lowerLeftPos).LowerLeft, this.GetFieldFromAddress(upperRightPos).UpperRight), mapSize); // マップを生成 ansMap.Initialize(float.NaN); // 非値で初期化することで値が存在しない領域をはっきりさせる //int count = 0; Parallel.For(lowerLeftPos.x, upperRightPos.x + 1, x => // 可能なら並列処理でデータを格納する //for (int x = lowerLeftPos.x; x < upperRightPos.x + 1; x++) { Parallel.For(lowerLeftPos.y, upperRightPos.y + 1, y => //for (int y = lowerLeftPos.y; y < upperRightPos.y + 1; y++) { Pos address = new Pos(x, y); if (this.dict.ContainsKey(address) == true) { if (this.dict[address].Error == false && this.dict[address].IsLoaded) { //count++; Pos relativeAddress = new Pos(x - lowerLeftPos.x, y - lowerLeftPos.y); /* for (int localX = 0; localX < this.amountOfMeshInMap.x; localX++) { for (int localY = 0; localY < this.amountOfMeshInMap.y; localY++) { int globalX = relativeAddress.x * this.amountOfMeshInMap.x + localX; int globalY = (fieldMaxIndex.y - relativeAddress.y) * this.amountOfMeshInMap.y + localY; ansMap[globalX, globalY] = this.dict[address].DemMap[localX, localY]; } } */ Parallel.For(0, this.amountOfMeshInMap.x, localX => { Parallel.For(0, this.amountOfMeshInMap.y, localY => { int globalX = relativeAddress.x * this.amountOfMeshInMap.x + localX; int globalY = (fieldMaxIndex.y - relativeAddress.y) * this.amountOfMeshInMap.y + localY; ansMap[globalX, globalY] = this.dict[address].map[localX, localY]; }); }); } } }); }); //sw2.Stop(); // ストップウォッチを止める //Console.WriteLine("マップの統合にかけた処理時間: " + sw2.Elapsed); // 結果を表示する //Console.WriteLine("マップの結合数: " + count.ToString()); return ansMap; }
/// <summary> /// マップのサイズ情報の支援を受けた上で、指定されたテキストよりDEM値を読み出して返す /// </summary> /// <param name="fileName">解析対象ファイルのパス</param> /// <param name="size">マップサイズ</param> /// <returns>読み出されたDEM値(float型の2次元配列)<para>読み出しに失敗するとnullを返します。</para></returns> public float[,] GetValues(string fileName, Pos size) { if (System.IO.File.Exists(fileName) == false) return null; /* string txt = ""; using (System.IO.StreamReader sr = new System.IO.StreamReader(fileName, System.Text.Encoding.GetEncoding("shift_jis"))) { txt = sr.ReadToEnd(); } MatchCollection mc = this.data.Matches(txt); // 文字コードが合わなければヒットしない // デバッグコード //foreach (Match m in mc) //{ // Console.WriteLine(m.Groups["kind"].Value + m.Groups["value"].Value); //} if (mc.Count != size.x * size.y) return null; float[,] height = new float[size.x, size.y]; // 配列サイズを調整 Parallel.For(0, size.x * size.y, i => //for(int i = 0; i < size.x * size.y; i++) { int k = i % size.x; int j = i / size.x; height[k, j] = float.Parse(mc[i].Groups["value"].Value); // データを取得&浮動小数点型の数値変換 if (height[k, j] == -9999.0) height[k, j] = float.NaN; }); */ // 上の処理方法と比べると、下の処理は20倍くらい早い。正規表現によるマッチングがかなりボトルネックとなっていた。。。 float[,] height = new float[size.x, size.y]; // 配列サイズを調整 int i = 0; using (System.IO.StreamReader sr = new System.IO.StreamReader(fileName, System.Text.Encoding.GetEncoding("shift_jis"))) { while (sr.EndOfStream == false) { string line = sr.ReadLine(); string[] field = line.Split(','); if (field.Length == 2) { float value; if (i >= size.x * size.y) return null; // 大きすぎる if (float.TryParse(field[1], out value)) // 浮動小数点型の数値変換 { int k = i % size.x; int j = i / size.x; if (value == -9999.0) value = float.NaN; height[k, j] = value; // データを設定 i++; } } } } if (i != size.x * size.y) return null; // サイズミスマッチ return height; }
// もう少しマッチの条件をスマートに書きたいんだけどなぁ。 /* メソッド *****************************************************/ /// <summary> /// 引数で渡されたテキストを走査してDEM情報クラスオブジェクトを返す /// <para>フォーマット不一致の場合はnew Info()を返します。</para> /// </summary> /// <param name="fileName">解析対象のファイルへのパス</param> /// <returns>正常に読み出せた場合はDEM情報クラスオブジェクトを返す</returns> public Info GetHeader(string fileName) { if (System.IO.File.Exists(fileName) == false) return new Info(); string txt = ""; using (System.IO.StreamReader sr = new System.IO.StreamReader(fileName, System.Text.Encoding.GetEncoding("shift_jis"))) { StringBuilder sb = new StringBuilder(2000); // 予め大きなメモリ容量を確保しておく for (int i = 0; i < 50; i++) // ヘッダー部分だけに限ると、処理時間が半分になる。正規表現のマッチングが時間がかかっているみたいだ。 sb.Append(sr.ReadLine()); txt = sb.ToString(); //txt = sr.ReadToEnd(); } string mapName = ""; // 地点名は2012/5/22時点では未対応 int ID = 0; // ID番号 Pos size = new Pos(); // マップサイズ(縦方向と横方向のメッシュ数) Blh upperRight = new Blh(), lowerLeft = new Blh(); Match m; m = this.meshID.Match(txt); if (m.Success) ID = int.Parse(m.Groups["ID"].Value); m = this.lowerCorner.Match(txt); if (m.Success) upperRight = new Blh(double.Parse(m.Groups["lat"].Value), double.Parse(m.Groups["lon"].Value)); m = this.upperCorner.Match(txt); if (m.Success) lowerLeft = new Blh(double.Parse(m.Groups["lat"].Value), double.Parse(m.Groups["lon"].Value)); m = this.size.Match(txt); if (m.Success) size = new Pos(int.Parse(m.Groups["width"].Value) + 1, int.Parse(m.Groups["hight"].Value) + 1); RectangleField corner = new RectangleField(upperRight, lowerLeft); return new Info(mapName, ID, size, corner, Model.JPGIS2x_GML); }
/// <summary> /// 1つの座標をセットするコンストラクタ /// <para>自動的にPos(0, 0)をもう一点として張ります。</para> /// <para>マップのサイズを表すのに便利かと思います。</para> /// </summary> public MapField(Pos pos) { this.param = new FieldParameter(); SetPos(pos, ref this.param); return; }
/// <summary> /// 座標をセットする /// <para>引数で渡された座標を検査して、領域を増やすことができれば拡張が実行されます。</para> /// </summary> /// <param name="pos">追加・検査する座標</param> /// <param name="param">領域のパラメータ(参照渡し)</param> private static void SetPos(Pos pos, ref FieldParameter param) { if (param.upperY > pos.y) param.upperY = pos.y; if (param.lowerY < pos.y) param.lowerY = pos.y; if (param.leftX > pos.x) param.leftX = pos.x; if (param.rightX < pos.y) param.rightX = pos.x; return; }
/// <summary> /// 領域を平行移動させる /// <para>破壊的メソッド(オブジェクト自身の値を変更します)</para> /// </summary> /// <param name="shiftAmount">シフト量<para>x成分は正値で右へ,y成分は正値で下方へ領域が平行移動します。</para></param> public void Translate(Pos shiftAmount) { this.param.leftX += shiftAmount.x; this.param.lowerY += shiftAmount.y; this.param.rightX += shiftAmount.x; this.param.upperY += shiftAmount.y; return; }
/// <summary> /// 座標をセットして領域を再設定・拡張する /// <para>領域の中の座標を設定しても無視します。</para> /// </summary> /// <param name="pos">指定座標</param> public void Set(Pos pos) { SetPos(pos, ref this.param); return; }
/// <summary> /// 任意の地点が領域内にあるか確認する /// </summary> /// <param name="pos">検査座標</param> /// <returns>true: 領域内にある。</returns> public Boolean CheckInclusion(Pos pos) { if (this.UpperY < pos.y && this.LowerY > pos.y && this.LeftX < pos.x && this.RightX > pos.x) return true; else return false; }
/// <summary> /// 引数で渡された領域と重ね合わせ、共通の領域を返す /// </summary> /// <param name="field">ターゲットの領域</param> /// <returns>共通領域</returns> public MapField And(MapField field) { List<int> xbuff = new List<int>(); List<int> ybuff = new List<int>(); if (this.UpperY <= field.UpperY && this.LowerY >= field.UpperY) ybuff.Add(field.UpperY); // 2つの領域が重なるかどうかをチェック if (this.UpperY <= field.LowerY && this.LowerY >= field.LowerY) ybuff.Add(field.LowerY); if (field.UpperY < this.UpperY && field.LowerY > this.UpperY) ybuff.Add(this.UpperY); if (field.UpperY < this.LowerY && field.LowerY > this.LowerY) ybuff.Add(this.LowerY); if (this.RightX >= field.RightX && this.LeftX <= field.RightX) xbuff.Add(field.RightX); if (this.RightX >= field.LeftX && this.LeftX <= field.LeftX) xbuff.Add(field.LeftX); if (field.RightX > this.RightX && field.LeftX < this.RightX) xbuff.Add(this.RightX); if (field.RightX > this.LeftX && field.LeftX < this.LeftX) xbuff.Add(this.LeftX); Pos p1, p2; // 抽出されたz,yの組を2つ作って領域を作る if (xbuff.Count >= 2 && ybuff.Count >= 2) { p1 = new Pos(xbuff[0], ybuff[0]); p2 = new Pos(xbuff[1], ybuff[1]); } else { p1 = new Pos(); p2 = new Pos(); } return new MapField(p1, p2); }