Convert Easting/Northing to Lat Lng - c#

I've been struggling to convert northing/eastings to lat/long without using a nuget package. I'm sure my UTM zone is 30U (Great Britain).
How would one go about doing this in C#?
When manually done using this site. It provides the correct values and locations.
Easting: 426342 Northing: 505339
Lat/Long should be: 54.44277977022131, -1.5953328509040021
I had previously found some code on stackoverflow but it's giving me the wrong values.
utmZone = "30U"
public static LatLng ToLatLon(double utmX, double utmY, string utmZone)
{
bool isNorthHemisphere = utmZone.Last() >= 'N';
var diflat = -0.00066286966871111111111111111111111111;
var diflon = -0.0003868060578;
var zone = int.Parse(utmZone.Remove(utmZone.Length - 1));
var c_sa = 6378137.000000;
var c_sb = 6356752.314245;
var e2 = Math.Pow((Math.Pow(c_sa, 2) - Math.Pow(c_sb, 2)), 0.5) / c_sb;
var e2cuadrada = Math.Pow(e2, 2);
var c = Math.Pow(c_sa, 2) / c_sb;
var x = utmX - 500000;
var y = isNorthHemisphere ? utmY : utmY - 10000000;
var s = ((zone * 6.0) - 183.0);
var lat = y / (c_sa * 0.9996);
var v = (c / Math.Pow(1 + (e2cuadrada * Math.Pow(Math.Cos(lat), 2)), 0.5)) * 0.9996;
var a = x / v;
var a1 = Math.Sin(2 * lat);
var a2 = a1 * Math.Pow((Math.Cos(lat)), 2);
var j2 = lat + (a1 / 2.0);
var j4 = ((3 * j2) + a2) / 4.0;
var j6 = ((5 * j4) + Math.Pow(a2 * (Math.Cos(lat)), 2)) / 3.0;
var alfa = (3.0 / 4.0) * e2cuadrada;
var beta = (5.0 / 3.0) * Math.Pow(alfa, 2);
var gama = (35.0 / 27.0) * Math.Pow(alfa, 3);
var bm = 0.9996 * c * (lat - alfa * j2 + beta * j4 - gama * j6);
var b = (y - bm) / v;
var epsi = ((e2cuadrada * Math.Pow(a, 2)) / 2.0) * Math.Pow((Math.Cos(lat)), 2);
var eps = a * (1 - (epsi / 3.0));
var nab = (b * (1 - epsi)) + lat;
var senoheps = (Math.Exp(eps) - Math.Exp(-eps)) / 2.0;
var delt = Math.Atan(senoheps / (Math.Cos(nab)));
var tao = Math.Atan(Math.Cos(delt) * Math.Tan(nab));
double longitude = ((delt * (180.0 / Math.PI)) + s) + diflon;
double latitude = ((lat + (1 + e2cuadrada * Math.Pow(Math.Cos(lat), 2) - (3.0 / 2.0) * e2cuadrada * Math.Sin(lat) * Math.Cos(lat) * (tao - lat)) * (tao - lat)) * (180.0 / Math.PI)) + diflat;
return new LatLng
{
Latitude = latitude,
Longitude = longitude
};
}

Refer the below code and run LatLonConversions.ConvertOSToLatLon(426342, 505339)
public class LatLonConversions
{
const double a = 6377563.396;
const double b = 6356256.91;
const double e2 = (a - b) / a;
const double n0 = -100000;
const double e0 = 400000;
const double f0 = 0.999601272;
const double phi0 = 0.855211333;
const double lambda0 = -0.034906585;
const double n = (a - b) / (a + b);
static double lat, lng;
private LatLonConversions() { }
private static double Deg2Rad(double x)
{
return x * (Math.PI / 180);
}
private static double Rad2Deg(double x)
{
return x * (180 / Math.PI);
}
private static double SinSquared(double x)
{
return Math.Sin(x) * Math.Sin(x);
}
private static double TanSquared(double x)
{
return Math.Tan(x) * Math.Tan(x);
}
private static double Sec(double x)
{
return 1.0 / Math.Cos(x);
}
private static void OSGB36ToWGS84()
{
var airy1830 = new RefEll(6377563.396, 6356256.909);
var a = airy1830.maj;
var b = airy1830.min;
var eSquared = airy1830.ecc;
var phi = Deg2Rad(lat);
var lambda = Deg2Rad(lng);
var v = a / (Math.Sqrt(1 - eSquared * SinSquared(phi)));
var H = 0; // height
var x = (v + H) * Math.Cos(phi) * Math.Cos(lambda);
var y = (v + H) * Math.Cos(phi) * Math.Sin(lambda);
var z = ((1 - eSquared) * v + H) * Math.Sin(phi);
var tx = 446.448;
var ty = -124.157;
var tz = 542.060;
var s = -0.0000204894;
var rx = Deg2Rad(0.00004172222);
var ry = Deg2Rad(0.00006861111);
var rz = Deg2Rad(0.00023391666);
var xB = tx + (x * (1 + s)) + (-rx * y) + (ry * z);
var yB = ty + (rz * x) + (y * (1 + s)) + (-rx * z);
var zB = tz + (-ry * x) + (rx * y) + (z * (1 + s));
var wgs84 = new RefEll(6378137.000, 6356752.3141);
a = wgs84.maj;
b = wgs84.min;
eSquared = wgs84.ecc;
var lambdaB = Rad2Deg(Math.Atan(yB / xB));
var p = Math.Sqrt((xB * xB) + (yB * yB));
var phiN = Math.Atan(zB / (p * (1 - eSquared)));
for (var i = 1; i < 10; i++)
{
v = a / (Math.Sqrt(1 - eSquared * SinSquared(phiN)));
double phiN1 = Math.Atan((zB + (eSquared * v * Math.Sin(phiN))) / p);
phiN = phiN1;
}
var phiB = Rad2Deg(phiN);
lat = phiB;
lng = lambdaB;
}
public static LatLon ConvertOSToLatLon(double easting, double northing)
{
RefEll airy1830 = new RefEll(6377563.396, 6356256.909);
double OSGB_F0 = 0.9996012717;
double N0 = -100000.0;
double E0 = 400000.0;
double phi0 = Deg2Rad(49.0);
double lambda0 = Deg2Rad(-2.0);
double a = airy1830.maj;
double b = airy1830.min;
double eSquared = airy1830.ecc;
double phi = 0.0;
double lambda = 0.0;
double E = easting;
double N = northing;
double n = (a - b) / (a + b);
double M = 0.0;
double phiPrime = ((N - N0) / (a * OSGB_F0)) + phi0;
do
{
M =
(b * OSGB_F0)
* (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n))
* (phiPrime - phi0))
- (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))
* Math.Sin(phiPrime - phi0)
* Math.Cos(phiPrime + phi0))
+ ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))
* Math.Sin(2.0 * (phiPrime - phi0))
* Math.Cos(2.0 * (phiPrime + phi0)))
- (((35.0 / 24.0) * n * n * n)
* Math.Sin(3.0 * (phiPrime - phi0))
* Math.Cos(3.0 * (phiPrime + phi0))));
phiPrime += (N - N0 - M) / (a * OSGB_F0);
} while ((N - N0 - M) >= 0.001);
var v = a * OSGB_F0 * Math.Pow(1.0 - eSquared * SinSquared(phiPrime), -0.5);
var rho =
a
* OSGB_F0
* (1.0 - eSquared)
* Math.Pow(1.0 - eSquared * SinSquared(phiPrime), -1.5);
var etaSquared = (v / rho) - 1.0;
var VII = Math.Tan(phiPrime) / (2 * rho * v);
var VIII =
(Math.Tan(phiPrime) / (24.0 * rho * Math.Pow(v, 3.0)))
* (5.0
+ (3.0 * TanSquared(phiPrime))
+ etaSquared
- (9.0 * TanSquared(phiPrime) * etaSquared));
var IX =
(Math.Tan(phiPrime) / (720.0 * rho * Math.Pow(v, 5.0)))
* (61.0
+ (90.0 * TanSquared(phiPrime))
+ (45.0 * TanSquared(phiPrime) * TanSquared(phiPrime)));
var X = Sec(phiPrime) / v;
var XI =
(Sec(phiPrime) / (6.0 * v * v * v))
* ((v / rho) + (2 * TanSquared(phiPrime)));
var XII =
(Sec(phiPrime) / (120.0 * Math.Pow(v, 5.0)))
* (5.0
+ (28.0 * TanSquared(phiPrime))
+ (24.0 * TanSquared(phiPrime) * TanSquared(phiPrime)));
var XIIA =
(Sec(phiPrime) / (5040.0 * Math.Pow(v, 7.0)))
* (61.0
+ (662.0 * TanSquared(phiPrime))
+ (1320.0 * TanSquared(phiPrime) * TanSquared(phiPrime))
+ (720.0
* TanSquared(phiPrime)
* TanSquared(phiPrime)
* TanSquared(phiPrime)));
phi =
phiPrime
- (VII * Math.Pow(E - E0, 2.0))
+ (VIII * Math.Pow(E - E0, 4.0))
- (IX * Math.Pow(E - E0, 6.0));
lambda =
lambda0
+ (X * (E - E0))
- (XI * Math.Pow(E - E0, 3.0))
+ (XII * Math.Pow(E - E0, 5.0))
- (XIIA * Math.Pow(E - E0, 7.0));
lat = Rad2Deg(phi);
lng = Rad2Deg(lambda);
// convert to WGS84
OSGB36ToWGS84();
return new LatLon(lat, lng);
}
}
public class RefEll
{
public double maj, min, ecc;
public RefEll(double major, double minor)
{
maj = major;
min = minor;
ecc = ((major * major) - (minor * minor)) / (major * major);
}
}
public class LatLon
{
public double Latitude;
public double Longitude;
public LatLon()
{
Latitude = 0;
Longitude = 0;
}
public LatLon(double lat, double lon)
{
Latitude = lat;
Longitude = lon;
}
}

I ran into the same issue. Ended up using this library: https://github.com/Tronald/CoordinateSharp
This sample will return the exact position in latlon decimal.
UniversalTransverseMercator utm = new UniversalTransverseMercator("T", 30, 581177.3879, 4794824.5279);
Coordinate c = UniversalTransverseMercator.ConvertUTMtoLatLong(utm);
c.FormatOptions.Format = CoordinateFormatType.Decimal;
c.FormatOptions.Round = 7;
Debug.Log($"({c.Latitude}, {c.Longitude})");

Related

Converting TSQL code to C# data type problem

I was trying to answer this SO question..
Given the following TSQL code
DECLARE #input1 INT = 100000
DECLARE #input2 INT = 40
DECLARE #input3 INT = 106833
DECLARE #X decimal(22,6) = 0
DECLARE #Y decimal(22,6) = 0.001
DECLARE #Z decimal(22,6)
DECLARE #r decimal(22,6)
DECLARE #v decimal(22,6)
SET #v = POWER(1/(1+ (#Y/12)), #input2)
SET #r = ((#Y/#input2) * #input1) / (1-#v)
IF (#r < #input3)
SET #Z = #Y + ABS((#X - #Y)/2)
ELSE
SET #Z = #Y - ABS((#X - #Y) /2)
SET #X = #Y
SET #Y = #Z
WHILE (ABS(#r - #input3) > 0.001)
BEGIN
SET #v = POWER(1/(1+ (#Y/12)), #input2)
SET #r = ((#Y/#input2) * #input1) / (1-#v)
IF (#r < #input3)
SET #Z = #Y + ABS((#X - #Y)/2)
ELSE
SET #Z = #Y - ABS((#X - #Y) /2)
SET #X = #Y
IF #Y = #Z
BREAK
SET #Y = #Z
END
SELECT (CAST(#Y AS decimal(22,6)) * 100)
I tried to convert it to C#
decimal input1 = 100000m;
decimal input2 = 40m;
decimal input3 = 106833m;
decimal x = 0m;
decimal y = 0.001m;
decimal z;
decimal r;
decimal v;
v = (decimal)(Math.Pow(Convert.ToDouble(1m / (1m + (y / 12m))), Convert.ToDouble(input2)));
r = ((y / input2) * input1) / (1 - v);
if (r<input3)
{
z = y + Math.Abs((x - y) / 2);
}
else
{
z = y - Math.Abs((x - y) / 2);
}
x = y;
y = z;
while (Math.Abs(r - input3) > 0.001m)
{
v = (decimal)(Math.Pow(Convert.ToDouble(1 / (1 + (y / 12))), Convert.ToDouble(input2)));
r = ((y / input2) * input1) / (1 - v);
if (r<input3)
{
z = y + Math.Abs((x - y) / 2);
}
else
{
z = y - Math.Abs((x - y) / 2);
}
x = y;
if (y==z) break;
y = z;
}
Console.WriteLine(y*100);
But the results are different.
The TSQL returns 4273.320000 whereas the C# code returns 0,1999999999999999999999998900.
Furthermore if i put the exact same C# code inside a SqlFunction (CLR function) via
[Microsoft.SqlServer.Server.SqlFunction]
public static decimal CalcFinancialSpreading(decimal input1 = 100000, decimal input2 = 40, decimal input3 = 106833)
it returns 0
Does anybody spot the error?
Your Solution it is. So answer is c# decimal precision is different than SQL, in SQL you are use 6 precision digit. So always set your number same like this (example) you can write your own helper converter class. :
public static decimal ConvertTo6(double d)
{
return Math.Round(Convert.ToDecimal(d), 6, MidpointRounding.AwayFromZero);
}
public static decimal ConvertTo6(decimal d)
{
return Math.Round(d, 6, MidpointRounding.AwayFromZero);
}
static void Main(string[] args)
{
int input1 = 100000;
int input2 = 40;
int input3 = 106833;
decimal x = 0.000000m;
decimal y = 0.001000m;
decimal z;
decimal r;
decimal v;
v = ConvertTo6(Pow(1 / (1 + (Convert.ToDouble(y) / 12d)), input2));
r = ConvertTo6(((y / input2) * input1) / (1 - v));
if (r < input3)
{
z = y + Math.Abs((x - y) / 2);
z = ConvertTo6(z);
}
else
{
z = y - Math.Abs((x - y) / 2);
z = ConvertTo6(z);
}
x = y;
y = z;
while (Math.Abs(r - input3) > 0.001m)
{
v = ConvertTo6((Math.Pow(Convert.ToDouble(1 / (1 + (y / 12))), Convert.ToDouble(input2))));
r = ((y / input2) * input1) / (1 - v);
r = ConvertTo6(r);
if (r < input3)
{
z = y + Math.Abs((x - y) / 2);
z = ConvertTo6(z);
}
else
{
z = y - Math.Abs((x - y) / 2);
z = ConvertTo6(z);
}
x = y;
if (y == z) break;
y = z;
}
Console.WriteLine(y * 100);
Console.Read();
}

Implementing Cardano method for a cubic equation solution

I'm trying to find real roots for a cubic equation defined by a set of four coefficients by using Cardano method as described here. The problem is, the roots found by my implementation do not actually work - testing by inserting them in the equation give a significant error (more than the required 10^-6). Is the algorithm implemented wrong, or the error is caused by something else, like rounding accuracy?
static double CubicRoot(double n)
{
return Math.Pow(Math.Abs(n), 1d / 3d) * Math.Sign(n);
}
public static List<double> SolveCubic(double A, double B = 0, double C = 0, double D = 0)
{
List<double> output = new List<double>();
if (A != 0)
{
double A1 = B / A;
double A2 = C / A;
double A3 = D / A;
double P = -((A1 * A1) / 3) + A2;
double Q = ((2.0 * A1 * A1 * A1) / 27.0) - ((A1 * A2) / 3.0) + A3;
double cubeDiscr = Q * Q / 4.0 + P * P * P / 27.0;
if (cubeDiscr > 0)
{
double u = CubicRoot(-Q / 2.0 + Math.Sqrt(cubeDiscr));
double v = CubicRoot(-Q / 2.0 - Math.Sqrt(cubeDiscr));
output.Add(u + v - (A1 / 3.0));
return output;
}
else if (cubeDiscr == 0)
{
double u = CubicRoot(-Q / 2.0);
output.Add(2u - (A1 / 3.0));
output.Add(-u - (A1 / 3.0));
}
else if (cubeDiscr < 0)
{
double r = CubicRoot(Math.Sqrt(-(P * P * P / 27.0)));
double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
output.Add(r * (Math.Cos(alpha / 3.0) + Math.Cos((6 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
output.Add(r * (Math.Cos((2 * Math.PI + alpha) / 3.0) + Math.Cos((4 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
output.Add(r * (Math.Cos((4 * Math.PI + alpha) / 3.0) + Math.Cos((2 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
}
}
return output;
}
A few things
Math.Sign will return zero on zero, which happens to be what you want in this case, but perhaps you are not so lucky with code or algorithm change.
You will have rounding issues and not execute cubeDiscr == 0 branch when you should. You may have rounding issues and execute the wrong > 0 and < 0 branch for the same reason. Test within a delta of zero instead (see below).
But the cubeDiscr == 0 branch is wrong because 1) you didn't calculate v and 2) 2u is an UInt32 with a value of 2, not 2*u.
Calculating alpha is wrong (see below)
(there may be more, but that's all I saw at a quick glance)
On calculating alpha:
double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
is not the same as
double alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0) // if q > 0 the angle becomes PI + alpha
alpha = Math.PI + alpha;
What's wrong with using the code included from that page?
public double Xroot(double a, double x)
{
double i = 1;
if (a < 0)
i = -1;
return (i * Math.Exp( Math.Log(a*i)/x));
}
public int Calc_Cardano() // solve cubic equation according to cardano
{
double p, q, u, v;
double r, alpha;
int res;
res = 0;
if (a1 != 0)
{
a = b / a1;
b = c / a1;
c = d / a1;
p = -(a * a / 3.0) + b;
q = (2.0 / 27.0 * a * a * a) - (a * b / 3.0) + c;
d = q * q / 4.0 + p * p * p / 27.0;
if (Math.Abs(d) < Math.Pow(10.0, -11.0))
d = 0;
// 3 cases D > 0, D == 0 and D < 0
if (d > 1e-20)
{
u = Xroot(-q / 2.0 + Math.Sqrt(d), 3.0);
v = Xroot(-q / 2.0 - Math.Sqrt(d), 3.0);
x1.real = u + v - a / 3.0;
x2.real = -(u + v) / 2.0 - a / 3.0;
x2.imag = Math.Sqrt(3.0) / 2.0 * (u - v);
x3.real = x2.real;
x3.imag = -x2.imag;
res = 1;
}
if (Math.Abs(d) <= 1e-20)
{
u = Xroot(-q / 2.0, 3.0);
v = Xroot(-q / 2.0, 3.0);
x1.real = u + v - a / 3.0;
x2.real = -(u + v) / 2.0 - a / 3.0;
res = 2;
}
if (d < -1e-20)
{
r = Math.Sqrt(-p * p * p / 27.0);
alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0) // if q > 0 the angle becomes PI + alpha
alpha = Math.PI + alpha;
x1.real = Xroot(r, 3.0) * (Math.Cos((6.0 * Math.PI - alpha) / 3.0) + Math.Cos(alpha / 3.0)) - a / 3.0;
x2.real = Xroot(r, 3.0) * (Math.Cos((2.0 * Math.PI + alpha) / 3.0) + Math.Cos((4.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
x3.real = Xroot(r, 3.0) * (Math.Cos((4.0 * Math.PI + alpha) / 3.0) + Math.Cos((2.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
res = 3;
}
}
else
res = 0;
return res;
}

Why is converting RGB to CMYK not showing the correct result

I am using this site as a reference: http://www.rapidtables.com/convert/color/rgb-to-cmyk.htm
My code:
public void Convert2CMYK()
{
float c, m, y, k;
if (inRed == 0 && inGreen == 0 && inBlue == 0)
{
tbCyan.Text = "0";
tbMagenta.Text = "0";
tbYellow.Text = "0";
tbBlack.Text = "1";
}
c = 1 - (inRed / 255f);
m = 1 - (inGreen / 255f);
y = 1 - (inBlue / 255f);
var minCMY = Math.Min(c, Math.Min(m, y));
c = (c - minCMY) / (1 - minCMY) * 100;
m = (m - minCMY) / (1 - minCMY) * 100;
y = (y - minCMY) / (1 - minCMY) * 100;
k = minCMY * 100;
tbCyan.Text = c.ToString();
tbMagenta.Text = m.ToString();
tbYellow.Text = y.ToString();
tbBlack.Text = k.ToString();
}
On the site, R=25, G=25, B=25 results in C=0, M=0, Y=0, K=0.902
In the app (my code), R=25, G=25, B=25 results in C=0, M=0, Y=0, K=90.19608
What do I have to modify to ensure my results are accurate.
Thanks to everyone for their help. Here are the final equations which did the trick:
c = Math.Round((c - minCMY) / (1 - minCMY), 3);
m = Math.Round((m - minCMY) / (1 - minCMY), 3);
y = Math.Round((y - minCMY) / (1 - minCMY), 3);
k = Math.Round(minCMY, 3);

Conversion from NZMG to latitude and longitude

I want to convert NEW ZEALAND MAP GRID coordinates i.e. Northing and Easting into WGS84 coordinates i.e. Latitude and Longitude.
I have searched on internet but there is no proper explanation of how to do this, or an online calculator to do this.
My final goal is to write a program in C# or JAVA , which will convert NZMG coordinates into WGS84 coordinates.
Please note: This implementation does not convert with pinpoint accuracy.
Here is a c# implementation I put together for converting from NZMG to NZGD1949 and then to datum shift from NZGD1949 to NZGD2000. NZGD2000 coordinates are apparently compatible with WGS84 source: http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/wgs84-nzgd2000. I have added a 'useStaticCorrection' parameter to the datum shift method because I found my results were consistently off by approximately 40 meters in latitude - this varies depending on the input coordinates. This may be from a mistake in the logic but, as far as i can tell, it appears all correct (as by the sources referenced).
Sources:
Constants and coefficients
http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/projection-conversions/new-zealand-map-grid
Datum info.
http://www.linz.govt.nz/regulatory/25000
NZMG -> NZGD1949 formula
http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/projection-conversions/new-zealand-map-grid
NZ1949 -> NZGD2000 differences
http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/nzgd1949-nzgd2000/three-seven
Equivilent of a Three parameter shift.
http://sas2.elte.hu/tg/eesti_datums_egs9.htm
//Lat / Long fudge amounts http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/datum-transformation-examples
public class Pair<T, U>
{
public Pair()
{
}
public Pair(T first, U second)
{
this.First = first;
this.Second = second;
}
public T First { get; set; }
public U Second { get; set; }
};
using System.Numerics;
public class NZMGConverter
{
//Constants and coefficients
//Source: http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/projection-conversions/new-zealand-map-grid
private static Double N0 = 6023150;
private static Double E0 = 2510000;
private static Double NZGD1949a = 6378388;
private static Double NZMGLatOrigin = -41;
private static Double NZMGLongOrigin = 173;
//some of these are unused but are included for completeness. (unused arrays are for converting from wgs84 / nzgd2000 -> nzmg).
private static Double[] A = new Double[] { 0.6399175073, -0.1358797613, 0.0632944090, -0.0252685300, 0.0117879000, -0.0055161000, 0.0026906000, -0.0013330000, 0.0006700000, -0.0003400000 };
private static Complex[] B = new Complex[] { new Complex(0.7557853228, 0.0), new Complex(0.249204646, 0.003371507), new Complex(-0.001541739, 0.04105856), new Complex(-0.10162907, 0.01727609), new Complex(-0.26623489, -0.36249218), new Complex(-0.6870983, -1.1651967) };
private static Complex[] C = new Complex[] { new Complex(1.3231270439, 0.0), new Complex(-0.577245789, -0.007809598), new Complex(0.508307513, -0.112208952), new Complex(-0.15094762, 0.18200602), new Complex(1.01418179, 1.64497696), new Complex(1.9660549, 2.5127645) };
private static Double[] D = new Double[] { 1.5627014243, 0.5185406398, -0.0333309800, -0.1052906000, -0.0368594000, 0.0073170000, 0.0122000000, 0.0039400000, -0.0013000000 };
//Datum info.
//Source: http://www.linz.govt.nz/regulatory/25000
private static Double NZGD1949f = 0.003367003;
//Double NZGD1949InverseFlattening = 297;
private static Double NZGD1949e2 = (2 * 0.003367003) - Math.Pow(0.003367003, 2);
private static Double NZGD2000a = 6378137;
private static Double NZGD2000f = 0.003352811;
//Double NZGD2000InverseFlattening = 298.2572221;
private static Double NZGD2000e2 = (2 * 0.003352811) - Math.Pow(0.003352811, 2);
//NZ1949 -> NZGD2000 differences
//Source: http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/nzgd1949-nzgd2000/three-seven
private static Double differenceX = 54.4;
private static Double differenceY = -20.1;
private static Double differenceZ = 183.1;
//Source: http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/projection-conversions/new-zealand-map-grid
public static Pair<Double, Double> convertToNZGD1949(Double nzmgX, Double nzmgY)
{
Pair<Double, Double> result = null;
Complex z = new Complex((nzmgY - N0) / NZGD1949a, (nzmgX - E0) / NZGD1949a);
Complex theta0 = new Complex();
for (int i = 0; i < C.Length; ++i) { theta0 += Complex.Multiply(C[i], Complex.Pow(z, i + 1)); }
Double deltaLatWtheta0 = 0;
for (int i = 0; i < D.Length; ++i) { deltaLatWtheta0 += D[i] * Math.Pow(theta0.Real, (i + 1)); }
result = new Pair<double, double>();
result.First = NZMGLatOrigin + (Math.Pow(10, 5) / 3600) * deltaLatWtheta0; //NZGD1949 lat
result.Second = NZMGLongOrigin + 180 / Math.PI * theta0.Imaginary; //NZGD1949 long
return result;
}
//Equivilent of a Three parameter shift.
//Source: http://sas2.elte.hu/tg/eesti_datums_egs9.htm
public static Pair<Double, Double> datumShiftNZGD1949toNZGD2000(Double nzgd1949Lat, Double nzgd1949Long, Boolean useStaticCorrection)
{
Pair<Double, Double> result = null;
Double latInRaidans = nzgd1949Lat * (Math.PI / 180);
Double lngInRaidans = nzgd1949Long * (Math.PI / 180);
Double n = NZGD1949a / Math.Sqrt((1 - NZGD1949e2 * Math.Pow(Math.Sin(latInRaidans), 2)) * (3 / 2));
Double m = NZGD1949a * ((1 - NZGD1949e2) / (1 - NZGD1949e2 * Math.Pow(Math.Sin(latInRaidans), 2) * (3 / 2)));
Double difF = NZGD2000f - NZGD1949f;
Double difA = NZGD2000a - NZGD1949a;
Double changeInLat = (-1 * differenceX * Math.Sin(latInRaidans) * Math.Cos(lngInRaidans)
- differenceY * Math.Sin(latInRaidans) * Math.Sin(lngInRaidans)
+ differenceZ * Math.Cos(latInRaidans) + (NZGD1949a * difF + NZGD1949f * difA) * Math.Sin(2 * latInRaidans))
/ (m * Math.Sin(1));
Double changeInLong = (-1 * differenceX * Math.Sin(lngInRaidans) + differenceY * Math.Cos(lngInRaidans)) / (n * Math.Cos(latInRaidans) * Math.Sin(1));
result = new Pair<Double, Double>();
result.First = (latInRaidans + changeInLat) * (180 / Math.PI);
result.Second = (lngInRaidans + changeInLong) * (180 / Math.PI);
if (useStaticCorrection)
{
//Difference found by 3-parameter test values # http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/datum-transformation-examples
Double staticLatCorrection = 0.00037432;
Double staticLongCorrection = -0.00003213;
result.First -= staticLatCorrection;
result.Second += staticLongCorrection;
}
return result;
}
}
NZMG to Latitude/Longitude using Swift. Thanks to DavidG's post above. Fixed 40m latitude error.
import Foundation
import Darwin
class NZMGConverter {
private let easting: Double!
private let northing: Double!
private var z: Complex! = nil
private let N0: Double = 6023150; //false Northing
private let E0: Double = 2510000; //false Easting
private let NZMGLatOrigin: Double = -41;
private let NZMGLongOrigin: Double = 173;
private let c: [Complex] = [Complex(real: 1.3231270439, imag: 0.0), Complex(real: -0.577245789, imag: -0.007809598), Complex(real: 0.508307513, imag: -0.112208952), Complex(real: -0.15094762, imag: 0.18200602), Complex(real: 1.01418179, imag: 1.64497696), Complex(real: 1.9660549, imag: 2.5127645)]
private let b: [Complex] = [Complex(real: 0.7557853228, imag: 0.0), Complex(real: 0.249204646, imag: 0.003371507), Complex(real: -0.001541739, imag: 0.04105856), Complex(real: -0.10162907, imag: 0.01727609), Complex(real: -0.26623489, imag: -0.36249218), Complex(real: -0.6870983, imag: -1.1651967)]
private let d: [Double] = [1.5627014243, 0.5185406398, -0.0333309800, -0.1052906000, -0.0368594000, 0.0073170000, 0.0122000000, 0.0039400000, -0.0013000000]
//Datum info.
private let NZGD1949f: Double = 0.003367003;
//Double NZGD1949InverseFlattening = 297;
private let NZGD1949e2: Double = (2 * 0.003367003) - pow(0.003367003, 2);
private let NZGD1949a: Double = 6378388; //Semi-major axis
private let NZGD2000a: Double = 6378137;
private let NZGD2000f: Double = 0.003352811;
//Double NZGD2000InverseFlattening = 298.2572221;
private let NZGD2000e2: Double = (2 * 0.003352811) - pow(0.003352811, 2);
//NZ1949 -> NZGD2000 differences
private let differenceX: Double = 54.4;
private let differenceY: Double = -20.1;
private let differenceZ: Double = 183.1;
init(easting: Double, northing: Double) {
self.easting = easting
self.northing = northing
}
func nzmgToNZGD1949() -> (latitude: Double, longitude: Double) {
z = Complex(real: (northing - N0) / NZGD1949a, imag: (easting - E0) / NZGD1949a)
let theta0 = thetaZero(z)
let theta: Complex = thetaSuccessive(theta0, i: 0)
let latlong = calcLatLong(theta)
print(latlong)
return datumShift(latlong.0, longitude: latlong.1)
}
func thetaZero(z: Complex) -> Complex {
var theta0 = Complex()
for var i = 0; i < c.count; i++ {
theta0 = theta0 + (c[i] * (z ^ (i + 1)))
//print(theta0)
}
return theta0
}
func thetaSuccessive(theta0: Complex, i: Int) -> Complex {
var numerator: Complex = Complex()
var denominator: Complex = Complex()
for var n = 1; n < b.count; n++ {
numerator = numerator + (Complex(real: n) * b[n] * (theta0 ^ (n + 1)))
}
numerator = z + numerator
for var n = 0; n < b.count; n++ {
denominator = denominator + (Complex(real: n + 1) * b[n] * (theta0 ^ n))
}
let theta = numerator / denominator
if i == 2 {
return theta
} else {
return thetaSuccessive(theta, i: i + 1)
}
}
func calcLatLong(theta: Complex) -> (latitude: Double, longitude: Double) {
let deltaPsi = theta.real
let deltaLambda = theta.imag
var deltaPhi: Double = 0
for var n = 0; n < d.count; n++ {
deltaPhi += d[n] * pow(deltaPsi, Double(n + 1))
}
let lat: Double = NZMGLatOrigin + ((pow(10, 5) / 3600) * deltaPhi)
let long: Double = NZMGLongOrigin + ((180 / M_PI) * deltaLambda)
return (latitude: lat, longitude: long)
}
func datumShift(latitude: Double, longitude: Double) -> (latitude: Double, longitude: Double) {
let latInRadians = latitude * (M_PI / 180)
let lngInRadians = longitude * (M_PI / 180)
let v = NZGD1949a / (sqrt(1 - (NZGD1949e2 * pow(sin(latInRadians), 2))))
var x_cart = v * cos(latInRadians) * cos(lngInRadians)
var y_cart = v * cos(latInRadians) * sin(lngInRadians)
var z_cart = (v * (1 - NZGD1949e2)) * sin(latInRadians)
x_cart += differenceX
y_cart += differenceY
z_cart += differenceZ
print(x_cart)
print(y_cart)
print(z_cart)
let p = sqrt(pow(x_cart, 2) + pow(y_cart, 2))
let r = sqrt(pow(p, 2) + pow(z_cart, 2))
let mu = atan((z_cart / p) * ((1 - NZGD2000f) + ((NZGD2000e2 * NZGD2000a) / r)))
let num = (z_cart * (1 - NZGD2000f)) + (NZGD2000e2 * NZGD2000a * pow(sin(mu), 3))
let denom = (1 - NZGD2000f) * (p - (NZGD2000e2 * NZGD2000a * pow(cos(mu), 3)))
var lat = atan(num / denom)
var long = atan(y_cart / x_cart)
print(long)
lat = (180 / M_PI) * lat
long = 180 + (180 / M_PI) * long
return (latitude: lat, longitude: long)
}
}
}
Proj4j provides this functionality:
It is one of the few universal coordinate conversions libs.
http://trac.osgeo.org/proj4j/
Then you have to find the proj4 string that defines the new zeal and datum description:
Depending if you need th eold or the new grid, you need the new zealand datum shift grids,+
which are included in proj4.
As a first step find exactly the name of the datum that you need:
NZGD49 or NZGD2000 ?

Refactoring blocks of code that differ only by one function call

I've got similar formulas for counting Hartley transformation. The only difference is input function - sin, cos, exp in the following lines of code:
Math.Exp((double)tau)
Math.Sin((double)tau)
Math.Cos((double)tau)
How can I escape almost identical code fragments and shorten my code in following fragment?
private void CountHartley(ref double [] arr, string function)
{
int N = arr.Length;
if (function == "exp")
{
for (int nu = 0, tau = 0; ((nu < N) && (tau < N)); nu++, tau++)
{
arr[nu] = 1 / (double)N *
Math.Exp((double)tau) *
(Math.Sin(2 * Math.PI * nu * tau / (double) N) +
Math.Cos(2 * Math.PI * nu * tau / (double) N));
}
}
else if (function == "sin")
{
for (int nu = 0, tau = 0; ((nu < N) && (tau < N)); nu++, tau++)
{
arr[nu] = 1 / (double)N *
Math.Sin((double)tau) *
(Math.Sin(2 * Math.PI * nu * tau / (double)N) +
Math.Cos(2 * Math.PI * nu * tau / (double)N));
}
}
else
{
for (int nu = 0, tau = 0; ((nu < N) && (tau < N)); nu++, tau++)
{
arr[nu] = 1 / (double)N *
Math.Cos((double)tau) *
(Math.Sin(2 * Math.PI * nu * tau / (double)N) +
Math.Cos(2 * Math.PI * nu * tau / (double)N));
}
}
}
Instead of passing in a string with the function to use, you can pass in the function directly.
You can use the Func<T, TResult> Delegate for this as follows:
private void CountHartley(ref double [] arr, Func<double, double> function)
{
int N = arr.Length;
for (int nu = 0, tau = 0; ((nu < N) && (tau < N)); nu++, tau++)
{
arr[nu] = 1 / (double)N *
function((double)tau) *
(Math.Sin(2 * Math.PI * nu * tau / (double) N) +
Math.Cos(2 * Math.PI * nu * tau / (double) N));
}
}
Usage:
var result = CountHartley(arr, Math.Cos);
How about passing in the Math.* as an argument to a Func<double, double> function parameter?

Categories

Resources