/// <summary> /// XYZ座標系へ変換したオブジェクトを返す /// <para>参考文献</para> /// <para>[1]理科年表,p.563,2003.</para> /// <para>[2]坂井,GPSのための実用プログラミング,東京電機大学出版局,pp.28-29,2007/7.</para> /// <para>[3]測位航法学会 訳,精説GPS 第二版,p.126,2010/4.</para> /// <para>[4]杉本・柴崎,GPSハンドブック,朝倉書店,p.23 and pp.415-416,2010/9.</para> /// <para>[5]西修二郎訳,物理測地学,シュプリンガー・ジャパン,pp.177-179,2006/8.</para> /// <para>[6]西修二郎訳,GPS理論と応用,シュプリンガー・ジャパン,pp.319-320,2009/3.</para> /// </summary> /// <param name="nextDatum">変換後の測地系(2012/6/5時点では基準測地系の変換には未対応)</param> /// <returns>変換したXYZ座標</returns> public Ecef ToXYZ(Datum nextDatum) { Ecef xyz = new Ecef(); double N; GlobalDatum _datum = new GlobalDatum(this.DatumKind); N = _datum.a / Math.Sqrt(1.0d - _datum.e2 * Math.Pow(Math.Sin(this.ToRadian().B), 2.0d)); xyz.x = (N + this.H) * Math.Cos(this.ToRadian().B) * Math.Cos(this.ToRadian().L); xyz.y = (N + this.H) * Math.Cos(this.ToRadian().B) * Math.Sin(this.ToRadian().L); xyz.z = (N * (1.0d - _datum.e2) + this.H) * Math.Sin(this.ToRadian().B); // 原点や軸の向きがずれた基準座標系ならば、変換が必要 return xyz; }
/// <summary> /// 楕円体に沿った、東西方向の単位長[m/deg]を返す /// <para>実際は高度の分だけ若干の誤差が発生するし、2地点の高度差は考慮しない。</para> /// <para>参考:理科年表,p.563,2003.</para> /// <para>ちなみに、http://yamadarake.web.fc2.com/trdi/2009/report000001.html を見ると使用した公式がヒュベニの公式と言うものであることが分かる</para> /// </summary> /// <returns>楕円体に沿った、東西方向の単位長[m/deg]</returns> public Length GetUnitLengthForEN() { Length ans = new Length(0, 0); Blh temp = this.ToRadian(); GlobalDatum _datum = new GlobalDatum(this.DatumKind); ans.E = Math.PI / 180.0 * _datum.a * Math.Cos(temp.B) / Math.Sqrt(1.0d - _datum.e2 * Math.Pow(Math.Sin(temp.B), 2.0d)); // 高度については無視 ans.N = Math.PI / 180.0 * _datum.a * (1.0d - _datum.e2) / Math.Pow(1.0d - _datum.e2 * Math.Pow(Math.Sin(temp.B), 2.0d), 1.5d); // 地表面に限れば誤差は無視可能。誤差は、地上では最大6408/6400程度 return ans; }
/// <summary> /// 距離演算その6 /// <para>Lambert-Andoyerの式を変形した小野の公式を用い引数で指定された座標までの距離[m]を返します。</para> /// <para>参考文献</para> /// <para>[1] 河合,測地線航海算法,富山高専 航海科学研究室,http://www.toyama-cmt.ac.jp/~mkawai/lecture/sailing/geodetic/geosail.html#note1 ,2012/6.</para> /// <para></para> /// </summary> /// <param name="pos">求めたい地点の座標</param> /// <returns>測地線長<para>未知の測地系の場合、非値を返します。</para></returns> public double GetDistance6(Blh pos) { Blh me = this.ToRadian(); Blh he = pos.ToRadian(); GlobalDatum _datum = new GlobalDatum(this.DatumKind); double myParametricLat = Math.Atan(_datum.b / _datum.a * Math.Tan(me.B)); double hisParametricLat = Math.Atan(_datum.b / _datum.a * Math.Tan(he.B)); double sphericalDistance = Math.Acos(Math.Sin(myParametricLat) * Math.Sin(hisParametricLat) + Math.Cos(myParametricLat) * Math.Cos(hisParametricLat) * Math.Cos(me.L - he.L)); double C = Math.Pow(Math.Sin(myParametricLat) + Math.Sin(hisParametricLat), 2.0); double D = Math.Pow(Math.Sin(myParametricLat) - Math.Sin(hisParametricLat), 2.0); double P = (_datum.a - _datum.b) * (sphericalDistance - Math.Sin(sphericalDistance)) / (4 * (1 + Math.Cos(sphericalDistance))); double Q = (_datum.a - _datum.b) * (sphericalDistance + Math.Sin(sphericalDistance)) / (4 * (1 - Math.Cos(sphericalDistance))); double geodeticDistance = double.NaN; geodeticDistance = _datum.a * sphericalDistance - C * P - D * Q; return geodeticDistance; }
/// <summary> /// 距離演算その5 /// <para>Lambert-Andoyerの式を用いて引数で指定された座標までの距離[m]を返します。</para> /// <para>10 m程度の距離で0.2 mm程度の誤差を生じます。数百km以上の長距離になると誤差は少なめです。</para> /// <para>距離演算その6と比較して、10 m程のごく短距離において、0.01 pm(ピコメートル)の差が生じました。また、通常距離では差は生じませんでした。</para> /// <para>参考文献</para> /// <para>[1] 河合,測地線航海算法,富山高専 航海科学研究室,http://www.toyama-cmt.ac.jp/~mkawai/lecture/sailing/geodetic/geosail.html#note1 ,2012/6.</para> /// </summary> /// <param name="pos">求めたい地点の座標</param> /// <returns>測地線長<para>未知の測地系の場合、非値を返します。</para></returns> public double GetDistance5(Blh pos) { Blh me = this.ToRadian(); Blh he = pos.ToRadian(); GlobalDatum _datum = new GlobalDatum(this.DatumKind); double myParametricLat = Math.Atan(_datum.b / _datum.a * Math.Tan(me.B)); double hisParametricLat = Math.Atan(_datum.b / _datum.a * Math.Tan(he.B)); double sphericalDistance = Math.Acos(Math.Sin(myParametricLat) * Math.Sin(hisParametricLat) + Math.Cos(myParametricLat) * Math.Cos(hisParametricLat) * Math.Cos(me.L - he.L)); double C = Math.Pow(Math.Sin(myParametricLat) + Math.Sin(hisParametricLat), 2.0); double D = Math.Pow(Math.Sin(myParametricLat) - Math.Sin(hisParametricLat), 2.0); double correction = _datum.f / 8.0 * ( (Math.Sin(sphericalDistance) - sphericalDistance) * C / Math.Pow(Math.Cos(sphericalDistance / 2.0), 2.0) - (Math.Sin(sphericalDistance) + sphericalDistance) * D / Math.Pow(Math.Sin(sphericalDistance / 2.0), 2.0) ); double geodeticDistance = double.NaN; geodeticDistance = _datum.a * (sphericalDistance + correction); return geodeticDistance; }
/// <summary> /// 距離演算その2 /// <para> /// <para>引数で指定された座標までの距離[m]を返します。</para> /// 参考:http://homepage3.nifty.com/kubota01/distance.htm /// 距離が50kmを超えるようなら、こちらのメソッドの使用を推奨します。 /// ただし、数百km以上であればその5またはその6の使用を推奨します。 /// <para>なお、高度は無視して楕円体面上での距離を求めています。</para> /// </para> /// </summary> /// <param name="pos">求めたい地点の座標</param> /// <returns>距離</returns> public double GetDistance2(Blh pos) { Ecef me = this.ToXYZ(); Ecef he = pos.ToXYZ(); GlobalDatum _datum = new GlobalDatum(this.DatumKind); double d_straight = Math.Sqrt(Math.Pow(me.x - he.x, 2.0) + Math.Pow(me.y - he.y, 2.0) + Math.Pow(me.z - he.z, 2.0)); // XYZ直交座標系を用いた直線距離(線は地中に潜る) double Nme = _datum.a / Math.Sqrt(1.0d - _datum.e2 * Math.Sin(this.ToRadian().B)); double Nse = _datum.a / Math.Sqrt(1.0d - _datum.e2 * Math.Sin(pos.ToRadian().B)); double N = (Nse + Nme) / 2.0d; // 平均の半径のようなもの double angle = Math.Asin(d_straight / 2.0d / N); // 半射程角(絵を描けば分かる) return 2.0d * angle * N; }
/******************** メソッド ****************************/ /// <summary> /// XYZからBlh座標系(緯度・経度・楕円体高[m])へ変換する /// </summary> /// <param name="datum">変換後の測地系</param> /// <returns>Blhに変換した結果</returns> public Blh ToBLH(Datum datum = Datum.WGS84) { Blh ans = new Blh(); double a, b, e, n, h, p, t, sint, cost; GlobalDatum _datum = new GlobalDatum(datum); ans.H = -_datum.a; if (this.x == 0 && this.y == 0 && this.z == 0) return ans; a = _datum.a; // 長半径 b = _datum.b; // 短半径 e = Math.Sqrt(_datum.e2); // 離心率 // 座標変換のためのパラメータ h = Math.Pow(a, 2) - Math.Pow(b, 2); p = Math.Sqrt(Math.Pow(this.x, 2) + Math.Pow(this.y, 2)); t = Math.Atan2(this.z * a, p * b); sint = Math.Sin(t); cost = Math.Cos(t); ans.B = Math.Atan2(this.z + h / b * Math.Pow(sint, 3), p - h / a * Math.Pow(cost, 3)); // 緯度[rad]を計算する ans.L = Math.Atan2(this.y, this.x); // 経度[rad]を求める n = a / Math.Sqrt(1 - _datum.e2 * Math.Pow(Math.Sin(ans.B), 2)); // 卯酉線曲率半径 ans.H = (p / Math.Cos(ans.B)) - n; // 楕円体高[m] return ans; }