/// <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> /// NMEA読み出し機能およびRectangleFieldクラスのテスト /// <para>ダイアログを利用してNMEAファイルを開きます。</para> /// <para>NMEA読み取り後に、測位結果を配列化して測位結果全体が収まるRectangleFieldオブジェクトを生成します。</para> /// <para>未測位データは除いています。</para> /// </summary> /// <returns>測位データ全体を覆う長方形領域情報</returns> public static RectangleField ReadNmeaToRectangleFeild() { NmeaReader reader = new NmeaReader(); reader.OpenWithDialog(); // NMEAファイル読み出し Blh[] pos = reader.GetPositions(); // 測位情報のうちから位置座標のみを取得 RectangleField field = new RectangleField(); foreach (Blh x in pos) // 最外領域を求める { if (x.B != 0.0 && x.L != 0.0) field.Set(x); // 未測位データは除く } return field; }
/// <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; }
/* [2012/5/12] 更新後は動作未確認 */ /// <summary> /// ログを全走査し、2つ以上のログが存在した場合に、ログを覆う長方形領域を返す /// </summary> /// <returns> /// 領域情報またはnull /// <para>領域を構成できない場合はnullを返します。</para> /// </returns> public RectangleField GetRectangleField() { RectangleField field = new RectangleField(); if (this.data.Count >= 2) // 領域は2点以上なければ指定できない { foreach (PositioningInfo pos in this.data) { field.Set(pos.Position); } return field; } else { return null; } }
// もう少しマッチの条件をスマートに書きたいんだけどなぁ。 /* メソッド *****************************************************/ /// <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> /// 領域の比較を行い、本オブジェクトが引数で渡したオブジェクトを含むかどうかチェックします /// <para>比較は、一部の領域ではなく完全に含むかどうかを判定します。</para> /// </summary> /// <param name="comparativeField">比較対象のオブジェクト</param> /// <returns>true: 含む</returns> /// <exception cref="ArgumentException">引数のインスタンスが確保されていない場合にスロー</exception> public Boolean IsContain(RectangleField comparativeField) { if (comparativeField != null) { double hisLeftLon = comparativeField.LowerLeft.ToDegree().L + 180.0; // 経度の定義域は-180 - +180であるため、比較が容易になるように180度オフセットを取る double hisRightLon = comparativeField.LowerRight.ToDegree().L + 180.0; if (hisRightLon < hisLeftLon) hisRightLon += 360.0; // 経度180度をまたがっている場合の対策 double hisUpperLat = comparativeField.UpperLeft.ToDegree().B; double hisLowerLat = comparativeField.LowerLeft.ToDegree().B; double myLeftLon = this.LowerLeft.ToDegree().L + 180.0; double myRightLon = this.LowerRight.ToDegree().L + 180.0; if (myRightLon < myLeftLon) // 経度180度をまたがっていた場合の対策 { if (hisLeftLon < myRightLon) // 見切れている範囲内に比較対象の領域がかかっていた場合 { hisRightLon += 360.0; // 1サイクル分進める hisLeftLon += 360.0; } myRightLon += 360.0; // こちらも1サイクル進める } double myUpperLat = this.UpperLeft.ToDegree().B; double myLowerLat = this.LowerLeft.ToDegree().B; if (hisLeftLon >= myLeftLon && hisRightLon <= myRightLon && // 自身の中に、比較対象の領域が完全に含まれているか確認する hisUpperLat <= myUpperLat && hisLowerLat >= myLowerLat) return true; else return false; } else throw new ArgumentException("RectangleFieldクラスのIsContain()にてエラーがスローされました。引数のインスタンスは確保してください。"); }
/// <summary> /// 領域を指定マージン[km]にて拡張した領域を返します /// <para>非破壊的メソッド</para> /// <para>経度方向は高緯度に合わせ、緯度方向は低緯度に合わせて拡張します。</para> /// </summary> /// <param name="marginKm">マージン[km]<para>負値の場合は処理を行いません。</para></param> /// <returns>拡張済みのオブジェクト<para>引数のmarginが負値の場合はnullを返します。</para></returns> public RectangleField Extend(double marginKm) { RectangleField ans = null; if (marginKm > 0) { ans = new RectangleField(this); Blh highLat, lowLat; if (Math.Abs(this.LowerLeft.B) > Math.Abs(this.UpperLeft.B)) { highLat = this.LowerLeft; lowLat = this.UpperLeft; } else { highLat = this.UpperLeft; lowLat = this.LowerLeft; } double n = marginKm * 1000.0 / lowLat.GetUnitLengthForEN().N; // 緯度が低い方が緯度方向の単位距離が短い double e = marginKm * 1000.0 / highLat.GetUnitLengthForEN().E; // 緯度が高い方が経度方向の単位距離は短い Blh pad = new Blh(n, e); ans.Set(this.UpperRight + pad); ans.Set(this.LowerLeft - pad); } return ans; }
/// <summary> /// コピーコンストラクタ /// </summary> /// <param name="field">コピー元のインスタンス</param> public RectangleField(RectangleField field) { this.param = field.param; }
/// <summary> /// 距離演算その1 /// <para>ヒュベニの公式を利用して、引数で指定された座標までの距離[m]を返します。</para> /// <para>GetDistance2()と比較して、40 km差で0.01 m以下の差が生じます。</para> /// <para>最短距離を求めているわけではないことに注意してください。</para> /// <para>なお、高度は無視して楕円体面上での距離を求めています。</para> /// </summary> /// <param name="pos">求めたい地点の座標</param> /// <returns>南北・東西方向の距離[m]を構造体で返す</returns> public Length GetDistance(Blh pos) { Length ans; Blh me = this.ToDegree(); // 自身のコピー Blh he = pos.ToDegree(); // 引数のコピー。単位はdegに統一する Field.RectangleField field = new Field.RectangleField(me, he); Blh center = field.Center; ans = new Length(field.DifferInLon * center.GetUnitLengthForEN().E, field.DifferInLat * center.GetUnitLengthForEN().N); // 緯度・経度差から距離を求める。 return ans; }
/// <summary> /// 指定領域をカバーするマップを生成する /// <para>取得可能なマップ規格のうち、最も細かく且つ指定領域でのカバー率がthresholdOfCoverRateを超える系統を使って一つのマップを生成します。</para> /// </summary> /// <param name="field">領域</param> /// <param name="thresholdOfCoverRate">許容カバー率<para>この閾値を下回るカバー率であれば、マップの生成を行わずにnullを返します。</para></param> /// <returns>生成したマップ</returns> public MapDem CreateMap(RectangleField field, double thresholdOfCoverRate = 0.3) { this.manager.Sort(); // リストを小さい順に並び替える foreach (Manager man in this.manager) { double coverRate = man.GetCoverRate(field); if (coverRate >= thresholdOfCoverRate) { return man.CreateMap(field); } } return null; }