/// <summary> /// Converts ‘this’ (geocentric) cartesian (x/y/z) point to (ellipsoidal geodetic) latitude/longitude coordinates on specified datum. /// Uses Bowring’s (1985) formulation for μm precision in concise form. /// </summary> /// <param name="toDatum">Datum to use when converting point.</param> public LatLonEllipsoidal ToLatLonE(Datum toDatum) { var Major = toDatum.Ellipsoid.Major; var Minor = toDatum.Ellipsoid.Minor; var Flattening = toDatum.Ellipsoid.Flattening; var E2 = 2 * Flattening - Flattening * Flattening; // 1st eccentricity squared ≡ (a²-b²)/a² var ε2 = E2 / (1 - E2); // 2nd eccentricity squared ≡ (a²-b²)/b² var P = Math.Sqrt(X * X + Y * Y); // distance from minor axis var R = Math.Sqrt(P * P + Z * Z); // polar radius // parametric latitude (Bowring eqn 17, replacing tanβ = z·a / p·b) var Tanβ = (Minor * Z) / (Major * P) * (1 + ε2 * Minor / R); var Sinβ = Tanβ / Math.Sqrt(1 + Tanβ * Tanβ); var Cosβ = Sinβ / Tanβ; // geodetic latitude (Bowring eqn 18: tanφ = z+ε²bsin³β / p−e²cos³β) var φ = Math.Atan2(Z + ε2 * Minor * Sinβ * Sinβ * Sinβ, P - E2 * Major * Cosβ * Cosβ * Cosβ); // longitude var λ = Math.Atan2(Y, X); // height above ellipsoid (Bowring eqn 7) [not currently used] var Sinφ = Math.Sin(φ); var Cosφ = Math.Cos(φ); var V = Major / Math.Sqrt(1 - E2 * Sinφ * Sinφ); // length of the normal terminated by the minor axis var H = P * Cosφ + Z * Sinφ - (Major * Major / V); var point = new LatLonEllipsoidal(φ.ToDegrees(), λ.ToDegrees(), toDatum); return(point); }
/// <summary> /// /Converts ‘this’ lat/lon coordinate to new coordinate system. /// </summary> /// <param name="toDatum">Datum this coordinate is to be converted to.</param> /// <example> /// var pWGS84 = new LatLon(51.4778, -0.0016, Datum.WGS84); /// var pOSGB = pWGS84.convertDatum(Datum.OSGB36); // 51.4773°N, 000.0000°E /// </example> public LatLonEllipsoidal ConvertDatum(Datum toDatum) { LatLonEllipsoidal OldLatLon = this; double[] Transform = toDatum.Transform; bool UsingWgs84 = false; if (OldLatLon.Datum == Datum.WGS84) { // converting from WGS 84 Transform = toDatum.Transform; UsingWgs84 = true; } if (toDatum == Datum.WGS84) { // converting to WGS 84; use inverse transform (don't overwrite original!) UsingWgs84 = true; Transform = new double[7]; for (var p = 0; p < 7; p++) { Transform[p] = -OldLatLon.Datum.Transform[p]; } } if (!UsingWgs84) { // neither this.datum nor toDatum are WGS84: convert this to WGS84 first OldLatLon = ConvertDatum(Datum.WGS84); } var OldCartesian = OldLatLon.ToCartesian(); // convert polar to cartesian... var NewCartesian = OldCartesian.ApplyTransform(Transform); // ...apply transform... var NewLatLon = NewCartesian.ToLatLonE(toDatum); // ...and convert cartesian to polar return(NewLatLon); }
/// <summary>Converts Ordnance Survey grid reference easting/northing coordinate to latitude/longitude (SW corner of grid square). /// /// Note formulation implemented here due to Thomas, Redfearn, etc is as published by OS, but is /// inferior to Krüger as used by e.g. Karney 2011.</summary> ///<param name="gridRef">Grid ref E/N to be converted to lat/long (SW corner of grid square).</param> ///<param name="datum">Datum to convert grid reference into.</param> ///<example>var gridref = new OsGridRef(651409.903, 313177.270); /// var pWgs84 = OsGridRef.osGridToLatLon(gridref); // 52°39′28.723″N, 001°42′57.787″E /// to obtain (historical) OSGB36 latitude/longitude point: /// var pOsgb = OsGridRef.osGridToLatLon(gridref, LatLon.datum.OSGB36); // 52°39′27.253″N, 001°43′04.518″E ///</example> ///<remarks>Currently not accurate enough, owing to a lack of precision in C#'s number handling. Will be ported to use arbitrary-precision maths.</remarks> public LatLonEllipsoidal ToLatLon(Datum datum) { var E = Easting; var N = Northing; var a = 6377563.396; var b = 6356256.909; // Airy 1830 major & minor semi-axes var F0 = 0.9996012717; // NatGrid scale factor on central meridian var φ0 = (49.0).ToRadians(); var λ0 = (-2.0).ToRadians(); // NatGrid true origin is 49°N,2°W var N0 = -100000; var E0 = 400000; // northing & easting of true origin, metres var e2 = 1 - b * b / (a * a); // eccentricity squared var n = (a - b) / (a + b); var n2 = n * n; var n3 = n * n * n; // n, n², n³ var φ = φ0; var M = 0.0; do { φ = (N - N0 - M) / (a * F0) + φ; var Ma = (1 + n + 1.25 * n2 + 1.25 * n3) * (φ - φ0); var Mb = (3 * n + 3 * n * n + 2.625 * n3) * Math.Sin(φ - φ0) * Math.Cos(φ + φ0); var Mc = (1.875 * n2 + 1.875 * n3) * Math.Sin(2 * (φ - φ0)) * Math.Cos(2 * (φ + φ0)); var Md = (35.0 / 24.0) * n3 * Math.Sin(3 * (φ - φ0)) * Math.Cos(3 * (φ + φ0)); M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc } while (N - N0 - M >= 0.00001); // ie until < 0.01mm var cosφ = Math.Cos(φ); var sinφ = Math.Sin(φ); var ν = a * F0 / Math.Sqrt(1 - e2 * sinφ * sinφ); // nu = transverse radius of curvature var ρ = a * F0 * (1 - e2) / Math.Pow(1 - e2 * sinφ * sinφ, 1.5); // rho = meridional radius of curvature var η2 = ν / ρ - 1; // eta = ? var tanφ = Math.Tan(φ); var tan2φ = tanφ * tanφ; var tan4φ = tan2φ * tan2φ; var tan6φ = tan4φ * tan2φ; var secφ = 1 / cosφ; var ν3 = ν * ν * ν; var ν5 = ν3 * ν * ν; var ν7 = ν5 * ν * ν; var VII = tanφ / (2 * ρ * ν); var VIII = tanφ / (24 * ρ * ν3) * (5 + 3 * tan2φ + η2 - 9 * tan2φ * η2); var IX = tanφ / (720 * ρ * ν5) * (61 + 90 * tan2φ + 45 * tan4φ); var X = secφ / ν; var XI = secφ / (6 * ν3) * (ν / ρ + 2 * tan2φ); var XII = secφ / (120 * ν5) * (5 + 28 * tan2φ + 24 * tan4φ); var XIIA = secφ / (5040 * ν7) * (61 + 662 * tan2φ + 1320 * tan4φ + 720 * tan6φ); var dE = (E - E0); var dE2 = dE * dE; var dE3 = dE2 * dE; var dE4 = dE2 * dE2; var dE5 = dE3 * dE2; var dE6 = dE4 * dE2; var dE7 = dE5 * dE2; φ = φ - VII * dE2 + VIII * dE4 - IX * dE6; var λ = λ0 + X * dE - XI * dE3 + XII * dE5 - XIIA * dE7; var Point = new LatLonEllipsoidal(φ.ToDegrees(), λ.ToDegrees(), Datum.OSGB36); if (datum != Datum.OSGB36) { Point = Point.ConvertDatum(datum); } return(Point); }
public OsGridRef ToGridRef() { // if necessary convert to OSGB36 first if (Datum != Datum.OSGB36) { var Point = new LatLonEllipsoidal(Latitude, Longitude, Datum); Point = Point.ConvertDatum(Datum.OSGB36); Latitude = Point.Latitude; Longitude = Point.Longitude; Datum = Datum.OSGB36; } var φ = Latitude.ToRadians(); var λ = Longitude.ToRadians(); var A = 6377563.396; var B = 6356256.909; // Airy 1830 major & minor semi-axes var F0 = 0.9996012717; // NatGrid scale factor on central meridian var φ0 = (49.0).ToRadians(); var λ0 = (-2.0).ToRadians(); // NatGrid true origin is 49°N,2°W var N0 = -100000; var E0 = 400000; // northing & easting of true origin, metres var E2 = 1 - (B * B) / (A * A); // eccentricity squared var N = (A - B) / (A + B); var N2 = N * N; var N3 = N * N * N; // n, n², n³ var Cosφ = Math.Cos(φ); var Sinφ = Math.Sin(φ); var ν = A * F0 / Math.Sqrt(1 - E2 * Sinφ * Sinφ); // nu = transverse radius of curvature var ρ = A * F0 * (1 - E2) / Math.Pow(1 - E2 * Sinφ * Sinφ, 1.5); // rho = meridional radius of curvature var η2 = ν / ρ - 1; // eta = ? var Ma = (1 + N + 1.25 * N2 + 1.25 * N3) * (φ - φ0); var Mb = (3 * N + 3 * N * N + 2.625 * N3) * Math.Sin(φ - φ0) * Math.Cos(φ + φ0); var Mc = (1.875 * N2 + 1.875 * N3) * Math.Sin(2 * (φ - φ0)) * Math.Cos(2 * (φ + φ0)); var Md = 35.0 / 24.0 * N3 * Math.Sin(3 * (φ - φ0)) * Math.Cos(3 * (φ + φ0)); var M = B * F0 * (Ma - Mb + Mc - Md); // meridional arc var Cos3φ = Cosφ * Cosφ * Cosφ; var Cos5φ = Cos3φ * Cosφ * Cosφ; var Tan2φ = Math.Tan(φ) * Math.Tan(φ); var Tan4φ = Tan2φ * Tan2φ; var I = M + N0; var II = (ν / 2) * Sinφ * Cosφ; var III = (ν / 24) * Sinφ * Cos3φ * (5 - Tan2φ + 9 * η2); var IIIA = (ν / 720) * Sinφ * Cos5φ * (61 - 58 * Tan2φ + Tan4φ); var IV = ν * Cosφ; var V = (ν / 6) * Cos3φ * (ν / ρ - Tan2φ); var VI = (ν / 120) * Cos5φ * (5 - 18 * Tan2φ + Tan4φ + 14 * η2 - 58 * Tan2φ * η2); var Δλ = λ - λ0; var Δλ2 = Δλ * Δλ; var Δλ3 = Δλ2 * Δλ; var Δλ4 = Δλ3 * Δλ; var Δλ5 = Δλ4 * Δλ; var Δλ6 = Δλ5 * Δλ; var North = I + II * Δλ2 + III * Δλ4 + IIIA * Δλ6; var East = E0 + IV * Δλ + V * Δλ3 + VI * Δλ5; North = Math.Round(North, 3); //North.toFixed(3)); // round to mm precision East = Math.Round(East, 3); return(new OsGridRef(East, North)); // gets truncated to SW corner of 1m grid square }