Conversion from NZMG to latitude and longitude - c#

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 ?

Related

Convert Easting/Northing to Lat Lng

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})");

The function outputs the wrong value

When ever I input these coordinates I get a wrong output.
static double ReadCoordinateFromConsole(double lat1, double lon1, double
lat2, double lon2)
{
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1);
var dLon = deg2rad(lon2 - lon1);
var a =
Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) *
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
var d = R * c; // Distance in km
return d;
}
static double deg2rad(double deg)
{
return deg * (Math.PI / 180);
}
Then in my function where I input some coordinates. 41.507483 -99.436554 38.504048 -98.315949. These coordinates should equal about 347 but instead, I get the output 7022,88 which is wrong and I have no clue as to why.
static double ReadDoubleFromConsole(string msg)
while (true)
{
Console.Write(msg);
string test = Console.ReadLine();
string[] words = test.Split(' ');
bool inputContainsNumber = Regex.IsMatch(words[0], #"^-*[0-9,\.]+$");
bool inputContainsNumber2 = Regex.IsMatch(words[1], #"^-*[0-9,\.]+$");
bool inputContainsNumber3 = Regex.IsMatch(words[2], #"^-*[0-9,\.]+$");
bool inputContainsNumber4 = Regex.IsMatch(words[3], #"^-*[0-9,\.]+$");
if(inputContainsNumber && inputContainsNumber2 && inputContainsNumber3
&& inputContainsNumber4)
{
double test1 = double.Parse(words[0]);
double test2 = double.Parse(words[1]);
double test3 = double.Parse(words[2]);
double test4 = double.Parse(words[3]);
double test5 = ReadCoordinateFromConsole(test1, test2, test3,
test4);
return test5;
}
Console.WriteLine("hmm, doesn't look correct - try again");
}
}
As you've been shown by the other answer your algorithm is correct - your parsing of the user input is wrong!
Here's the demonstration: http://rextester.com/IEEA93176
And the reason is, that in the culture that rextester runs in the , is used as a decimal separator - and I assume its the same in your environment. When you use double.parse("41.1234") in a culture expecting a , you get the value 411234 not 41.1234.
One fix is to force the culture
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
Live demo(working): http://rextester.com/KALRN89806
There's nothing wrong with your implementation of the Haversine formula - it generates the correct output.
The following prints 347.328348039426:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
Console.WriteLine(ReadCoordinateFromConsole(41.507483, -99.436554, 38.504048, -98.315949));
}
static double ReadCoordinateFromConsole(double lat1, double lon1, double
lat2, double lon2)
{
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1);
var dLon = deg2rad(lon2 - lon1);
var a =
Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) *
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
var d = R * c; // Distance in km
return d;
}
static double deg2rad(double deg)
{
return deg * (Math.PI / 180);
}
}
}
Therefore you must look elsewhere for your error. I suspect you're not feeding the correct values to the method - I recommend that you single-step your code in the debugger to determine what's happening.
Incidentally, is there any reason that you're not using the .Net GeoCoordinate class to calculate this? For example:
using System;
using System.Device.Location;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
var a = new GeoCoordinate(41.507483, -99.436554);
var b = new GeoCoordinate(38.504048, -98.315949);
Console.WriteLine(a.GetDistanceTo(b)/1000.0);
}
}
}
This prints 347.628192006498.

Recursive way to calculate distance among co-ordinates

I am able to calculate the distance between two points(Latitude and Longtitude) by using the below function
public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
{
double sLatitude = 48.672309;
double sLongitude = 15.695585;
double eLatitude = 48.237867;
double eLongitude = 16.389477;
var coordinate1 = new GeoCoordinate(lat1, lng1);
var coordinate2 = new GeoCoordinate(lat2, lng2);
var resultInMeter = coordinate1.GetDistanceTo(coordinate2); //in meters
//convert to KM : 1 meter = 0.001 KM
var resultInKM = resultInMeter * 0.001; //in KM's
return resultInKM ;
}
this works as under
double lat1 = 48.672309;
double lng1 = 15.695585;
double lat2 = 48.237867;
double lng2 = 16.389477;
var distance = GetDistance(lat1,lng1,lat2,lng2);
Now say I have a collection of Latitudes and Longtitudes as under
double[,] arrLatLong = new double[,]
{
{22.57891304, 88.34285622},
{22.54398196, 88.38221001},
{22.58277011, 88.38303798},
{22.59950095, 88.39352995},
{22.59887647, 88.32905243},
};
How to calculate the total distance? Is there any recursive way?
Trying to use recursion will only make this problem more difficult. Plain old iteration is more than sufficient.
double dist = 0.0;
for (int i = 0; i < arrLatLong.GetLength(0) - 1; i++)
{
dist += GetDistance(
arrLatLong[i, 0],
arrLatLong[i, 1],
arrLatLong[i+1, 0],
arrLatLong[i+1, 1]);
}
If you absolutely must use a recursive method, this should do the trick:
double GetDistanceRecursive(double[,] coords, int idx)
{
if (idx + 1 >= coords.Length)
return 0.0;
double dist = GetDistance(
coords[idx, 0],
coords[idx, 1],
coords[idx+1, 0],
coords[idx+1, 1]);
dist += GetDistanceRecursive(coords, idx + 1);
return dist;
}

Quadratic equation formula

I am trying to make a program that calculates the answer of a quadratic equation with the general formula but i am encountering a few errors.
The way I have my Windows Form Application set up it asks for a, b, and c, and substitutes them in the general formula.
I have 3 text boxes, one for each value of a, b and c and one more for the answer, and it is supposed to work with a button I called "calculate".
My problem is that when I try something other than a perfect trinomial square, the answer comes up as NaN.
Here is some of the code I have:
private void textBox1_TextChanged(object sender, EventArgs e)
{
a = Double.Parse(textBox1.Text);
}
^ This is how I am assigning values to the variables
double sqrtpart = b * b - 4 * a * c;
answer = (b + Math.Sqrt(sqrtpart)) / 2 * a;
textBox4.Text = answer.ToString();
group 2a and make sure values are valid (b^2 > 4ac)
answer = (b + Math.Sqrt(sqrtpart)) / (2 * a);
First, make sure that 4ac < b². The quadratic formula also utilizes the ± sign, so we must make two separate variables to hold the two different answers.
double sqrtpart = (b * b) - (4 * a * c);
answer1 = ((-1)*b + Math.Sqrt(sqrtpart)) / (2 * a);
answer2 = ((-1)*b - Math.Sqrt(sqrtpart)) / (2 * a);
textBox4.Text = answer1.ToString() + " and " + answer2.ToString();
If you want your calculator to be able to compute complex numbers try the following
string ans = "";
double root1 = 0;
double root2 = 0;
double b = 0;
double a = 0;
double c = 0;
double identifier = 0;
a =Convert.ToDouble(Console.ReadLine());
b = Convert.ToDouble(Console.ReadLine());
c = Convert.ToDouble(Console.ReadLine());
identifier = b * b - (4 * a * c);
if (identifier > 0)
{
root1 = (-b+(Math.Sqrt(identifier)/(2*a)));
root2 = (-b - (Math.Sqrt(identifier) / (2 * a)));
string r1 = Convert.ToString(root1);
string r2 = Convert.ToString(root2);
ans = "Root1 =" + r1 + "Root2 = " + r2;
Console.WriteLine(ans);
}
if (identifier < 0)
{
double Real = (-b / (2 * a));
double Complex = ((Math.Sqrt((identifier*(-1.00))) / (2 * a)));
string SReal = Convert.ToString(Real);
string SComplex = Convert.ToString(Complex);
ans = "Roots = " + SReal + "+/-" + SComplex + "i";
Console.WriteLine(ans);
}
if (identifier == 0)
{
root1 = (-b / (2 * a));
string Root = Convert.ToString(root1);
ans = "Repeated roots : " + Root;
}
Use the following code:
using System;
using System.Collections.Generic;
using System.Text;
namespace SoftwareAndFinance
{
class Math
{
// quadratic equation is a second order of polynomial equation in a single variable
// x = [ -b +/- sqrt(b^2 - 4ac) ] / 2a
public static void SolveQuadratic(double a, double b, double c)
{
double sqrtpart = b * b - 4 * a * c;
double x, x1, x2, img;
if (sqrtpart > 0)
{
x1 = (-b + System.Math.Sqrt(sqrtpart)) / (2 * a);
x2 = (-b - System.Math.Sqrt(sqrtpart)) / (2 * a);
Console.WriteLine("Two Real Solutions: {0,8:f4} or {1,8:f4}", x1, x2);
}
else if (sqrtpart < 0)
{
sqrtpart = -sqrtpart;
x = -b / (2 * a);
img = System.Math.Sqrt(sqrtpart) / (2 * a);
Console.WriteLine("Two Imaginary Solutions: {0,8:f4} + {1,8:f4} i or {2,8:f4} + {3,8:f4} i", x, img, x, img);
}
else
{
x = (-b + System.Math.Sqrt(sqrtpart)) / (2 * a);
Console.WriteLine("One Real Solution: {0,8:f4}", x);
}
}
static void Main(string[] args)
{
// 6x^2 + 11x - 35 = 0
SolveQuadratic(6, 11, -35);
// 5x^2 + 6x + 1 = 0
SolveQuadratic(5, 6, 1);
// 2x^2 + 4x + 2 = 0
SolveQuadratic(2, 4, 2);
// 5x^2 + 2x + 1 = 0
SolveQuadratic(5, 2, 1);
}
}
}
Here is the original source.
A little late, but here is my solution:
using System;
using System.Collections.Generic;
namespace MyFunctions
{
class Program
{
static void Main(string[] args)
{
printABCSolution(1, -3, 4);
printABCSolution(1, -4, 4);
printABCSolution(1, -5, 4);
printABCSolution(9, 30, 25);
printABCSolution(9, -15, 25);
Console.ReadKey();
}
private static void printABCSolution(double a, double b, double c)
{
Console.WriteLine($"Solution a={a} b={b} c={c}");
var solution = ABCMath.ABCFormula(a, b, c);
Console.WriteLine($"# Solutions found: {solution.Count}");
solution.ForEach(x => Console.WriteLine($"x={x}"));
}
}
public static class ABCMath
{
public static List<double> ABCFormula(double a, double b, double c)
{
// Local formula
double formula(int plusOrMinus, double d) => (-b + (plusOrMinus * Math.Sqrt(d))) / (2 * a);
double discriminant = b * b - 4 * a * c;
List<double> result = new List<double>();
if (discriminant >= 0)
{
result.Add(formula(1, discriminant));
if (discriminant > 0)
result.Add(formula(-1, discriminant));
}
return result;
}
}
}
Output:
Solution a=1 b=-3 c=4
# Solutions found: 0
Solution a=1 b=-4 c=4
# Solutions found: 1
x=2
Solution a=1 b=-5 c=4
# Solutions found: 2
x=4
x=1
Solution a=9 b=30 c=25
# Solutions found: 1
x=-1,66666666666667
Solution a=9 b=-15 c=25
# Solutions found: 0
Here is code:
using System;
namespace RoomPaintCost
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("This program applies quadratic formula to Ax^2+Bx+C=0 problems");
double[] ABC= getABC();
double[] answerArray=QuadFormula(ABC);
Console.WriteLine("Positive answer: {0:0.000} \n Negative answer: {1:0.000}", answerArray[0], answerArray[1]);
}
//Form of Ax^2 + Bx + C
//Takes array of double containing A,B,C
//Returns array of positive and negative result
public static double[] QuadFormula(double[] abc)
{
NotFiniteNumberException e = new NotFiniteNumberException();
try
{
double a = abc[0];
double b = abc[1];
double c = abc[2];
double discriminant = ((b * b) - (4 * a * c));
if (discriminant < 0.00)
{
throw e;
}
discriminant = (Math.Sqrt(discriminant));
Console.WriteLine("discriminant: {0}",discriminant);
double posAnswer = ((-b + discriminant) / (2 * a));
double negAnswer = ((-b - discriminant) / (2 * a));
double[] xArray= { posAnswer,negAnswer};
return xArray;
}
catch (NotFiniteNumberException)
{
Console.WriteLine("Both answers will be imaginary numbers");
double[] xArray = { Math.Sqrt(-1), Math.Sqrt(-1) };
return xArray;
}
}
public static double[] getABC()
{
Console.Write("A=");
string sA = Console.ReadLine();
Console.Write("B=");
string sB = Console.ReadLine();
Console.Write("C=");
string sC = Console.ReadLine();
int intA = Int32.Parse(sA);
int intB = Int32.Parse(sB);
int intC = Int32.Parse(sC);
double[] ABC = { intA, intB, intC };
return ABC;
}
}
}

Is there a C# library that will perform the Excel NORMINV function?

I'm running some Monte Carlo simulations and making extensive use of the Excel function NORM.INV using Office Interrop. This functions takes three arguments (probability, average, standard deviation) and returns the inverse of the cumulative distribution.
I'd like to move my code into a web app, but that will require installing Excel on the server. Does anybody know of a C# statistics library that has an equivalent function to NORM.INV?
Meta.Numerics has exactly what you are looking for. Here is the code to do it using that library:
Distribution n = new NormalDistribution(mean, standardDeviation);
double x = n.InverseLeftProbability(probability);
If you are doing this in order to generate normal deviates, the GetRandomValue function is even faster.
I was also needing a C# implementation of NORMINV, the closest thing I found was a C++ implementation http://www.wilmott.com/messageview.cfm?catid=10&threadid=38771, so I made a quick and dirty translation to C#, the details here http://weblogs.asp.net/esanchez/archive/2010/07/29/a-quick-and-dirty-implementation-of-excel-norminv-function-in-c.aspx. I have made only a few basic tests so be careful if you decide to use it, anyway, hope it helps!
https://numerics.mathdotnet.com/ has a pretty neat looking library that deals with stats (so I assume the CDF), I've not used it so I can't say for definite that it's what you want, but it seems like it should be.
The inverse normal CDF, including coefficients, is described here. And the absolute value of the relative error is less than 1.15 × 10−9
public static class NormalDistributionConfidenceCalculator
{
/// <summary>
///
/// </summary>
public static double InverseNormalDistribution(double probability, double min, double max)
{
double x = 0;
double a = 0;
double b = 1;
double precision = Math.Pow(10, -3);
while ((b - a) > precision)
{
x = (a + b) / 2;
if (NormInv(x) > probability)
{
b = x;
}
else
{
a = x;
}
}
if ((max > 0) && (min > 0))
{
x = x * (max - min) + min;
}
return x;
}
/// <summary>
/// Returns the cumulative density function evaluated at A given value.
/// </summary>
/// <param name="x">A position on the x-axis.</param>
/// <param name="mean"></param>
/// <param name="sigma"></param>
/// <returns>The cumulative density function evaluated at <C>x</C>.</returns>
/// <remarks>The value of the cumulative density function at A point <C>x</C> is
/// probability that the value of A random variable having this normal density is
/// less than or equal to <C>x</C>.
/// </remarks>
public static double NormalDistribution(double x, double mean, double sigma)
{
// This algorithm is ported from dcdflib:
// Cody, W.D. (1993). "ALGORITHM 715: SPECFUN - A Portabel FORTRAN
// Package of Special Function Routines and Test Drivers"
// acm Transactions on Mathematical Software. 19, 22-32.
int i;
double del, xden, xnum, xsq;
double result, ccum;
double arg = (x - mean) / sigma;
const double sixten = 1.60e0;
const double sqrpi = 3.9894228040143267794e-1;
const double thrsh = 0.66291e0;
const double root32 = 5.656854248e0;
const double zero = 0.0e0;
const double min = Double.Epsilon;
double z = arg;
double y = Math.Abs(z);
const double half = 0.5e0;
const double one = 1.0e0;
double[] a =
{
2.2352520354606839287e00, 1.6102823106855587881e02, 1.0676894854603709582e03,
1.8154981253343561249e04, 6.5682337918207449113e-2
};
double[] b =
{
4.7202581904688241870e01, 9.7609855173777669322e02, 1.0260932208618978205e04,
4.5507789335026729956e04
};
double[] c =
{
3.9894151208813466764e-1, 8.8831497943883759412e00, 9.3506656132177855979e01,
5.9727027639480026226e02, 2.4945375852903726711e03, 6.8481904505362823326e03,
1.1602651437647350124e04, 9.8427148383839780218e03, 1.0765576773720192317e-8
};
double[] d =
{
2.2266688044328115691e01, 2.3538790178262499861e02, 1.5193775994075548050e03,
6.4855582982667607550e03, 1.8615571640885098091e04, 3.4900952721145977266e04,
3.8912003286093271411e04, 1.9685429676859990727e04
};
double[] p =
{
2.1589853405795699e-1, 1.274011611602473639e-1, 2.2235277870649807e-2,
1.421619193227893466e-3, 2.9112874951168792e-5, 2.307344176494017303e-2
};
double[] q =
{
1.28426009614491121e00, 4.68238212480865118e-1, 6.59881378689285515e-2,
3.78239633202758244e-3, 7.29751555083966205e-5
};
if (y <= thrsh)
{
//
// Evaluate anorm for |X| <= 0.66291
//
xsq = zero;
if (y > double.Epsilon) xsq = z * z;
xnum = a[4] * xsq;
xden = xsq;
for (i = 0; i < 3; i++)
{
xnum = (xnum + a[i]) * xsq;
xden = (xden + b[i]) * xsq;
}
result = z * (xnum + a[3]) / (xden + b[3]);
double temp = result;
result = half + temp;
}
//
// Evaluate anorm for 0.66291 <= |X| <= sqrt(32)
//
else if (y <= root32)
{
xnum = c[8] * y;
xden = y;
for (i = 0; i < 7; i++)
{
xnum = (xnum + c[i]) * y;
xden = (xden + d[i]) * y;
}
result = (xnum + c[7]) / (xden + d[7]);
xsq = Math.Floor(y * sixten) / sixten;
del = (y - xsq) * (y + xsq);
result = Math.Exp(-(xsq * xsq * half)) * Math.Exp(-(del * half)) * result;
ccum = one - result;
if (z > zero)
{
result = ccum;
}
}
//
// Evaluate anorm for |X| > sqrt(32)
//
else
{
xsq = one / (z * z);
xnum = p[5] * xsq;
xden = xsq;
for (i = 0; i < 4; i++)
{
xnum = (xnum + p[i]) * xsq;
xden = (xden + q[i]) * xsq;
}
result = xsq * (xnum + p[4]) / (xden + q[4]);
result = (sqrpi - result) / y;
xsq = Math.Floor(z * sixten) / sixten;
del = (z - xsq) * (z + xsq);
result = Math.Exp(-(xsq * xsq * half)) * Math.Exp(-(del * half)) * result;
ccum = one - result;
if (z > zero)
{
result = ccum;
}
}
if (result < min)
result = 0.0e0;
return result;
}
/// <summary>
/// Given a probability, a mean, and a standard deviation, an x value can be calculated.
/// </summary>
/// <returns></returns>
public static double NormInv(double probability)
{
const double a1 = -39.6968302866538;
const double a2 = 220.946098424521;
const double a3 = -275.928510446969;
const double a4 = 138.357751867269;
const double a5 = -30.6647980661472;
const double a6 = 2.50662827745924;
const double b1 = -54.4760987982241;
const double b2 = 161.585836858041;
const double b3 = -155.698979859887;
const double b4 = 66.8013118877197;
const double b5 = -13.2806815528857;
const double c1 = -7.78489400243029E-03;
const double c2 = -0.322396458041136;
const double c3 = -2.40075827716184;
const double c4 = -2.54973253934373;
const double c5 = 4.37466414146497;
const double c6 = 2.93816398269878;
const double d1 = 7.78469570904146E-03;
const double d2 = 0.32246712907004;
const double d3 = 2.445134137143;
const double d4 = 3.75440866190742;
//Define break-points
// using Epsilon is wrong; see link above for reference to 0.02425 value
//const double pLow = double.Epsilon;
const double pLow = 0.02425;
const double pHigh = 1 - pLow;
//Define work variables
double q;
double result = 0;
// if argument out of bounds.
// set it to a value within desired precision.
if (probability <= 0)
probability = pLow;
if (probability >= 1)
probability = pHigh;
if (probability < pLow)
{
//Rational approximation for lower region
q = Math.Sqrt(-2 * Math.Log(probability));
result = (((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1);
}
else if (probability <= pHigh)
{
//Rational approximation for lower region
q = probability - 0.5;
double r = q * q;
result = (((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q /
(((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + 1);
}
else if (probability < 1)
{
//Rational approximation for upper region
q = Math.Sqrt(-2 * Math.Log(1 - probability));
result = -(((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1);
}
return result;
}
/// <summary>
///
/// </summary>
/// <param name="probability"></param>
/// <param name="mean"></param>
/// <param name="sigma"></param>
/// <returns></returns>
public static double NormInv(double probability, double mean, double sigma)
{
double x = NormInv(probability);
return sigma * x + mean;
}
}
I do not know of a library, but found this link - http://home.online.no/~pjacklam/notes/invnorm/ - describing an algoritm. It has implementations in a number of languages, but not C#. You could use the VB.NET version, or port it yourself.
Maybe you can try this component,http://www.smartxls.com,it has an Excel compatible runtime calculation engine and it does not need Excel installed.
add a chart control
double result = Chart1.DataManipulator.Statistics.InverseNormalDistribution(probability);
probability eg : 0.9, 0.4

Categories

Resources