国土地理院の「測量計算サイト」に掲載されている計算式をC#でコーディングしただけですが…
他を探しても無かったので、自分用のバックアップを兼ねここにアップしておきます。
下のコードを拡張子「.cs」でコピペ&保存して、VisualStudioでプロジェクトに追加して、
Coordinate.cXY.LonLat2XY(dLon, dLat, 35.833333d, 141d, out dX, out dY)
みたいにすれば使うことが出来るかと思います。
測量分野1)平成十四年国土交通省告示第九号ではXが南北、Yが東西ですが、私自身が何度も間違えるので、Xを東西・Yを南北にしています。使うときはお気を付けください。
なお、UTM座標系ではXがEasting、YがNorthing2)”The y value, called the northing” Handbook for Transformation of
Datums… p.44になっています。(Twitterで@mapignさんに教えて頂きました、ありがとうございます)。
また、ミスなどがありましたら、教えてくれると喜びます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Coordinate
{
/// <summary>
/// 平面直角座標系と緯度経度を相互変換する
/// 国土地理院の「測量計算サイト」の式をC#でコーディングした
/// http://vldb.gsi.go.jp/sokuchi/surveycalc/main.html
/// </summary>
public static class cXY
{
const double daa = 6378137; //長半径
const double dF = 298.257222101d; //逆扁平率
const double dM0 = 0.9999; //平面直角座標系のY軸上における縮尺係数(UTM座標系の場合→0.9996)
/// <summary>
/// 緯度経度から任意原点の平面直角座標を求める
/// </summary>
/// <param name="Lon">経度[度]</param>
/// <param name="Lat">緯度[度]</param>
/// <param name="Lon0">原点:経度[度]</param>
/// <param name="Lat0">原点:緯度[度]</param>
/// <param name="X">X座標(東西方向、m)</param>
/// <param name="Y">Y座標(南北方向、m)</param>
public static void LonLat2XY(double Lon, double Lat, double Lon0, double Lat0, out double X, out double Y)
{
double dn = 1d / (2 * dF - 1);
//ラジアン単位に
Lon = Deg2Rad(Lon); Lat = Deg2Rad(Lat); Lon0 = Deg2Rad(Lon0); Lat0 = Deg2Rad(Lat0);
double dt = Math.Sinh(atanh(Math.Sin(Lat)) - (2 * Math.Sqrt(dn)) / (1 + dn) * atanh(2 * Math.Sqrt(dn) / (1 + dn) * Math.Sin(Lat)));
double dtb = Math.Sqrt(1 + Math.Pow(dt, 2));
double dLmc = Math.Cos(Lon - Lon0);
double dLms = Math.Sin(Lon - Lon0);
double dXi = Math.Atan(dt / dLmc);
double dEt = atanh(dLms / dtb);
//α1→0~α5→4
double[] dal = new double[6];
dal[0] = 0;
dal[1] = 1d / 2d * dn - 2d / 3d * Math.Pow(dn, 2) + 5d / 16d * Math.Pow(dn, 3) + 41d / 180d * Math.Pow(dn, 4) - 127d / 288d * Math.Pow(dn, 5);
dal[2] = 13d / 48d * Math.Pow(dn, 2) - 3d / 5d * Math.Pow(dn, 3) + 557d / 1440d * Math.Pow(dn, 4) + 281d / 630d * Math.Pow(dn, 5);
dal[3] = 61d / 240d * Math.Pow(dn, 3) - 103d / 140d * Math.Pow(dn, 4) + 15061d / 26880d * Math.Pow(dn, 5);
dal[4] = 49561d / 161280d * Math.Pow(dn, 4) - 179d / 168d * Math.Pow(dn, 5);
dal[5] = 34729d / 80640d * Math.Pow(dn, 5);
double dSg = 0; double dTu = 0;
for (int j = 1; j <= 5; j++)
{
dSg = dSg + 2 * j * dal[j] * Math.Cos(2 * j * dXi) * Math.Cosh(2 * j * dEt);
dTu = dTu + 2 * j * dal[j] * Math.Sin(2 * j * dXi) * Math.Sinh(2 * j * dEt);
}
dSg = 1 + dSg;
//A0-A5
double[] dA = new double[6];
dA[0] = 1 + Math.Pow(dn, 2) / 4 + Math.Pow(dn, 4) / 64;
dA[1] = -3d / 2d * (dn - Math.Pow(dn, 3) / 8 - Math.Pow(dn, 5) / 64);
dA[2] = 15d / 16d * (Math.Pow(dn, 2) - Math.Pow(dn, 4) / 4);
dA[3] = -35d / 48d * (Math.Pow(dn, 3) - 5d / 16d * Math.Pow(dn, 5));
dA[4] = 315d / 512d * Math.Pow(dn, 4);
dA[5] = -693d / 1280d * Math.Pow(dn, 5);
double dAb = dM0 * daa / (1 + dn) * dA[0];
double dSb = 0;
for (int j = 1; j <= 5; j++)
{
dSb = dSb + dA[j] * Math.Sin(2 * j * Lat0);
}
dSb = dM0 * daa / (1 + dn) * (dA[0] * Lat0 + dSb);
Y = 0; X = 0;
for (int j = 1; j <= 5; j++)
{
Y = Y + dal[j] * Math.Sin(2 * j * dXi) * Math.Cosh(2 * j * dEt);
X = X + dal[j] * Math.Cos(2 * j * dXi) * Math.Sinh(2 * j * dEt);
}
Y = dAb * (dXi + Y) - dSb;
X = dAb * (dEt + X);
}
/// <summary>
/// 平面直角座標から緯度・経度を求める
/// </summary>
/// <param name="X">X座標(東西方向、m)</param>
/// <param name="Y">Y座標(南北方向、m)</param>
/// <param name="Lon0">原点:経度[度]</param>
/// <param name="Lat0">原点:緯度[度]</param>
/// <param name="Lon">経度[度]</param>
/// <param name="Lat">緯度[度]</param>
public static void XY2LonLat(double X, double Y, double Lon0, double Lat0, out double Lon, out double Lat)
{
double dn = 1d / (2 * dF - 1);
//ラジアン単位に
Lon0 = Deg2Rad(Lon0);
Lat0 = Deg2Rad(Lat0);
//Sφ0、A
double[] dA = new double[6];
dA[0] = 1 + Math.Pow(dn, 2) / 4 + Math.Pow(dn, 4) / 64;
dA[1] = -3d / 2d * (dn - Math.Pow(dn, 3) / 8 - Math.Pow(dn, 5) / 64);
dA[2] = 15d / 16d * (Math.Pow(dn, 2) - Math.Pow(dn, 4) / 4);
dA[3] = -35d / 48d * (Math.Pow(dn, 3) - 5d / 16d * Math.Pow(dn, 5));
dA[4] = 315d / 512d * Math.Pow(dn, 4);
dA[5] = -693d / 1280d * Math.Pow(dn, 5);
double dAb = dM0 * daa / (1 + dn) * dA[0];
double dSb = 0;
for (int j = 1; j <= 5; j++)
{
dSb = dSb + dA[j] * Math.Sin(2 * j * Lat0);
}
dSb = dM0 * daa / (1 + dn) * (dA[0] * Lat0 + dSb);
//ξ・η
double dXi = (Y + dSb) / dAb;
double dEt = X / dAb;
//β
double[] dBt = new double[6];
dBt[1] = 1d / 2d * dn - 2d / 3d * Math.Pow(dn, 2) + 37d / 96d * Math.Pow(dn, 3) - 1d / 360d * Math.Pow(dn, 4) - 81d / 512d * Math.Pow(dn, 5);
dBt[2] = 1d / 48d * Math.Pow(dn, 2) + 1d / 15d * Math.Pow(dn, 3) - 437d / 1440d * Math.Pow(dn, 4) + 46d / 105d * Math.Pow(dn, 5);
dBt[3] = 17d / 480d * Math.Pow(dn, 3) - 37d / 840d * Math.Pow(dn, 4) - 209d / 4480d * Math.Pow(dn, 5);
dBt[4] = 4397d / 161280d * Math.Pow(dn, 4) - 11d / 504d * Math.Pow(dn, 5);
dBt[5] = 4583d / 161280d * Math.Pow(dn, 5);
//ξ’・η'・σ'・τ'・χ
double dXi2 = 0;
double dEt2 = 0;
double dSg2 = 0;
double dTu2 = 0;
for (int j = 1; j <= 5; j++)
{
dXi2 = dXi2 + dBt[j] * Math.Sin(2 * j * dXi) * Math.Cosh(2 * j * dEt);
dEt2 = dEt2 + dBt[j] * Math.Cos(2 * j * dXi) * Math.Sinh(2 * j * dEt);
dSg2 = dSg2 + dBt[j] * Math.Cos(2 * j * dXi) * Math.Cosh(2 * j * dEt);
dTu2 = dTu2 + dBt[j] * Math.Sin(2 * j * dXi) * Math.Sinh(2 * j * dEt);
}
dXi2 = dXi - dXi2;
dEt2 = dEt - dEt2;
dSg2 = 1 - dSg2;
double dCi = Math.Asin(Math.Sin(dXi2) / Math.Cosh(dEt2));
//δ
double[] dDt = new double[7];
dDt[1] = 2 * dn - 2d / 3d * Math.Pow(dn, 2) - 2 * Math.Pow(dn, 3) + 116d / 45d * Math.Pow(dn, 4) + 26d / 45d * Math.Pow(dn, 5) - 2854d / 675d * Math.Pow(dn, 6);
dDt[2] = 7d / 3d * Math.Pow(dn, 2) - 8d / 5d * Math.Pow(dn, 3) - 227d / 45d * Math.Pow(dn, 4) + 2704d / 315d * Math.Pow(dn, 5) + 2323d / 945d * Math.Pow(dn, 6);
dDt[3] = 56d / 15d * Math.Pow(dn, 3) - 136d / 35d * Math.Pow(dn, 4) - 1262d / 105d * Math.Pow(dn, 5) + 73814d / 2835d * Math.Pow(dn, 6);
dDt[4] = 4279d / 630d * Math.Pow(dn, 4) - 332d / 35d * Math.Pow(dn, 5) - 399572d / 14175d * Math.Pow(dn, 6);
dDt[5] = 4174d / 315d * Math.Pow(dn, 5) - 144838d / 6237d * Math.Pow(dn, 6);
dDt[6] = 601676d / 22275d * Math.Pow(dn, 6);
//ラジアン単位の緯度経度
Lon = Lon0 + Math.Atan(Math.Sinh(dEt2) / Math.Cos(dXi2));
Lat = dCi;
for (int j = 1; j <= 6; j++)
{
Lat = Lat + dDt[j] * Math.Sin(2 * j * dCi);
}
//度単位に
Lon = 180 * Lon / Math.PI;
Lat = 180 * Lat / Math.PI;
}
//双曲線正接関数の逆関数
private static double atanh(double x)
{
return (1d / 2d * Math.Log((1 + x) / (1 - x), Math.E));
}
private static double Deg2Rad(double Deg)
{
return (Math.PI * Deg / 180d);
}
}
}
脚注
| ↑1 | 平成十四年国土交通省告示第九号 |
|---|---|
| ↑2 | ”The y value, called the northing” Handbook for Transformation of Datums… p.44 |