Example #1
0
        /// <summary>
        /// Serializes content from the writer
        /// </summary>
        public override void InternalFromBinary(IBinaryRawReader reader)
        {
            base.InternalFromBinary(reader);

            var version = VersionSerializationHelper.CheckVersionByte(reader, VERSION_NUMBER);

            if (version == 1)
            {
                CellSize = reader.ReadDouble();

                StartPoint = new WGS84Point();
                if (reader.ReadBoolean())
                {
                    StartPoint.FromBinary(reader);
                }

                EndPoint = new WGS84Point();
                if (reader.ReadBoolean())
                {
                    EndPoint.FromBinary(reader);
                }

                PositionsAreGrid = reader.ReadBoolean();
            }
        }
Example #2
0
        public static HashSet <GeoObj> SearchGeoHashIndex(SessionBase session, double lat, double lon, double radius)
        {
            HashSet <GeoObj>   resultSet = new HashSet <GeoObj>();
            WGS84Point         center    = new WGS84Point(lat, lon);
            GeoHashCircleQuery query     = new GeoHashCircleQuery(center, radius); // radius in meters
            BoundingBox        bbox      = query.BoundingBox;
            var btreeSet = session.AllObjects <BTreeSet <GeoObj> >().FirstOrDefault();

            foreach (GeoHash hash in query.SearchHashes)
            {
                var itr = btreeSet.Iterator();
                itr.GoTo(new GeoObj(hash.LongValue));
                var current = itr.Current();
                while (current != null)
                {
                    GeoHash geoHash = GeoHash.FromLongValue(current.GeoHashAsLong);
                    if ((geoHash.SignificantBits >= hash.SignificantBits && geoHash.Within(hash)) || (geoHash.SignificantBits < hash.SignificantBits && hash.Within(geoHash)))
                    {
                        if (!(current.Latitude < bbox.MinLat || current.Latitude > bbox.MaxLat || current.Longitude < bbox.MinLon || current.Longitude > bbox.MaxLon))
                        {
                            resultSet.Add(current);
                        }
                        current = itr.Next();
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return(resultSet);
        }
Example #3
0
        /// <summary>
        /// Serializes content from the writer
        /// </summary>
        public override void InternalFromBinary(IBinaryRawReader reader)
        {
            base.InternalFromBinary(reader);

            var version = VersionSerializationHelper.CheckVersionByte(reader, VERSION_NUMBER);

            if (version == 1)
            {
                ProfileTypeRequired = (GridDataType)reader.ReadInt();

                ProfileStyle = (ProfileStyle)reader.ReadInt();

                StartPoint = new WGS84Point();
                if (reader.ReadBoolean())
                {
                    StartPoint.FromBinary(reader);
                }

                EndPoint = new WGS84Point();
                if (reader.ReadBoolean())
                {
                    EndPoint.FromBinary(reader);
                }

                PositionsAreGrid = reader.ReadBoolean();

                ReturnAllPassesAndLayers = reader.ReadBoolean();

                VolumeType = (VolumeComputationType)reader.ReadInt();
            }
        }
 /// <summary>
 /// create a <seealso cref="GeoHashCircleQuery"/> with the given center point and a
 /// radius in meters.
 /// </summary>
 public GeoHashCircleQuery(WGS84Point center, double radius)
 {
   m_radius = radius;
   m_center = center;
   WGS84Point northEast = VincentyGeodesy.MoveInDirection(VincentyGeodesy.MoveInDirection(center, 0, radius), 90, radius);
   WGS84Point southWest = VincentyGeodesy.MoveInDirection(VincentyGeodesy.MoveInDirection(center, 180, radius), 270, radius);
   BoundingBox bbox = new BoundingBox(northEast, southWest);
   m_query = new GeoHashBoundingBoxQuery(bbox);
 }
Example #5
0
        /// <summary>
        /// create a <seealso cref="GeoHashCircleQuery"/> with the given center point and a
        /// radius in meters.
        /// </summary>
        public GeoHashCircleQuery(WGS84Point center, double radius)
        {
            m_radius = radius;
            m_center = center;
            WGS84Point  northEast = VincentyGeodesy.MoveInDirection(VincentyGeodesy.MoveInDirection(center, 0, radius), 90, radius);
            WGS84Point  southWest = VincentyGeodesy.MoveInDirection(VincentyGeodesy.MoveInDirection(center, 180, radius), 270, radius);
            BoundingBox bbox      = new BoundingBox(northEast, southWest);

            m_query = new GeoHashBoundingBoxQuery(bbox);
        }
Example #6
0
        public void WGS84Point_with_ZERO_height_should_return_valid_result(double lat, double lon, double height, double toY, double toX, double toZ, string csib)
        {
            var points = new WGS84Point(lon, lat, height);

            var xyzCoords = _convertCoordinates.WGS84ToCalibration(csib, points, InputAs.Radians);

            xyzCoords.Should().NotBeNull();

            xyzCoords.Y.Should().BeApproximately(toY, GRID_CM_TOLERANCE);
            xyzCoords.X.Should().BeApproximately(toX, GRID_CM_TOLERANCE);
            xyzCoords.Z.Should().BeApproximately(toZ, GRID_CM_TOLERANCE);
        }
Example #7
0
        public void WGS84Point_with_NULL_height_should_return_invalid_result()
        {
            var points = new WGS84Point(lon: 0.14729266728569143, lat: 0.8596927023775642, height: TestConsts.NULL_DOUBLE);

            var xyzCoords = _convertCoordinates.WGS84ToCalibration(CSIB.PHILIPSBURG, points, InputAs.Radians);

            xyzCoords.Should().NotBeNull();

            xyzCoords.Y.Should().Be(0);
            xyzCoords.X.Should().Be(0);
            xyzCoords.Z.Should().Be(0);
        }
        public GeoHashBoundingBoxQuery(BoundingBox bbox)
        {
            int        fittingBits = GeoHashSizeTable.NumberOfBitsForOverlappingGeoHash(bbox);
            WGS84Point center      = bbox.CenterPoint;
            GeoHash    centerHash  = GeoHash.WithBitPrecision(center.Latitude, center.Longitude, fittingBits);

            if (HashFits(centerHash, bbox))
            {
                AddSearchHash(centerHash);
            }
            else
            {
                ExpandSearch(centerHash, bbox);
            }
        }
Example #9
0
        public static double DistanceInMeters(WGS84Point foo, WGS84Point bar)
        {
            double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563; // WGS-84
            // ellipsiod
            double L = (bar.Longitude - foo.Longitude) * DegToRad;
            double U1 = Math.Atan((1 - f) * Math.Tan(foo.Latitude * DegToRad));
            double U2 = Math.Atan((1 - f) * Math.Tan(bar.Latitude * DegToRad));
            double sinU1 = Math.Sin(U1), cosU1 = Math.Cos(U1);
            double sinU2 = Math.Sin(U2), cosU2 = Math.Cos(U2);

            double cosSqAlpha, sinSigma, cos2SigmaM, cosSigma, sigma;

            double lambda = L, lambdaP, iterLimit = 20;

            do
            {
                double sinLambda = Math.Sin(lambda), cosLambda = Math.Cos(lambda);
                sinSigma = Math.Sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
                if (sinSigma == 0)
                {
                    return(0); // co-incident points
                }
                cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
                sigma    = Math.Atan2(sinSigma, cosSigma);
                double sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
                cosSqAlpha = 1 - sinAlpha * sinAlpha;
                cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
                if (double.IsNaN(cos2SigmaM))
                {
                    cos2SigmaM = 0; // equatorial line: cosSqAlpha=0
                }
                double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
                lambdaP = lambda;
                lambda  = L + (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
            } while (Math.Abs(lambda - lambdaP) > EPSILON && --iterLimit > 0);

            if (iterLimit == 0)
            {
                return(double.NaN);
            }
            double uSquared   = cosSqAlpha * (a * a - b * b) / (b * b);
            double A          = 1 + uSquared / 16384 * (4096 + uSquared * (-768 + uSquared * (320 - 175 * uSquared)));
            double B          = uSquared / 1024 * (256 + uSquared * (-128 + uSquared * (74 - 47 * uSquared)));
            double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
            double s          = b * A * (sigma - deltaSigma);

            return(s);
        }
Example #10
0
        /// <summary>
        /// returns the <seealso cref="WGS84Point"/> that is in the given direction at the
        /// following distance of the given point.<br>
        /// Uses Vincenty's formula and the WGS84 ellipsoid.
        /// </summary>
        /// <param name="bearingInDegrees">
        ///            : must be within 0 and 360 </param>
        /// <param name="point"> : where to start </param>
        /// <param name="distanceInMeters">: How far to move in the given direction </param>
        public static WGS84Point MoveInDirection(WGS84Point point, double bearingInDegrees, double distanceInMeters)
        {
            if (bearingInDegrees < 0 || bearingInDegrees > 360)
            {
                throw new System.ArgumentException("direction must be in (0,360)");
            }

            double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563; // WGS-84
            // ellipsiod
            double alpha1 = bearingInDegrees * DegToRad;
            double sinAlpha1 = Math.Sin(alpha1), cosAlpha1 = Math.Cos(alpha1);

            double tanU1 = (1 - f) * Math.Tan(point.Latitude * DegToRad);
            double cosU1 = 1 / Math.Sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
            double sigma1     = Math.Atan2(tanU1, cosAlpha1);
            double sinAlpha   = cosU1 * sinAlpha1;
            double cosSqAlpha = 1 - sinAlpha * sinAlpha;
            double uSq        = cosSqAlpha * (a * a - b * b) / (b * b);
            double A          = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
            double B          = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

            double sinSigma = 0, cosSigma = 0, cos2SigmaM = 0;
            double sigma = distanceInMeters / (b * A), sigmaP = 2 * Math.PI;

            while (Math.Abs(sigma - sigmaP) > 1e-12)
            {
                cos2SigmaM = Math.Cos(2 * sigma1 + sigma);
                sinSigma   = Math.Sin(sigma);
                cosSigma   = Math.Cos(sigma);
                double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
                sigmaP = sigma;
                sigma  = distanceInMeters / (b * A) + deltaSigma;
            }

            double tmp    = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
            double lat2   = Math.Atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - f) * Math.Sqrt(sinAlpha * sinAlpha + tmp * tmp));
            double lambda = Math.Atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
            double C      = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
            double L      = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));

            double newLat = lat2 / DegToRad;
            double newLon = point.Longitude + L / DegToRad;

            newLon = (newLon > 180.0 ? 360.0 - newLon : newLon);
            newLon = (newLon < -180.0 ? 360.0 + newLon : newLon);

            return(new WGS84Point(newLat, newLon));
        }
Example #11
0
 /// <summary>
 /// Constructor taking the full state of the elevation patch computation operation
 /// </summary>
 public CalculateDesignProfileArgument(Guid projectUid,
                                       double cellSize,
                                       Guid designUid,
                                       double offset,
                                       WGS84Point startPoint,
                                       WGS84Point endPoint,
                                       bool positionsAreGrid = false) : this()
 {
     ProjectID = projectUid;
     CellSize  = cellSize;
     ReferenceDesign.DesignID = designUid;
     ReferenceDesign.Offset   = offset;
     StartPoint       = startPoint;
     EndPoint         = endPoint;
     PositionsAreGrid = positionsAreGrid;
 }
Example #12
0
    public static double DistanceInMeters(WGS84Point foo, WGS84Point bar)
    {
      double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563; // WGS-84
      // ellipsiod
      double L = (bar.Longitude - foo.Longitude) * DegToRad;
      double U1 = Math.Atan((1 - f) * Math.Tan(foo.Latitude * DegToRad));
      double U2 = Math.Atan((1 - f) * Math.Tan(bar.Latitude * DegToRad));
      double sinU1 = Math.Sin(U1), cosU1 = Math.Cos(U1);
      double sinU2 = Math.Sin(U2), cosU2 = Math.Cos(U2);

      double cosSqAlpha, sinSigma, cos2SigmaM, cosSigma, sigma;

      double lambda = L, lambdaP , iterLimit = 20;
      do
      {
        double sinLambda = Math.Sin(lambda), cosLambda = Math.Cos(lambda);
        sinSigma = Math.Sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
        if (sinSigma == 0)
        {
          return 0; // co-incident points
        }
        cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
        sigma = Math.Atan2(sinSigma, cosSigma);
        double sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
        cosSqAlpha = 1 - sinAlpha * sinAlpha;
        cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
        if (double.IsNaN(cos2SigmaM))
        {
          cos2SigmaM = 0; // equatorial line: cosSqAlpha=0
        }
        double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
        lambdaP = lambda;
        lambda = L + (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
      } while (Math.Abs(lambda - lambdaP) > EPSILON && --iterLimit > 0);

      if (iterLimit == 0)
      {
        return double.NaN;
      }
      double uSquared = cosSqAlpha * (a * a - b * b) / (b * b);
      double A = 1 + uSquared / 16384 * (4096 + uSquared * (-768 + uSquared * (320 - 175 * uSquared)));
      double B = uSquared / 1024 * (256 + uSquared * (-128 + uSquared * (74 - 47 * uSquared)));
      double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
      double s = b * A * (sigma - deltaSigma);

      return s;
    }
Example #13
0
        public static HashSet <GeoObj> SearchGeoHashIndex(SessionBase session, double minLat, double minLon, double maxLat, double maxLon)
        {
            HashSet <GeoObj> resultSet = new HashSet <GeoObj>();

            if (minLat > maxLat)
            {
                double t = minLat;
                minLat = maxLat;
                maxLat = t;
            }
            if (minLon > maxLon)
            {
                double t = minLon;
                minLon = maxLon;
                maxLon = t;
            }
            WGS84Point              min   = new WGS84Point(minLat, minLon);
            WGS84Point              max   = new WGS84Point(maxLat, maxLon);
            BoundingBox             bbox  = new BoundingBox(min, max);
            GeoHashBoundingBoxQuery query = new GeoHashBoundingBoxQuery(bbox);
            var btreeSet = session.AllObjects <BTreeSet <GeoObj> >().FirstOrDefault();

            foreach (GeoHash hash in query.SearchHashes)
            {
                var itr = btreeSet.Iterator();
                itr.GoTo(new GeoObj(hash.LongValue));
                var current = itr.Current();
                while (current != null)
                {
                    GeoHash geoHash = GeoHash.FromLongValue(current.GeoHashAsLong);
                    if ((geoHash.SignificantBits >= hash.SignificantBits && geoHash.Within(hash)) || (geoHash.SignificantBits < hash.SignificantBits && hash.Within(geoHash)))
                    {
                        if (!(current.Latitude < bbox.MinLat || current.Latitude > bbox.MaxLat || current.Longitude < bbox.MinLon || current.Longitude > bbox.MaxLon))
                        {
                            resultSet.Add(current);
                        }
                        current = itr.Next();
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return(resultSet);
        }
Example #14
0
        /// <inheritdoc/>
        public XYZ WGS84ToCalibration(string csib, WGS84Point wgs84Point, InputAs inputAs)
        {
            var requestId = Guid.NewGuid();

            if (_log.IsTraceEnabled())
            {
                _log.LogTrace($"{nameof(WGS84ToCalibration)}: CoreXRequestID: {requestId}, wgs84Point: {wgs84Point}, InputAs: {inputAs}, CSIB: {csib}");
            }

            if (inputAs == InputAs.Degrees)
            {
                wgs84Point.Lat = wgs84Point.Lat.DegreesToRadians();
                wgs84Point.Lon = wgs84Point.Lon.DegreesToRadians();
            }

            var nee = _coreX
                      .TransformLLHToNEE(csib, new LLH
            {
                Latitude  = wgs84Point.Lat,
                Longitude = wgs84Point.Lon,
                Height    = wgs84Point.Height
            },
                                         fromType: CoordinateTypes.ReferenceGlobalLLH, toType: CoordinateTypes.OrientatedNEE);

            var result = new XYZ
            {
                X = nee.East,
                Y = nee.North,
                Z = nee.Elevation
            };

            if (_log.IsTraceEnabled())
            {
                _log.LogTrace($"{nameof(WGS84ToCalibration)}: CoreXRequestID: {requestId}, Returning XYZ: {result}");
            }

            return(result);
        }
Example #15
0
 /// <inheritdoc />
 public XYZ NullWGSLLToXY(WGS84Point wgsPoint) => new XYZ(wgsPoint.Lon, wgsPoint.Lat);
 public bool Contains(WGS84Point point)
 {
     return(Contains(GeoHash.WithBitPrecision(point.Latitude, point.Longitude, 64)));
 }
Example #17
0
 public bool Contains(WGS84Point point)
 {
     return(m_query.Contains(point));
 }
Example #18
0
        /// <summary>
        /// Computes a geometric profile across the design given a series of vertices describing the path to be profiled.
        /// </summary>
        public async Task <(List <XYZS> profile, DesignProfilerRequestResult errorCode)> ComputeProfile(Guid projectUid, WGS84Point startPoint, WGS84Point endPoint, double cellSize, double offset, bool arePositionsGrid)
        {
            // Query the DesignProfiler service to get the patch of elevations calculated
            try
            {
                var profileRequest = new DesignProfileRequest();
                var arg            = new CalculateDesignProfileArgument
                {
                    ProjectID        = projectUid,
                    CellSize         = cellSize,
                    StartPoint       = startPoint,
                    EndPoint         = endPoint,
                    PositionsAreGrid = arePositionsGrid,
                    ReferenceDesign  =
                    {
                        DesignID = DesignDescriptor.DesignID,
                        Offset   = offset
                    }
                };

                var profileResult = await profileRequest.ExecuteAsync(arg);

                return(profileResult.Profile, profileResult.RequestResult);
            }
            catch
            {
                return(null, DesignProfilerRequestResult.UnknownError);
            }
        }
 public bool Contains(WGS84Point point)
 {
   return Contains(GeoHash.WithBitPrecision(point.Latitude, point.Longitude, 64));
 }
 public bool Contains(WGS84Point point)
 {
   return m_query.Contains(point);
 }
Example #21
0
        public static HashSet <Person> SearchGeoHashIndex(string bootDirectory, double minLat, double minLon, double maxLat, double maxLon)
        {
            HashSet <Person> resultSet = new HashSet <Person>();

            if (minLat > maxLat)
            {
                double t = minLat;
                minLat = maxLat;
                maxLat = t;
            }
            if (minLon > maxLon)
            {
                double t = minLon;
                minLon = maxLon;
                maxLon = t;
            }
            WGS84Point              min   = new WGS84Point(minLat, minLon);
            WGS84Point              max   = new WGS84Point(maxLat, maxLon);
            BoundingBox             bbox  = new BoundingBox(min, max);
            GeoHashBoundingBoxQuery query = new GeoHashBoundingBoxQuery(bbox);

            using (SessionNoServer session = new SessionNoServer(bootDirectory))
            {
                session.BeginRead();
                BTreeMap <Int64, VelocityDbList <Person> > btreeMap = session.AllObjects <BTreeMap <Int64, VelocityDbList <Person> > >().FirstOrDefault();
                foreach (GeoHash hash in query.SearchHashes)
                {
                    BTreeMapIterator <Int64, VelocityDbList <Person> > itr = btreeMap.Iterator();
                    itr.GoTo(hash.LongValue);
                    var current = itr.Current();
                    while (current.Value != null)
                    {
                        GeoHash geoHash = GeoHash.FromLongValue(current.Key);
                        if (geoHash.Within(hash) || (geoHash.SignificantBits > hash.SignificantBits && hash.Within(geoHash)))
                        {
                            foreach (Person person in current.Value)
                            {
                                resultSet.Add(person);
                            }
                            current = itr.Next();
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                // actual geohash bounding box may be including some that are not within requested bounding box so remove such items if any
                HashSet <Person> notWithin = new HashSet <Person>();
                foreach (Person person in resultSet)
                {
                    if (person.Lattitude < min.Latitude || person.Lattitude > max.Latitude || person.Longitude < min.Longitude || person.Lattitude > max.Latitude)
                    {
                        notWithin.Add(person);
                    }
                }
                foreach (Person person in notWithin)
                {
                    resultSet.Remove(person);
                }
                foreach (Person person in resultSet)
                {
                    Console.WriteLine(person.ToString() + " Lattitude: " + person.Lattitude + " Longitude: " + person.Longitude);
                }
                session.Commit();
            }

            return(resultSet);
        }
Example #22
-1
    /// <summary>
    /// returns the <seealso cref="WGS84Point"/> that is in the given direction at the
    /// following distance of the given point.<br>
    /// Uses Vincenty's formula and the WGS84 ellipsoid.
    /// </summary>
    /// <param name="bearingInDegrees">
    ///            : must be within 0 and 360 </param>
    /// <param name="point"> : where to start </param>
    /// <param name="distanceInMeters">: How far to move in the given direction </param>
    public static WGS84Point MoveInDirection(WGS84Point point, double bearingInDegrees, double distanceInMeters)
    {

      if (bearingInDegrees < 0 || bearingInDegrees > 360)
      {
        throw new System.ArgumentException("direction must be in (0,360)");
      }

      double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563; // WGS-84
      // ellipsiod
      double alpha1 = bearingInDegrees * DegToRad;
      double sinAlpha1 = Math.Sin(alpha1), cosAlpha1 = Math.Cos(alpha1);

      double tanU1 = (1 - f) * Math.Tan(point.Latitude * DegToRad);
      double cosU1 = 1 / Math.Sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
      double sigma1 = Math.Atan2(tanU1, cosAlpha1);
      double sinAlpha = cosU1 * sinAlpha1;
      double cosSqAlpha = 1 - sinAlpha * sinAlpha;
      double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
      double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
      double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

      double sinSigma = 0, cosSigma = 0, cos2SigmaM = 0;
      double sigma = distanceInMeters / (b * A), sigmaP = 2 * Math.PI;
      while (Math.Abs(sigma - sigmaP) > 1e-12)
      {
        cos2SigmaM = Math.Cos(2 * sigma1 + sigma);
        sinSigma = Math.Sin(sigma);
        cosSigma = Math.Cos(sigma);
        double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
        sigmaP = sigma;
        sigma = distanceInMeters / (b * A) + deltaSigma;
      }

      double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
      double lat2 = Math.Atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - f) * Math.Sqrt(sinAlpha * sinAlpha + tmp * tmp));
      double lambda = Math.Atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
      double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
      double L = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));

      double newLat = lat2 / DegToRad;
      double newLon = point.Longitude + L / DegToRad;

      newLon = (newLon > 180.0 ? 360.0 - newLon : newLon);
      newLon = (newLon < -180.0 ? 360.0 + newLon : newLon);

      return new WGS84Point(newLat, newLon);
    }