/// <summary>
 /// Used to convert latlon from degrees to radians
 /// </summary>
 /// <param name="latLon">Latitude and Longidute object in degrees</param>
 /// <returns>Latitude and Longidute object in radians</returns>
 private LatLon ToRadians(LatLon latLon)
 {
     return new LatLon
     {
         Latitude = latLon.Latitude * Math.PI / 180,
         Longitude = latLon.Longitude * Math.PI / 180,
     };
 }
 /// <summary>
 /// Used to convert latlon from radians to degrees
 /// <param name="latLon">Latitude and Longidute object in radians</param>
 /// <returns>Latitude and Longidute object in degrees</returns>
 /// </summary>
 private LatLon ToDegrees(LatLon latLon)
 {
     return new LatLon
     {
         Latitude = latLon.Latitude * 180 / Math.PI,
         Longitude = latLon.Longitude * 180 / Math.PI,
     };
 }
        /// <summary>
        /// Abridged Molodensky transformation between 2 datums
        /// </summary>
        /// <param name="input"></param>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        private LatLon Molodensky(LatLon input, string from, string to)
        {
            // from->WGS84 - to->WGS84 = from->WGS84 + WGS84->to = from->to
            double dX = Datums[from].DeltaX - Datums[to].DeltaX;
            double dY = Datums[from].DeltaY - Datums[to].DeltaY;
            double dZ = Datums[from].DeltaZ - Datums[to].DeltaZ;

            double slat = Math.Sin(input.Latitude);
            double clat = Math.Cos(input.Latitude);
            double slon = Math.Sin(input.Longitude);
            double clon = Math.Cos(input.Longitude);
            double ssqlat = slat * slat;

            //dlat = ((-dx * slat * clon - dy * slat * slon + dz * clat)
            //        + (da * rn * from_esq * slat * clat / from_a)
            //        + (df * (rm * adb + rn / adb )* slat * clat))
            //       / (rm + from.h);

            double from_f = Datums[from].Flatenning;
            double df = Datums[to].Flatenning - from_f;
            double from_a = Datums[from].EquatorialEarthRadius;
            double da = Datums[to].EquatorialEarthRadius - from_a;
            double from_esq = Datums[from].EccentricitySquared;
            double adb = 1.0 / (1.0 - from_f);
            double rn = from_a / Math.Sqrt(1 - from_esq * ssqlat);
            double rm = from_a * (1 - from_esq) / Math.Pow((1 - from_esq * ssqlat), 1.5);
            double from_h = 0.0; // we're flat!

            double dlat = (-dX * slat * clon - dY * slat * slon + dZ * clat
                           + da * rn * from_esq * slat * clat / from_a +
                           +df * (rm * adb + rn / adb) * slat * clat) / (rm + from_h);

            // dlon = (-dx * slon + dy * clon) / ((rn + from.h) * clat);
            double dlon = (-dX * slon + dY * clon) / ((rn + from_h) * clat);

            // result lat (radians)
            return new LatLon
            {
                Latitude = input.Latitude + dlat,
                Longitude = input.Longitude + dlon
            };
        }
        /// <summary>
        /// Lat/Lon to Local Grid conversion
        /// </summary>
        /// <param name="latLon"></param>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        private NorthEast LatLon2Grid(LatLon latLon, string from, string to)
        {
            // Datum data for Lat/Lon to TM conversion
            double a = Datums[from].EquatorialEarthRadius;
            double e = Datums[from].Eccentricity; 	// sqrt(esq);
            double b = Datums[from].PolarEarthRadius;
            double lat = latLon.Latitude;
            double lon = latLon.Longitude;
            double slat1 = Math.Sin(lat);
            double clat1 = Math.Cos(lat);
            double clat1sq = clat1 * clat1;
            double tanlat1sq = slat1 * slat1 / clat1sq;
            double e2 = e * e;
            double e4 = e2 * e2;
            double e6 = e4 * e2;
            double eg = (e * a / b);
            double eg2 = eg * eg;

            double l1 = 1 - e2 / 4 - 3 * e4 / 64 - 5 * e6 / 256;
            double l2 = 3 * e2 / 8 + 3 * e4 / 32 + 45 * e6 / 1024;
            double l3 = 15 * e4 / 256 + 45 * e6 / 1024;
            double l4 = 35 * e6 / 3072;
            double M = a * (l1 * lat - l2 * Math.Sin(2 * lat) + l3 * Math.Sin(4 * lat) - l4 * Math.Sin(6 * lat));
            //double rho = a*(1-e2) / pow((1-(e*slat1)*(e*slat1)),1.5);
            double nu = a / Math.Sqrt(1 - (e * slat1) * (e * slat1));
            double p = lon - Grids[to].CentralLongitude;
            double k0 = Grids[to].ScaleFactor;
            // y = northing = K1 + K2p2 + K3p4, where
            double K1 = M * k0;
            double K2 = k0 * nu * slat1 * clat1 / 2;
            double K3 = (k0 * nu * slat1 * clat1 * clat1sq / 24) * (5 - tanlat1sq + 9 * eg2 * clat1sq + 4 * eg2 * eg2 * clat1sq * clat1sq);
            // ING north
            double Y = K1 + K2 * p * p + K3 * p * p * p * p - Grids[to].FalseNorthing;

            // x = easting = K4p + K5p3, where
            double K4 = k0 * nu * clat1;
            double K5 = (k0 * nu * clat1 * clat1sq / 6) * (1 - tanlat1sq + eg2 * clat1 * clat1);
            // ING east
            double X = K4 * p + K5 * p * p * p + Grids[to].FalseEasting;

            // final rounded results
            return new NorthEast
            {
                North = (int)(Y + 0.5),
                East = (int)(X + 0.5),
            };
        }
        /// <summary>
        /// WGS84 to Israel New Grid (ITM) conversion
        /// </summary>
        /// <param name="latLon">Latitide and Longitude in WGS84</param>
        /// <returns>North East Coordinates in ITM grid</returns>
        public NorthEast Wgs84ToItm(LatLon latLon)
        {
            // 1. Molodensky WGS84 -> GRS80
            var latLon80 = Molodensky(ToRadians(latLon), WGS84, GRS80);

            // 2. Lat/Lon (GRS80) -> Local Grid (ITM)
            return LatLon2Grid(latLon80, GRS80, ITM);
        }
        /// <summary>
        /// WGS84 to Israel Old Grid (ICS) conversion
        /// </summary>
        /// <param name="latLon">Latitide and Longitude in WGS84</param>
        /// <returns>North East Coordinates in ICS grid</returns>
        public NorthEast Wgs84ToIcs(LatLon latLon)
        {
            // 1. Molodensky WGS84 -> Clark_1880_modified
            var latLon80 = Molodensky(ToRadians(latLon), WGS84, CLARK80M);

            // 2. Lat/Lon (Clark_1880_modified) -> Local Grid (ICS)
            return LatLon2Grid(latLon80, CLARK80M, ICS);
        }